Tue, 27 Jul 2021 16:29:00 +0300
Add vertex rendering
--- a/CMakeLists.txt Tue Jul 27 13:23:34 2021 +0300 +++ b/CMakeLists.txt Tue Jul 27 16:29:00 2021 +0300 @@ -48,6 +48,7 @@ src/gl/compiler.cpp src/gl/gridprogram.cpp src/gl/partrenderer.cpp + src/gl/vertexprogram.cpp src/linetypes/comment.cpp src/linetypes/conditionaledge.cpp src/linetypes/edge.cpp @@ -101,6 +102,7 @@ src/gl/compiler.h src/gl/gridprogram.h src/gl/partrenderer.h + src/gl/vertexprogram.h src/linetypes/comment.h src/linetypes/conditionaledge.h src/linetypes/edge.h
--- a/src/document.cpp Tue Jul 27 13:23:34 2021 +0300 +++ b/src/document.cpp Tue Jul 27 16:29:00 2021 +0300 @@ -31,10 +31,10 @@ model{model}, documents{documents}, colorTable{colorTable}, + vertexMap{model}, renderer{new Canvas{model, documents, colorTable, this}}, ui{*new Ui::Document}, - objectEditor{model, ldraw::NULL_ID, this}, - vertexMap{model} + objectEditor{model, ldraw::NULL_ID, this} { this->ui.setupUi(this); this->ui.listView->setModel(model); @@ -81,6 +81,10 @@ { Q_EMIT this->mouseMove(this, canvas); }); + connect(&this->vertexMap, &VertexMap::verticesChanged, [&]() + { + this->renderer->rebuildVertices(this); + }); } Document::~Document() @@ -113,6 +117,11 @@ return this->model->edit(); } +void Document::applyToVertices(VertexMap::ApplyFunction fn) const +{ + this->vertexMap.apply(fn); +} + void Document::selectionChanged(const QSet<ldraw::id_t>& newSelection) { if (newSelection.size() == 1)
--- a/src/document.h Tue Jul 27 13:23:34 2021 +0300 +++ b/src/document.h Tue Jul 27 16:29:00 2021 +0300 @@ -44,6 +44,7 @@ void setRenderPreferences(const gl::RenderPreferences& newPreferences); void setCanvasOverpaintCallback(Canvas::OverpaintCallback fn); Model::EditContext editModel(); + void applyToVertices(VertexMap::ApplyFunction fn) const; Q_SIGNALS: void newStatusText(const QString& newStatusText); void splitterChanged(); @@ -54,8 +55,8 @@ Model* model; DocumentManager* const documents; const ldraw::ColorTable& colorTable; + VertexMap vertexMap; Canvas* renderer; Ui::Document& ui; ObjectEditor objectEditor; - VertexMap vertexMap; };
--- a/src/gl/axesprogram.cpp Tue Jul 27 13:23:34 2021 +0300 +++ b/src/gl/axesprogram.cpp Tue Jul 27 16:29:00 2021 +0300 @@ -112,3 +112,8 @@ this->program->setAttributeBuffer(0, GL_FLOAT, offsetof(AxesVertex, position), 3, stride); this->program->setAttributeBuffer(1, GL_FLOAT, offsetof(AxesVertex, color), 3, stride); } + +QOpenGLBuffer::UsagePattern AxesProgram::usagePattern() const +{ + return QOpenGLBuffer::StaticDraw; +}
--- a/src/gl/axesprogram.h Tue Jul 27 13:23:34 2021 +0300 +++ b/src/gl/axesprogram.h Tue Jul 27 16:29:00 2021 +0300 @@ -15,4 +15,5 @@ int vertexSize() const override; int vertexCount() const override; void setupVertexArrays() override; + QOpenGLBuffer::UsagePattern usagePattern() const override; };
--- a/src/gl/basicshaderprogram.cpp Tue Jul 27 13:23:34 2021 +0300 +++ b/src/gl/basicshaderprogram.cpp Tue Jul 27 16:29:00 2021 +0300 @@ -37,8 +37,12 @@ this->program->bind(); this->buffer.create(); this->buffer.bind(); - this->buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); - this->buffer.allocate(this->vertexData(), this->vertexCount() * this->vertexSize()); + const QOpenGLBuffer::UsagePattern pattern = this->usagePattern(); + this->buffer.setUsagePattern(pattern); + if (pattern == QOpenGLBuffer::StaticDraw) + { + this->buffer.allocate(this->vertexData(), this->vertexCount() * this->vertexSize()); + } this->vertexArrayObject.create(); this->vertexArrayObject.bind(); this->setupVertexArrays();
--- a/src/gl/basicshaderprogram.h Tue Jul 27 13:23:34 2021 +0300 +++ b/src/gl/basicshaderprogram.h Tue Jul 27 16:29:00 2021 +0300 @@ -37,6 +37,7 @@ virtual void setupVertexArrays() = 0; // \returns what kind of elements are drawn (GL_QUADS, GL_TRIANGLES, GL_LINES, etc) virtual GLenum drawMode() const = 0; + virtual QOpenGLBuffer::UsagePattern usagePattern() const = 0; bool isInitialized = false; QOpenGLBuffer buffer; QOpenGLShader vertexShader;
--- a/src/gl/gridprogram.cpp Tue Jul 27 13:23:34 2021 +0300 +++ b/src/gl/gridprogram.cpp Tue Jul 27 16:29:00 2021 +0300 @@ -121,3 +121,8 @@ { return GL_QUADS; } + +QOpenGLBuffer::UsagePattern GridProgram::usagePattern() const +{ + return QOpenGLBuffer::StaticDraw; +}
--- a/src/gl/gridprogram.h Tue Jul 27 13:23:34 2021 +0300 +++ b/src/gl/gridprogram.h Tue Jul 27 16:29:00 2021 +0300 @@ -35,6 +35,7 @@ int vertexCount() const override; void setupVertexArrays() override; GLenum drawMode() const override; + QOpenGLBuffer::UsagePattern usagePattern() const override; private: glm::vec4 gridColor = {1.0f, 1.0f, 1.0f, 0.75f}; };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gl/vertexprogram.cpp Tue Jul 27 16:29:00 2021 +0300 @@ -0,0 +1,115 @@ +#include "vertexprogram.h" +#include "document.h" + +static const char vertexShaderSource[] = R"( +#version 330 core + +layout (location = 0) in vec3 in_position; +layout (location = 1) in vec3 in_color; +uniform mat4 view; +uniform mat4 projection; +uniform mat4 model; +smooth out vec3 ex_color; + +void main() +{ + gl_Position = projection * view * model * vec4(in_position, 1.0); + ex_color = in_color; +} +)"; + +static const char fragmentShaderSource[] = R"( +#version 330 core + +out vec4 color; +smooth in vec3 ex_color; + +void main(void) +{ + color = vec4(ex_color, 1); +} +)"; + +static constexpr int VERTICES_PER_OBJECT = 16; + + +VertexProgram::VertexProgram(QObject *parent) : + AbstractBasicShaderProgram{parent} +{ +} + +const char *VertexProgram::vertexShaderSource() const +{ + return ::vertexShaderSource; +} + +const char *VertexProgram::fragmentShaderSource() const +{ + return ::fragmentShaderSource; +} + +const void *VertexProgram::vertexData() const +{ + return this->data.data(); +} + +int VertexProgram::vertexSize() const +{ + return sizeof(Vertex); +} + +int VertexProgram::vertexCount() const +{ + return this->data.size(); +} + +void VertexProgram::setupVertexArrays() +{ + for (int i : {0, 1}) + { + this->program->enableAttributeArray(i); + } + const int stride = this->vertexSize(); + this->program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, position), 3, stride); + this->program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, color), 3, stride); +} + +GLenum VertexProgram::drawMode() const +{ + return GL_LINES; +} + +QOpenGLBuffer::UsagePattern VertexProgram::usagePattern() const +{ + return QOpenGLBuffer::DynamicDraw; +} + +void VertexProgram::build(const Document *document) +{ + constexpr float size = 1; + constexpr glm::vec3 color = {0.0, 1.0, 1.0}; + this->data.clear(); + document->applyToVertices([&](const glm::vec3& vertex, const std::set<ldraw::id_t>&) + { + reserveMore(this->data, VERTICES_PER_OBJECT); + this->data.push_back({vertex, color}); + this->data.push_back({vertex + glm::vec3{size, -size, size}, color}); + this->data.push_back({vertex, color}); + this->data.push_back({vertex + glm::vec3{size, -size, -size}, color}); + this->data.push_back({vertex, color}); + this->data.push_back({vertex + glm::vec3{-size, -size, -size}, color}); + this->data.push_back({vertex, color}); + this->data.push_back({vertex + glm::vec3{-size, -size, size}, color}); + this->data.push_back({vertex + glm::vec3{size, -size, size}, color}); + this->data.push_back({vertex + glm::vec3{size, -size, -size}, color}); + this->data.push_back({vertex + glm::vec3{size, -size, -size}, color}); + this->data.push_back({vertex + glm::vec3{-size, -size, -size}, color}); + this->data.push_back({vertex + glm::vec3{-size, -size, -size}, color}); + this->data.push_back({vertex + glm::vec3{-size, -size, size}, color}); + this->data.push_back({vertex + glm::vec3{-size, -size, size}, color}); + this->data.push_back({vertex + glm::vec3{size, -size, size}, color}); + }); + this->buffer.bind(); + this->buffer.allocate(this->vertexData(), this->vertexCount() * this->vertexSize()); + this->buffer.release(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gl/vertexprogram.h Tue Jul 27 16:29:00 2021 +0300 @@ -0,0 +1,29 @@ +#ifndef VERTEXPROGRAM_H +#define VERTEXPROGRAM_H +#include "basicshaderprogram.h" +class Document; + +class VertexProgram : public AbstractBasicShaderProgram +{ +public: + struct Vertex + { + glm::vec3 position; + glm::vec3 color; + }; + VertexProgram(QObject* parent = nullptr); + void build(const Document* document); +protected: + const char* vertexShaderSource() const override; + const char* fragmentShaderSource() const override; + const void* vertexData() const override; + int vertexSize() const override; + int vertexCount() const override; + void setupVertexArrays() override; + GLenum drawMode() const override; + QOpenGLBuffer::UsagePattern usagePattern() const override; +private: + std::vector<Vertex> data; +}; + +#endif // VERTEXPROGRAM_H
--- a/src/ui/canvas.cpp Tue Jul 27 13:23:34 2021 +0300 +++ b/src/ui/canvas.cpp Tue Jul 27 16:29:00 2021 +0300 @@ -21,6 +21,15 @@ this->update(); } +void Canvas::rebuildVertices(Document* document) +{ + if (this->vertexProgram.has_value()) + { + this->vertexProgram->build(document); + this->update(); + } +} + void Canvas::mouseMoveEvent(QMouseEvent* event) { const ldraw::id_t id = this->pick(event->pos()); @@ -105,9 +114,12 @@ this->gridProgram->initialize(); this->axesProgram.emplace(this); this->axesProgram->initialize(); + this->vertexProgram.emplace(this); + this->vertexProgram->initialize(); for (AbstractBasicShaderProgram* program : { static_cast<AbstractBasicShaderProgram*>(&*this->gridProgram), static_cast<AbstractBasicShaderProgram*>(&*this->axesProgram), + static_cast<AbstractBasicShaderProgram*>(&*this->vertexProgram), }) { connect(this, &PartRenderer::projectionMatrixChanged, @@ -156,6 +168,14 @@ this->gridProgram->draw(); glDisable(GL_BLEND); } + // Render vertices + { + glLineWidth(2); + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + this->vertexProgram->draw(); + glDisable(GL_LINE_SMOOTH); + } if (this->worldPosition.has_value()) { QPainter painter{this};
--- a/src/ui/canvas.h Tue Jul 27 13:23:34 2021 +0300 +++ b/src/ui/canvas.h Tue Jul 27 16:29:00 2021 +0300 @@ -5,6 +5,7 @@ #include "gl/partrenderer.h" #include "gl/gridprogram.h" #include "gl/axesprogram.h" +#include "gl/vertexprogram.h" class Canvas : public PartRenderer { @@ -24,6 +25,7 @@ const std::optional<glm::vec3>& getWorldPosition() const; public Q_SLOTS: void handleSelectionChange(const QSet<ldraw::id_t>& selectedIds, const QSet<ldraw::id_t>& deselectedIds); + void rebuildVertices(Document *document); protected: void mouseMoveEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override; @@ -41,6 +43,7 @@ bool isGridPerpendicularToScreen(float threshold) const; std::optional<GridProgram> gridProgram; std::optional<AxesProgram> axesProgram; + std::optional<VertexProgram> vertexProgram; std::optional<glm::vec3> worldPosition; glm::mat4 gridMatrix; geom::Plane gridPlane;
--- a/src/vertexmap.cpp Tue Jul 27 13:23:34 2021 +0300 +++ b/src/vertexmap.cpp Tue Jul 27 16:29:00 2021 +0300 @@ -28,12 +28,29 @@ void VertexMap::build() { this->map.clear(); + this->vertices.clear(); this->model->apply<ldraw::Object>([&](const ldraw::Object* object) { for (int i = 0; i < object->numPoints(); i += 1) { const glm::vec3& point = object->getPoint(i); - this->map[qHash(point)].insert(object->id); + const unsigned int hash = qHash(point); + this->map[hash].insert(object->id); + this->vertices[hash] = point; } }); + Q_EMIT this->verticesChanged(); } + +/** + * @brief Apply \c fn for all vertices in the map. + * @param fn + */ +void VertexMap::apply(ApplyFunction fn) const +{ + for (auto it = this->map.cbegin(); it != this->map.cend(); ++it) + { + const glm::vec3& point = this->vertices.at(it->first); + fn(point, it->second); + } +}
--- a/src/vertexmap.h Tue Jul 27 13:23:34 2021 +0300 +++ b/src/vertexmap.h Tue Jul 27 16:29:00 2021 +0300 @@ -4,14 +4,22 @@ #include "main.h" #include "model.h" +/** + * @brief Collects a map of all vertices in a model to all objects that use the specified vertex. + */ class VertexMap : public QObject { Q_OBJECT public: + using ApplyFunction = std::function<void(const glm::vec3&, const std::set<ldraw::id_t>&)>; VertexMap(const Model *model); Q_SLOT void build(); + void apply(ApplyFunction fn) const; +Q_SIGNALS: + Q_SIGNAL void verticesChanged(); private: const Model* const model; + std::map<unsigned int, glm::vec3> vertices; std::map<unsigned int, std::set<ldraw::id_t>> map; };