/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Serge Bazanski * * 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 #include #include #include #include #include #include "log.h" #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 vertices; std::vector normals; std::vector miters; std::vector indices; int last_render = 0; 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 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 Buffers { QOpenGLBuffer position; QOpenGLBuffer normal; QOpenGLBuffer miter; QOpenGLBuffer index; QOpenGLVertexArrayObject vao; int indices = 0; int last_vbo_update = 0; }; std::array 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_; public: LineShader(QObject *parent) : parent_(parent), program_(nullptr) {} static constexpr const char *vertexShaderSource_ = "#version 150\n" "in highp vec2 position;\n" "in highp vec2 normal;\n" "in 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 150\n" "uniform lowp vec4 color;\n" "out vec4 Out_Color;\n" "void main() {\n" " Out_Color = color;\n" "}\n"; // Must be called on initialization. bool compile(void); void update_vbos(enum GraphicElement::style_t style, const LineShaderData &line); // Render a LineShaderData with a given M/V/P transformation. void draw(enum GraphicElement::style_t style, const QColor &color, float thickness, const QMatrix4x4 &projection); }; NEXTPNR_NAMESPACE_END #endif