diff options
Diffstat (limited to 'gui/lineshader.h')
-rw-r--r-- | gui/lineshader.h | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/gui/lineshader.h b/gui/lineshader.h new file mode 100644 index 00000000..3f4c4057 --- /dev/null +++ b/gui/lineshader.h @@ -0,0 +1,209 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef LINESHADER_H +#define LINESHADER_H + +#include <QOpenGLBuffer> +#include <QOpenGLFunctions> +#include <QOpenGLShaderProgram> +#include <QOpenGLVertexArrayObject> +#include <QOpenGLWidget> + +#include "nextpnr.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); +}; + +NEXTPNR_NAMESPACE_END + +#endif |