aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergiusz Bazanski <q3k@q3k.org>2018-07-26 17:37:24 +0100
committerSergiusz Bazanski <q3k@q3k.org>2018-07-26 17:37:24 +0100
commit16acc6ea43f4c45dfb0e9550d731a2fba3f32618 (patch)
treebe13bd066321ff3c822e183f204b6817bc54f48c
parentba5395d89fea3a0a646d9bf3df643dac8c8bdec4 (diff)
downloadnextpnr-16acc6ea43f4c45dfb0e9550d731a2fba3f32618.tar.gz
nextpnr-16acc6ea43f4c45dfb0e9550d731a2fba3f32618.tar.bz2
nextpnr-16acc6ea43f4c45dfb0e9550d731a2fba3f32618.zip
gui: move polyline/lineshader to gui/lineshader.{h,cc}
-rw-r--r--gui/fpgaviewwidget.cc211
-rw-r--r--gui/fpgaviewwidget.h175
2 files changed, 1 insertions, 385 deletions
diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc
index d87c26a3..de73e27b 100644
--- a/gui/fpgaviewwidget.cc
+++ b/gui/fpgaviewwidget.cc
@@ -31,217 +31,6 @@
NEXTPNR_NAMESPACE_BEGIN
-void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
- const QVector2D *next) const
-{
- // buildPoint emits two vertices per line point, along with normals to move
- // them the right directio when rendering and miter to compensate for
- // bends.
-
- if (cur == nullptr) {
- // BUG
- return;
- }
-
- if (prev == nullptr && next == nullptr) {
- // BUG
- return;
- }
-
- // TODO(q3k): fast path for vertical/horizontal lines?
-
- // TODO(q3k): consider moving some of the linear algebra to the GPU,
- // they're better at this than poor old CPUs.
-
- // Build two unit vectors pointing in the direction of the two segments
- // defined by (prev, cur) and (cur, next)
- QVector2D dprev, dnext;
- if (prev == nullptr) {
- dnext = *next - *cur;
- dprev = dnext;
- } else if (next == nullptr) {
- dprev = *cur - *prev;
- dnext = dprev;
- } else {
- dprev = *cur - *prev;
- dnext = *next - *cur;
- }
- dprev.normalize();
- dnext.normalize();
-
- // Calculate tangent unit vector.
- QVector2D tangent(dprev + dnext);
- tangent.normalize();
-
- // Calculate normal to tangent - this is the line on which the vectors need
- // to be pushed to build a thickened line.
- const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());
-
- // Calculate normal to one of the lines.
- const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
- // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
- // (the ^-1 is performed in the shader)
- const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);
-
- const float x = cur->x();
- const float y = cur->y();
- const float mx = tangent_normal.x();
- const float my = tangent_normal.y();
-
- // Push back 'left' vertex.
- building->vertices.push_back(Vertex2DPOD(x, y));
- building->normals.push_back(Vertex2DPOD(mx, my));
- building->miters.push_back(miter);
-
- // Push back 'right' vertex.
- building->vertices.push_back(Vertex2DPOD(x, y));
- building->normals.push_back(Vertex2DPOD(mx, my));
- building->miters.push_back(-miter);
-}
-
-void PolyLine::build(LineShaderData &target) const
-{
- if (points_.size() < 2) {
- return;
- }
- const QVector2D *first = &points_.front();
- const QVector2D *last = &points_.back();
-
- // Index number of vertices, used to build the index buffer.
- unsigned int startIndex = target.vertices.size();
- unsigned int index = startIndex;
-
- // For every point on the line, call buildPoint with (prev, point, next).
- // If we're building a closed line, prev/next wrap around. Otherwise
- // they are passed as nullptr and buildPoint interprets that accordinglu.
- const QVector2D *prev = nullptr;
-
- // Loop iterator used to ensure next is valid.
- unsigned int i = 0;
- for (const QVector2D &point : points_) {
- const QVector2D *next = nullptr;
- if (++i < points_.size()) {
- next = (&point + 1);
- }
-
- // If the line is closed, wrap around. Otherwise, pass nullptr.
- if (prev == nullptr && closed_) {
- buildPoint(&target, last, &point, next);
- } else if (next == nullptr && closed_) {
- buildPoint(&target, prev, &point, first);
- } else {
- buildPoint(&target, prev, &point, next);
- }
-
- // If we have a prev point relative to cur, build a pair of triangles
- // to render vertices into lines.
- if (prev != nullptr) {
- target.indices.push_back(index);
- target.indices.push_back(index + 1);
- target.indices.push_back(index + 2);
-
- target.indices.push_back(index + 2);
- target.indices.push_back(index + 1);
- target.indices.push_back(index + 3);
-
- index += 2;
- }
- prev = &point;
- }
-
- // If we're closed, build two more vertices that loop the line around.
- if (closed_) {
- target.indices.push_back(index);
- target.indices.push_back(index + 1);
- target.indices.push_back(startIndex);
-
- target.indices.push_back(startIndex);
- target.indices.push_back(index + 1);
- target.indices.push_back(startIndex + 1);
- }
-}
-
-bool LineShader::compile(void)
-{
- program_ = new QOpenGLShaderProgram(parent_);
- program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource_);
- program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource_);
- if (!program_->link()) {
- printf("could not link program: %s\n", program_->log().toStdString().c_str());
- return false;
- }
-
- if (!vao_.create())
- log_abort();
- vao_.bind();
-
- if (!buffers_.position.create())
- log_abort();
- if (!buffers_.normal.create())
- log_abort();
- if (!buffers_.miter.create())
- log_abort();
- if (!buffers_.index.create())
- log_abort();
-
- attributes_.position = program_->attributeLocation("position");
- attributes_.normal = program_->attributeLocation("normal");
- attributes_.miter = program_->attributeLocation("miter");
- uniforms_.thickness = program_->uniformLocation("thickness");
- uniforms_.projection = program_->uniformLocation("projection");
- uniforms_.color = program_->uniformLocation("color");
-
- vao_.release();
- return true;
-}
-
-void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection)
-{
- auto gl = QOpenGLContext::currentContext()->functions();
- if (line.vertices.size() == 0)
- return;
- vao_.bind();
- program_->bind();
-
- buffers_.position.bind();
- buffers_.position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());
-
- buffers_.normal.bind();
- buffers_.normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());
-
- buffers_.miter.bind();
- buffers_.miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());
-
- buffers_.index.bind();
- buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
-
- program_->setUniformValue(uniforms_.projection, projection);
- program_->setUniformValue(uniforms_.thickness, thickness);
- program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());
-
- buffers_.position.bind();
- program_->enableAttributeArray("position");
- gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.normal.bind();
- program_->enableAttributeArray("normal");
- gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.miter.bind();
- program_->enableAttributeArray("miter");
- gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.index.bind();
- gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0);
-
- program_->disableAttributeArray("miter");
- program_->disableAttributeArray("normal");
- program_->disableAttributeArray("position");
-
- program_->release();
- vao_.release();
-}
-
FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this),
lineShader_(this), zoom_(500.0f),
diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h
index 6505c555..260ebf05 100644
--- a/gui/fpgaviewwidget.h
+++ b/gui/fpgaviewwidget.h
@@ -33,183 +33,10 @@
#include <QWaitCondition>
#include "nextpnr.h"
+#include "lineshader.h"
NEXTPNR_NAMESPACE_BEGIN
-// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL
-// directly.
-NPNR_PACKED_STRUCT(struct Vertex2DPOD {
- GLfloat x;
- GLfloat y;
-
- Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {}
-});
-
-// LineShaderData is a built set of vertices that can be rendered by the
-// LineShader.
-// Each LineShaderData can have its' own color and thickness.
-struct LineShaderData
-{
- std::vector<Vertex2DPOD> vertices;
- std::vector<Vertex2DPOD> normals;
- std::vector<GLfloat> miters;
- std::vector<GLuint> indices;
-
- LineShaderData(void) {}
-
- void clear(void)
- {
- vertices.clear();
- normals.clear();
- miters.clear();
- indices.clear();
- }
-};
-
-// PolyLine is a set of segments defined by points, that can be built to a
-// ShaderLine for GPU rendering.
-class PolyLine
-{
- private:
- std::vector<QVector2D> points_;
- bool closed_;
-
- void buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur, const QVector2D *next) const;
-
- public:
- // Create an empty PolyLine.
- PolyLine(bool closed = false) : closed_(closed) {}
-
- // Create a non-closed polyline consisting of one segment.
- PolyLine(float x0, float y0, float x1, float y1) : closed_(false)
- {
- point(x0, y0);
- point(x1, y1);
- }
-
- // Add a point to the PolyLine.
- void point(float x, float y) { points_.push_back(QVector2D(x, y)); }
-
- // Built PolyLine to shader data.
- void build(LineShaderData &target) const;
-
- // Set whether line is closed (ie. a loop).
- void setClosed(bool closed) { closed_ = closed; }
-};
-
-// LineShader is an OpenGL shader program that renders LineShaderData on the
-// GPU.
-// The LineShader expects two vertices per line point. It will push those
-// vertices along the given normal * miter. This is used to 'stretch' the line
-// to be as wide as the given thickness. The normal and miter are calculated
-// by the PolyLine build method in order to construct a constant thickness line
-// with miter edge joints.
-//
-// +------+------+
-//
-// |
-// PolyLine.build()
-// |
-// V
-//
-// ^ ^ ^
-// | | | <--- normal vectors (x2, pointing in the same
-// +/+----+/+----+/+ direction)
-//
-// |
-// vertex shader
-// |
-// V
-//
-// +------+------+ ^ by normal * miter * thickness/2
-// | | |
-// +------+------+ V by normal * miter * thickness/2
-//
-// (miter is flipped for every second vertex generated)
-class LineShader
-{
- private:
- QObject *parent_;
- QOpenGLShaderProgram *program_;
-
- // GL attribute locations.
- struct
- {
- // original position of line vertex
- GLuint position;
- // normal by which vertex should be translated
- GLuint normal;
- // scalar defining:
- // - how stretched the normal vector should be to
- // compensate for bends
- // - which way the normal should be applied (+1 for one vertex, -1
- // for the other)
- GLuint miter;
- } attributes_;
-
- // GL buffers
- struct
- {
- QOpenGLBuffer position;
- QOpenGLBuffer normal;
- QOpenGLBuffer miter;
- QOpenGLBuffer index;
- } buffers_;
-
- // GL uniform locations.
- struct
- {
- // combines m/v/p matrix to apply
- GLuint projection;
- // desired thickness of line
- GLuint thickness;
- // color of line
- GLuint color;
- } uniforms_;
-
- QOpenGLVertexArrayObject vao_;
-
- public:
- LineShader(QObject *parent) : parent_(parent), program_(nullptr)
- {
- buffers_.position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
- buffers_.position.setUsagePattern(QOpenGLBuffer::StaticDraw);
-
- buffers_.normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
- buffers_.normal.setUsagePattern(QOpenGLBuffer::StaticDraw);
-
- buffers_.miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
- buffers_.miter.setUsagePattern(QOpenGLBuffer::StaticDraw);
-
- buffers_.index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
- buffers_.index.setUsagePattern(QOpenGLBuffer::StaticDraw);
- }
-
- static constexpr const char *vertexShaderSource_ =
- "#version 110\n"
- "attribute highp vec2 position;\n"
- "attribute highp vec2 normal;\n"
- "attribute highp float miter;\n"
- "uniform highp float thickness;\n"
- "uniform highp mat4 projection;\n"
- "void main() {\n"
- " vec2 p = position.xy + vec2(normal * thickness/2.0 / miter);\n"
- " gl_Position = projection * vec4(p, 0.0, 1.0);\n"
- "}\n";
-
- static constexpr const char *fragmentShaderSource_ = "#version 110\n"
- "uniform lowp vec4 color;\n"
- "void main() {\n"
- " gl_FragColor = color;\n"
- "}\n";
-
- // Must be called on initialization.
- bool compile(void);
-
- // Render a LineShaderData with a given M/V/P transformation.
- void draw(const LineShaderData &data, const QColor &color, float thickness, const QMatrix4x4 &projection);
-};
-
class PeriodicRunner : public QThread
{
Q_OBJECT