# HG changeset patch # User Teemu Piippo # Date 1579645409 -7200 # Node ID 3a9e761e4faa63acdf83c0ee4f373b585abad924 # Parent 6de5ac1fb471e0332a3ebfbc9ab9c577aaf31a87 at least VAOs work now diff -r 6de5ac1fb471 -r 3a9e761e4faa CMakeLists.txt --- a/CMakeLists.txt Sun Jan 19 14:25:57 2020 +0200 +++ b/CMakeLists.txt Wed Jan 22 00:23:29 2020 +0200 @@ -18,6 +18,7 @@ include_directories(${GLUT_INCLUDE_DIR}) set (LDFORGE_SOURCES + src/colors.cpp src/document.cpp src/documentmanager.cpp src/libraries.cpp diff -r 6de5ac1fb471 -r 3a9e761e4faa src/basics.h --- a/src/basics.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/basics.h Wed Jan 22 00:23:29 2020 +0200 @@ -41,6 +41,18 @@ Z = 2 }; +enum Result +{ + Success = 0, + PartialSuccess, + Failure +}; + +constexpr bool failed(Result r) +{ + return r == Failure; +} + enum Winding { NoWinding, diff -r 6de5ac1fb471 -r 3a9e761e4faa src/colors.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/colors.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -0,0 +1,84 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2020 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "colors.h" + +const ColorTable::ColorDefinition ColorTable::unknownColor{{}, {}, "Unknown"}; + +void ColorTable::clear() +{ + definitions = {}; +} + +Result ColorTable::load(QIODevice& device, QTextStream& errors) +{ + this->clear(); + if (device.isReadable()) + { + QTextStream stream{&device}; + QString line; + while (stream.readLineInto(&line)) + { + this->loadColorFromString(line); + } + return Success; + } + else + { + errors << "could not read colors"; + return Failure; + } +} + +const ColorTable::ColorDefinition& ColorTable::operator[](Color color) const +{ + auto it = this->definitions.find(color.index); + if (it != this->definitions.end()) + { + return *it; + } + else + { + return unknownColor; + } +} + +void ColorTable::loadColorFromString(const QString& string) +{ + const QRegExp pattern{ + R"(^\s*0 \!COLOUR\s+([^\s]+)\s+)"_q + + R"(CODE\s+(\d+)\s+)"_q + + R"(VALUE\s+(\#[0-9a-fA-F]{3,6})\s+)"_q + + R"(EDGE\s+(\#[0-9a-fA-F]{3,6}))"_q + + R"((?:\s+ALPHA\s+(\d+))?)"_q + }; + if (pattern.indexIn(string) != -1) + { + const int code = pattern.cap(2).toInt(); + ColorDefinition& definition = definitions[code]; + definition = {}; // in case there's an existing definition + definition.name = pattern.cap(1); + definition.faceColor = pattern.cap(3); + definition.edgeColor = pattern.cap(4); + if (not pattern.cap(5).isEmpty()) + { + const int alpha = pattern.cap(5).toInt(); + definition.faceColor.setAlpha(alpha); + } + } +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/colors.h --- a/src/colors.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/colors.h Wed Jan 22 00:23:29 2020 +0200 @@ -17,6 +17,7 @@ */ #pragma once +#include #include "main.h" struct Color @@ -24,6 +25,24 @@ qint32 index; }; +class ColorTable +{ +public: + struct ColorDefinition + { + QColor faceColor; + QColor edgeColor; + QString name; + }; + void clear(); + Result load(QIODevice& device, QTextStream& errors); + const ColorDefinition& operator[](Color index) const; + static const ColorDefinition unknownColor; +private: + void loadColorFromString(const QString& string); + QMap definitions; +}; + inline bool operator==(const Color& one, const Color& other) { return one.index == other.index; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/document.cpp --- a/src/document.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/document.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -20,11 +20,12 @@ #include "ui_document.h" #include "model.h" -Document::Document(Model* model, DocumentManager* documents, QWidget* parent) : +Document::Document(Model* model, DocumentManager* documents, const ColorTable& colorTable, QWidget* parent) : QWidget{parent}, model{model}, documents{documents}, - renderer{new PartRenderer{model, documents, this}}, + colorTable{colorTable}, + renderer{new PartRenderer{model, documents, colorTable, this}}, ui{*new Ui::Document} { this->ui.setupUi(this); diff -r 6de5ac1fb471 -r 3a9e761e4faa src/document.h --- a/src/document.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/document.h Wed Jan 22 00:23:29 2020 +0200 @@ -32,7 +32,7 @@ { Q_OBJECT public: - explicit Document(Model* model, DocumentManager* documents, QWidget *parent = nullptr); + explicit Document(Model* model, DocumentManager* documents, const ColorTable& colorTable, QWidget *parent = nullptr); ~Document(); QByteArray saveSplitterState() const; void restoreSplitterState(const QByteArray& state); @@ -41,6 +41,7 @@ private: Model* model; DocumentManager* const documents; + const ColorTable& colorTable; PartRenderer* renderer; Ui::Document& ui; }; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/gl/common.h --- a/src/gl/common.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/gl/common.h Wed Jan 22 00:23:29 2020 +0200 @@ -129,31 +129,27 @@ } // Vbo names - enum class VboClass : std::uint8_t + enum class ArrayClass : std::uint8_t { Lines, Triangles, Quads, ConditionalLines }; - constexpr int numVboClasses = 4; + constexpr ArrayClass ARRAY_CLASSES[] = {ArrayClass::Lines, ArrayClass::Triangles, ArrayClass::Quads, ArrayClass::ConditionalLines}; + constexpr int NUM_ARRAY_CLASSES = countof(ARRAY_CLASSES); + constexpr int FLOATS_PER_VERTEX = 7; // Types of vbo per object enum class VboSubclass : std::uint8_t { - Surfaces, - RegularColors, - PickColors, - BfcFrontColors, - BfcBackColors, - RandomColors, - Normals, - InvertedNormals + VertexData, + Normals }; - constexpr int numVboSubclasses = 8; + constexpr int numVboSubclasses = 2; // Amount of vbos overall - constexpr int numVbos = gl::numVboClasses * gl::numVboSubclasses; + constexpr int numVbos = gl::NUM_ARRAY_CLASSES * gl::numVboSubclasses; enum class RenderStyle { @@ -162,4 +158,9 @@ BfcRedGreen, RandomColors }; + + inline void* offset(const int n) + { + return reinterpret_cast(n); + } } diff -r 6de5ac1fb471 -r 3a9e761e4faa src/gl/compiler.cpp --- a/src/gl/compiler.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/gl/compiler.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -19,27 +19,52 @@ #define GL_GLEXT_PROTOTYPES #include #include +#include #include "gl/compiler.h" #include "documentmanager.h" #include "invert.h" #include "ring.h" -gl::Compiler::Compiler(QObject* parent) : - QObject{parent} +gl::Compiler::Compiler(const ColorTable& colorTable, QObject* parent) : + QObject{parent}, + colorTable{colorTable} { } gl::Compiler::~Compiler() { + if (this->initialized) + { + for (int arrayId = 0; arrayId < gl::NUM_ARRAY_CLASSES; arrayId += 1) + { + glDeleteProgram(this->glObjects[arrayId].program); + glDeleteShader(this->glObjects[arrayId].vertexShader); + glDeleteProgram(this->glObjects[arrayId].fragmentShader); + } + } +} +void gl::Compiler::initialize() +{ + if (not this->initialized) + { + this->initializeOpenGLFunctions(); + for (int arrayId = 0; arrayId < gl::NUM_ARRAY_CLASSES; arrayId += 1) + { + QOpenGLVertexArrayObject& vao = this->vertexArrays[arrayId]; + vao.create(); + vao.bind(); + glGenBuffers(gl::numVboSubclasses, &this->storedVbo[gl::numVboSubclasses * arrayId]); + this->buildShaders(arrayId); + vao.release(); + } + //glGenBuffers(countof(this->storedVbo), &this->storedVbo[0]); + this->initialized = true; + } } void gl::Compiler::build(Model* model, DocumentManager* context) { - if (not this->initialized) - { - this->initializeVbo(); - } this->boundingBox = {}; std::vector vboData[gl::numVbos]; const std::vector polygons = model->getPolygons(context); @@ -47,29 +72,41 @@ { this->buildPolygon(polygon, vboData); } - for (int i = 0; i < gl::numVbos; i += 1) + /* + for (int index = 0; index < gl::numVbos; index += 1) + { + this->upload(index, vboData[index]); + }*/ + for (int arrayId = 0; arrayId < gl::NUM_ARRAY_CLASSES; arrayId += 1) { - this->upload(i, vboData[i]); + this->vertexArrays[arrayId].bind(); + for (int i = 0; i < gl::numVboSubclasses; i += 1) + { + const int vboIndex = gl::vboIndex({static_cast(arrayId), static_cast(i)}); + this->upload(vboIndex, vboData[vboIndex]); + } + this->vertexArrays[arrayId].release(); } } -gl::VboClass classifyPolygon(const gl::Polygon& polygon) +gl::ArrayClass classifyPolygon(const gl::Polygon& polygon) { switch (polygon.type) { case gl::Polygon::EdgeLine: - return gl::VboClass::Lines; + return gl::ArrayClass::Lines; case gl::Polygon::Triangle: - return gl::VboClass::Triangles; + return gl::ArrayClass::Triangles; case gl::Polygon::Quadrilateral: - return gl::VboClass::Quads; + return gl::ArrayClass::Quads; case gl::Polygon::ConditionalEdge: - return gl::VboClass::ConditionalLines; + return gl::ArrayClass::ConditionalLines; } - return gl::VboClass::Lines; + return gl::ArrayClass::Lines; } -QColor colorFromId(linetypes::Id id) +[[maybe_unused]] +static QColor colorFromId(linetypes::Id id) { // Calculate a color based from this index. This method caters for // 16777216 objects. I don't think that will be exceeded anytime soon. @@ -79,129 +116,62 @@ return {r, g, b}; } -void writeVertex(std::vector& data, const Point3D& point) -{ - data.push_back(static_cast(point.x)); - data.push_back(static_cast(point.y)); - data.push_back(static_cast(point.z)); -} - void gl::Compiler::buildPolygon(gl::Polygon polygon, std::vector* vboData) { - const gl::VboClass vboClass = classifyPolygon(polygon); - auto vboBuffer = [&](VboSubclass subclass) -> std::vector& - { - return vboData[gl::vboIndex({vboClass, subclass})]; - }; + const gl::ArrayClass vboClass = classifyPolygon(polygon); + std::vector& vertexBuffer = vboData[gl::vboIndex({vboClass, gl::VboSubclass::VertexData})]; + std::vector& normalsBuffer = vboData[gl::vboIndex({vboClass, gl::VboSubclass::Normals})]; auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices()); + reserveMore(normalsBuffer, polygon.numPolygonVertices() * 3_z); for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) { const Point3D& v1 = vertexRing[i - 1]; const Point3D& v2 = vertexRing[i]; const Point3D& v3 = vertexRing[i + 1]; const QVector3D normal = QVector3D::crossProduct(v3 - v2, v1 - v2).normalized(); - std::vector& data = vboBuffer(VboSubclass::Normals); for (const GLfloat coord : {normal.x(), normal.y(), normal.z()}) - data.push_back(coord); + normalsBuffer.push_back(coord); } - vboBuffer(VboSubclass::Surfaces).reserve(vboBuffer(VboSubclass::Surfaces).size() + polygon.numPolygonVertices()); + reserveMore(vertexBuffer, polygon.numPolygonVertices() * 7); + const QColor color = this->getColorForPolygon(polygon); // Transform vertices so that they're suitable for GL rendering for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) { + Point3D& point = polygon.vertices[i]; polygon.vertices[i].y = -polygon.vertices[i].y; polygon.vertices[i].z = -polygon.vertices[i].z; this->boundingBox.consider(polygon.vertices[i]); - writeVertex(vboBuffer(VboSubclass::Surfaces), polygon.vertices[i]); + vertexBuffer.push_back(static_cast(point.x)); + vertexBuffer.push_back(static_cast(point.y)); + vertexBuffer.push_back(static_cast(point.z)); + vertexBuffer.push_back(static_cast(color.redF())); + vertexBuffer.push_back(static_cast(color.greenF())); + vertexBuffer.push_back(static_cast(color.blueF())); + vertexBuffer.push_back(static_cast(color.alphaF())); } - this->writeColor(vboData, polygon, VboSubclass::RegularColors); - this->writeColor(vboData, polygon, VboSubclass::PickColors); - this->writeColor(vboData, polygon, VboSubclass::BfcFrontColors); - this->writeColor(vboData, polygon, VboSubclass::BfcBackColors); } -QColor gl::Compiler::getColorForPolygon(const gl::Polygon& polygon, VboSubclass subclass) +QColor gl::Compiler::getColorForPolygon(const gl::Polygon& polygon) { QColor color; - - switch (subclass) + // For normal colors, use the polygon's color. + if (polygon.color == colors::main) { - case VboSubclass::Surfaces: - case VboSubclass::Normals: - case VboSubclass::InvertedNormals: - // Surface and normal VBOs contain vertex data, not colors. So we can't return anything meaningful. - return {}; - case VboSubclass::BfcFrontColors: - // Use the constant green color for BFC front colors - return {64, 192, 80}; - case VboSubclass::BfcBackColors: - // Use the constant red color for BFC back colors - return {208, 64, 64}; - case VboSubclass::PickColors: - // For the picking scene, use unique picking colors provided by the model. - return colorFromId(polygon.id); - case VboSubclass::RandomColors: - // For the random color scene, the owner object has rolled up a random color. Use that. - color = {255, 64, 255}; // polygonOwner->randomColor(); - break; - case VboSubclass::RegularColors: - // For normal colors, use the polygon's color. - if (polygon.color == colors::main) - { - color = {255, 255, 64}; // mainColorRepresentation(); - } - else if (polygon.color == colors::edge) - { - // Edge color is black, unless we have a dark background, in which case lines need to be bright. - color = Qt::black; //luma(config::backgroundColor()) > 40 ? Qt::black : Qt::white; - } - else - { - // Not main or edge color, use the polygon's color as is. - color = {255, 255, 64}; //polygon.color.faceColor(); - } - break; + color = {255, 255, 64}; // mainColorRepresentation(); } - - if (color.isValid()) + else if (polygon.color == colors::edge) { - // We may wish to apply blending on the color to indicate selection or highlight. - /* - const double blendAlpha = 0.0; - if (blendAlpha != 0.0) - { - QColor selectedColor = config::selectColorBlend(); - double denominator = blendAlpha + 1.0; - color.setRed((color.red() + (selectedColor.red() * blendAlpha)) / denominator); - color.setGreen((color.green() + (selectedColor.green() * blendAlpha)) / denominator); - color.setBlue((color.blue() + (selectedColor.blue() * blendAlpha)) / denominator); - } - */ + // Edge color is black, unless we have a dark background, in which case lines need to be bright. + color = Qt::black; //luma(config::backgroundColor()) > 40 ? Qt::black : Qt::white; } else { - if (polygon.numPolygonVertices() == 2) - color = Qt::black; - else - color = {255, 255, 64}; + // Not main or edge color, use the polygon's color as is. + color = this->colorTable[polygon.color].faceColor; } - return color; } -void gl::Compiler::writeColor(std::vector* data, const gl::Polygon& polygon, VboSubclass subclass) -{ - std::vector& buffer = data[gl::vboIndex({classifyPolygon(polygon), subclass})]; - const QColor color = this->getColorForPolygon(polygon, subclass); - buffer.reserve(data->size() + 4 * polygon.numPolygonVertices()); - for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) - { - buffer.push_back(static_cast(color.redF())); - buffer.push_back(static_cast(color.greenF())); - buffer.push_back(static_cast(color.blueF())); - buffer.push_back(static_cast(color.alphaF())); - } -} - Point3D gl::Compiler::modelCenter() const { return boxCenter(this->boundingBox); @@ -212,11 +182,54 @@ return longestMeasure(this->boundingBox); } -void gl::Compiler::initializeVbo() +void gl::Compiler::bindVertexArray(gl::ArrayClass arrayClass) +{ + this->vertexArrays[static_cast(arrayClass)].bind(); + glUseProgram(this->glObjects[static_cast(arrayClass)].program); +} + +void gl::Compiler::releaseVertexArray(gl::ArrayClass arrayClass) +{ + this->vertexArrays[static_cast(arrayClass)].release(); +} + +void gl::Compiler::buildShaders(int arrayId) { - this->initializeOpenGLFunctions(); - glGenBuffers(countof(this->storedVbo), &this->storedVbo[0]); - this->initialized = true; + /* + this->glObjects[arrayId].vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(this->glObjects[arrayId].vertexShader, 1, &vertexShaderSource, nullptr); + glCompileShader(this->glObjects[arrayId].vertexShader); + this->glObjects[arrayId].fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(this->glObjects[arrayId].fragmentShader, 1, &fragmentShaderSource, nullptr); + glCompileShader(this->glObjects[arrayId].fragmentShader); + for (auto&& pair : { + std::make_pair(this->glObjects[arrayId].vertexShader, tr("vertex shader")), + std::make_pair(this->glObjects[arrayId].fragmentShader, tr("fragment shader")), + }) + { + GLint status; + glGetShaderiv(this->glObjects[arrayId].fragmentShader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) + { + char compileLog[512]; + glGetShaderInfoLog(pair.first, countof(compileLog), nullptr, compileLog); + QMessageBox::critical(nullptr, tr("Shader compile error"), tr("Unable to compile the %1. Compile log:\n\n%2").arg(pair.second).arg(compileLog)); + abort(); + } + } + this->glObjects[arrayId].program = glCreateProgram(); + glAttachShader(this->glObjects[arrayId].program, this->glObjects[arrayId].vertexShader); + glAttachShader(this->glObjects[arrayId].program, this->glObjects[arrayId].fragmentShader); + glLinkProgram(this->glObjects[arrayId].program); + glUseProgram(this->glObjects[arrayId].program); + const std::size_t size = gl::FLOATS_PER_VERTEX * sizeof(GLfloat); + const GLuint posAttrib = static_cast(glGetAttribLocation(this->glObjects[arrayId].program, "position")); + glEnableVertexAttribArray(posAttrib); + glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, size, gl::offset(0)); + const GLuint colAttrib = static_cast(glGetAttribLocation(this->glObjects[arrayId].program, "color")); + glEnableVertexAttribArray(colAttrib); + glVertexAttribPointer(colAttrib, 4, GL_FLOAT, GL_FALSE, size, gl::offset(3 * sizeof(GLfloat))); + */ } /// @@ -228,7 +241,6 @@ { glBindBuffer(GL_ARRAY_BUFFER, this->storedVbo[vboIndex]); glBufferData(GL_ARRAY_BUFFER, static_cast(data.size() * sizeof data[0]), data.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); this->storedVboSizes[vboIndex] = data.size(); } diff -r 6de5ac1fb471 -r 3a9e761e4faa src/gl/compiler.h --- a/src/gl/compiler.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/gl/compiler.h Wed Jan 22 00:23:29 2020 +0200 @@ -22,6 +22,9 @@ #include "types/boundingbox.h" #include #include +#include +#include +#include class Model; class DocumentManager; @@ -32,7 +35,7 @@ class Renderer; struct VboAddress { - VboClass vboClass; + ArrayClass vboClass; VboSubclass vboSubclass; }; int vboIndex(const VboAddress vboAddress); @@ -42,24 +45,34 @@ { Q_OBJECT public: - Compiler(QObject* parent); + Compiler(const ColorTable& colorTable, QObject* parent); ~Compiler(); void build(Model* model, DocumentManager* context); void buildPolygon(Polygon polygon, std::vector* vboData); void upload(const int vboIndex, const std::vector& data); GLuint vbo(const VboAddress vboAddress) const; std::size_t vboSize(const VboAddress vboAddress) const; - QColor getColorForPolygon(const gl::Polygon& polygon, VboSubclass subclass); - void writeColor(std::vector* data, const gl::Polygon& polygon, VboSubclass subclass); + QColor getColorForPolygon(const gl::Polygon& polygon); Point3D modelCenter() const; double modelDistance() const; + void initialize(); + void bindVertexArray(gl::ArrayClass arrayClass); + void releaseVertexArray(gl::ArrayClass arrayClass); + void buildShaders(int arrayId); private: - void initializeVbo(); - GLuint storedVbo[gl::numVbos]; + QOpenGLVertexArrayObject vertexArrays[gl::NUM_ARRAY_CLASSES]; + //GLuint storedVbo[gl::numVbos]; bool m_vboChanged[gl::numVbos] = {true}; std::size_t storedVboSizes[gl::numVbos] = {0_z}; bool initialized = false; BoundingBox boundingBox; + const ColorTable& colorTable; + struct + { + QOpenGLShaderProgram* program = nullptr; + QOpenGLBuffer buffer{QOpenGLBuffer::VertexBuffer}; + QOpenGLVertexArrayObject vertexArray; + } glObjects[gl::NUM_ARRAY_CLASSES]; }; #define CHECK_GL_ERROR() { checkGLError(__FILE__, __LINE__); } diff -r 6de5ac1fb471 -r 3a9e761e4faa src/gl/partrenderer.cpp --- a/src/gl/partrenderer.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/gl/partrenderer.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -16,19 +16,54 @@ * along with this program. If not, see . */ +#include #include -#include +#include #include "partrenderer.h" -PartRenderer::PartRenderer(Model* model, DocumentManager* documents, QWidget* parent) : + +static const char* vertexShaderSource = R"( +#version 330 core + +layout(location=0) in vec3 position; +layout(location=1) in vec4 color; +out vec4 vColor; +uniform mat4 CameraTransformation; + +void main() +{ + vColor = color; + gl_Position = CameraTransformation * vec4(position, 1.0); +} +)"; + +static const char* fragmentShaderSource = R"( +#version 330 core + +in vec4 vColor; +out vec4 fColor; + +void main() +{ + fColor = vColor; +} +)"; + +PartRenderer::PartRenderer(Model* model, DocumentManager* documents, const ColorTable& colorTable, QWidget* parent) : QOpenGLWidget{parent}, model{model}, documents{documents}, - compiler{new gl::Compiler{this}} + colorTable{colorTable}, + compiler{new gl::Compiler{this->colorTable, this}} { this->setMouseTracking(true); } +PartRenderer::~PartRenderer() +{ + delete this->objects.program; +} + void PartRenderer::initializeGL() { this->initializeOpenGLFunctions(); @@ -36,12 +71,64 @@ { abort(); } + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + //this->compiler->initialize(); + //this->compiler->build(this->model, this->documents); this->initializeLighting(); this->initialized = true; this->rotation = QQuaternion::fromAxisAndAngle({1, 0, 0}, 30); this->rotation *= QQuaternion::fromAxisAndAngle({0, 1, 0}, 330); - this->compiler->build(this->model, this->documents); glLineWidth(2.0); + this->objects.program = new QOpenGLShaderProgram; + this->objects.program->create(); + this->checkForGLErrors(); + this->objects.program->addShaderFromSourceCode(QOpenGLShader::Vertex, ::vertexShaderSource); + this->checkForGLErrors(); + this->objects.program->addShaderFromSourceCode(QOpenGLShader::Fragment, ::fragmentShaderSource); + this->checkForGLErrors(); + this->objects.program->link(); + this->checkForGLErrors(); + this->objects.program->bind(); + this->checkForGLErrors(); + this->objects.buffer.create(); + this->checkForGLErrors(); + this->objects.buffer.bind(); + this->checkForGLErrors(); + this->objects.buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + this->checkForGLErrors(); + /* + GLfloat data[] = { + 20.0f, 20.0f, 6.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 30.0f, 20.0f, 6.0f, 0.0f, 1.0f, 0.0f, 1.0f, + 30.0f, 30.0f, 6.0f, 0.0f, 0.0f, 1.0f, 1.0f, + }; + */ + GLfloat data[] = { + 0.00f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -0.75f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + 0.75f, -0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f + }; + this->objects.buffer.allocate(data, sizeof data); + this->checkForGLErrors(); + this->objects.vertexArray.create(); + this->checkForGLErrors(); + this->objects.vertexArray.bind(); + this->checkForGLErrors(); + this->objects.program->enableAttributeArray(0); + this->checkForGLErrors(); + this->objects.program->enableAttributeArray(1); + this->checkForGLErrors(); + this->objects.program->setAttributeBuffer(0, GL_FLOAT, 0, 3); + this->checkForGLErrors(); + this->objects.program->setAttributeBuffer(1, GL_FLOAT, 3, 4); + this->checkForGLErrors(); + this->objects.vertexArray.release(); + this->checkForGLErrors(); + this->objects.buffer.release(); + this->checkForGLErrors(); + this->objects.program->release(); + this->checkForGLErrors(); } /* @@ -85,29 +172,27 @@ glMatrixMode(GL_MODELVIEW); } -static GLenum getGlTypeForVboClass(const gl::VboClass vboClass) +static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass) { switch (vboClass) { - case gl::VboClass::Lines: - case gl::VboClass::ConditionalLines: + case gl::ArrayClass::Lines: + case gl::ArrayClass::ConditionalLines: return GL_LINES; - case gl::VboClass::Triangles: + case gl::ArrayClass::Triangles: return GL_TRIANGLES; - case gl::VboClass::Quads: + case gl::ArrayClass::Quads: return GL_QUADS; } throw std::runtime_error{"Bad vbo class passed to getGlTypeForVboClass"}; } -// https://www.codemiles.com/c-opengl-examples/drawing-teapot-using-opengl-t9010.html?mobile=on -#include void PartRenderer::paintGL() { + /* glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable (GL_POLYGON_OFFSET_FILL); - glPolygonOffset (1.0f, 1.0f); + */ glEnable (GL_DEPTH_TEST); glShadeModel (GL_SMOOTH); glEnable (GL_MULTISAMPLE); @@ -118,6 +203,20 @@ void PartRenderer::renderScene() { + glClearColor(0.8f, 0.8f, 0.8f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glLoadIdentity(); + //glTranslated(0.0, 0.0, -4.5 * this->compiler->modelDistance()); + //glTranslated(0.0, 0.0, -4.5); + //glMultMatrixf(padMatrix(this->rotation.toRotationMatrix()).constData()); + //xyz(glTranslatef, -this->compiler->modelCenter()); + auto rotationMatrix = padMatrix(this->rotation.toRotationMatrix()); + rotationMatrix(2, 3) = 0; + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0f, 1.0f); switch (this->renderStyle) { case gl::RenderStyle::Normal: @@ -128,60 +227,58 @@ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); break; } - glMatrixMode(GL_MODELVIEW); - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glLoadIdentity(); - glTranslatef(0.0, 0.0, -4.5 * this->compiler->modelDistance()); - glMultMatrixf(padMatrix(this->rotation.toRotationMatrix()).constData()); - xyz(glTranslatef, -this->compiler->modelCenter()); - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - for (const gl::VboClass vboClass : {gl::VboClass::Lines, gl::VboClass::Triangles, gl::VboClass::Quads}) - { - const GLuint vboSurfaces = this->compiler->vbo({vboClass, gl::VboSubclass::Surfaces}); - const GLuint vboColors = this->compiler->vbo({vboClass, gl::VboSubclass::RegularColors}); - const GLuint vboNormals = this->compiler->vbo({vboClass, gl::VboSubclass::Normals}); - const std::size_t count = this->compiler->vboSize({vboClass, gl::VboSubclass::Surfaces}) / 3_z; - glBindBuffer(GL_ARRAY_BUFFER, vboSurfaces); - glVertexPointer(3, GL_FLOAT, 0, nullptr); - glBindBuffer(GL_ARRAY_BUFFER, vboColors); - glColorPointer(4, GL_FLOAT, 0, nullptr); - glBindBuffer(GL_ARRAY_BUFFER, vboNormals); - glNormalPointer(GL_FLOAT, 0, nullptr); - glDrawArrays(getGlTypeForVboClass(vboClass), 0, static_cast(count)); - } - glBindBuffer(GL_ARRAY_BUFFER, 0); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - const GLenum glError = this->glGetError(); - if (glError != GL_NO_ERROR) + this->objects.program->bind(); + this->checkForGLErrors(); + const int cameraTransformationUniform = glGetUniformLocation(this->objects.program->programId(), "CameraTransformation"); + this->checkForGLErrors(); + this->objects.program->setUniformValue(cameraTransformationUniform, rotationMatrix); + this->checkForGLErrors(); + this->objects.vertexArray.bind(); + this->checkForGLErrors(); + glDrawArrays(GL_TRIANGLES, 0, 3); + this->checkForGLErrors(); + this->objects.vertexArray.release(); + this->checkForGLErrors(); + this->objects.program->release(); + this->checkForGLErrors(); +#if 0 + // Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering. + renderVao(gl::ArrayClass::Triangles); + renderVao(gl::ArrayClass::Quads); + renderVao(gl::ArrayClass::Lines); +#endif + glDisable(GL_POLYGON_OFFSET_FILL); +} + +void PartRenderer::renderVao(const gl::ArrayClass /*arrayClass*/) +{ + /* + this->compiler->bindVertexArray(arrayClass); + const std::size_t vertexCount = this->compiler->vboSize({arrayClass, gl::VboSubclass::VertexData}) / gl::FLOATS_PER_VERTEX; + glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast(vertexCount)); + this->compiler->releaseVertexArray(arrayClass); + this->checkForGLErrors(); + */ +} + +void PartRenderer::checkForGLErrors() +{ + GLenum glError; + QStringList errors; + while ((glError = glGetError()) != GL_NO_ERROR) { const QString glErrorString = QString::fromLatin1(reinterpret_cast(::gluErrorString(glError))); + errors.append(glErrorString); + } + if (not errors.isEmpty()) + { QMessageBox::critical( this, tr("Rendering error"), - QString{"Failed to render: %1"}.arg(glErrorString)); + QString{"Failed to render.\n%1"}.arg(errors.join("\n"))); } - glDisable(GL_CULL_FACE); } -static QPointF pointToPointF(const QPoint& point) -{ - return {static_cast(point.x()), static_cast(point.y())}; -} - -/* -static QPoint pointFToPoint(const QPointF& point) -{ - return {static_cast(std::round(point.x())), static_cast(std::round(point.y()))}; -} -*/ - void PartRenderer::mouseMoveEvent(QMouseEvent* event) { const bool left = event->buttons() & Qt::LeftButton; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/gl/partrenderer.h --- a/src/gl/partrenderer.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/gl/partrenderer.h Wed Jan 22 00:23:29 2020 +0200 @@ -2,6 +2,10 @@ #include #include #include +#include +#include +#include +#include #include "main.h" #include "gl/common.h" #include "gl/compiler.h" @@ -10,7 +14,8 @@ { Q_OBJECT public: - PartRenderer(Model* model, DocumentManager* documents, QWidget* parent = nullptr); + PartRenderer(Model* model, DocumentManager* documents, const ColorTable& colorTable, QWidget* parent = nullptr); + ~PartRenderer() override; protected: void initializeGL() override; void resizeGL(int width, int height) override; @@ -23,10 +28,19 @@ void renderScene(); Model* const model; DocumentManager* const documents; + const ColorTable& colorTable; QPointF lastMousePosition; QQuaternion rotation; gl::Compiler* compiler; gl::RenderStyle renderStyle = gl::RenderStyle::Normal; + struct + { + QOpenGLShaderProgram* program = nullptr; + QOpenGLBuffer buffer{QOpenGLBuffer::VertexBuffer}; + QOpenGLVertexArrayObject vertexArray; + } objects; bool initialized = false; void initializeLighting(); + void renderVao(const gl::ArrayClass arrayClass); + void checkForGLErrors(); }; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/libraries.cpp --- a/src/libraries.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/libraries.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -216,6 +216,26 @@ } /** + * @brief Iterates over libraries and loads LDConfig.ldr from each of them. + * @param errors Where to stream any encountered errors + * @return color table + */ +ColorTable LibraryManager::loadColorTable(QTextStream& errors) +{ + ColorTable result; + for (const Library& library : this->libraries) + { + const QString path = library.path.filePath("LDConfig.ldr"); + QFile file{path}; + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + (void) result.load(file, errors); + } + } + return result; +} + +/** * @brief Gets a human-readable string for the specified role * @param role Role to get a string for * @returns string diff -r 6de5ac1fb471 -r 3a9e761e4faa src/libraries.h --- a/src/libraries.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/libraries.h Wed Jan 22 00:23:29 2020 +0200 @@ -20,6 +20,7 @@ #include #include #include "main.h" +#include "colors.h" class QSettings; @@ -73,6 +74,7 @@ int rowCount(const QModelIndex&) const override; int columnCount(const QModelIndex&) const override; bool isValidIndex(const int libraryIndex) const; + ColorTable loadColorTable(QTextStream& errors); private: enum Column { diff -r 6de5ac1fb471 -r 3a9e761e4faa src/linetypes/object.h --- a/src/linetypes/object.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/linetypes/object.h Wed Jan 22 00:23:29 2020 +0200 @@ -64,6 +64,7 @@ virtual QBrush textRepresentationBackground() const; virtual QFont textRepresentationFont() const; virtual void getPolygons(std::vector& polygons, GetPolygonsContext* context) const; + virtual void invert() {} }; class linetypes::ColoredObject : public Object diff -r 6de5ac1fb471 -r 3a9e761e4faa src/linetypes/quadrilateral.cpp --- a/src/linetypes/quadrilateral.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/linetypes/quadrilateral.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -78,5 +78,12 @@ this->points[2], this->points[3], this->colorIndex, - this->id)); + this->id)); } + +void linetypes::Quadrilateral::invert() +{ + // 0 1 2 3 + // -> 2 1 0 3 + std::swap(this->points[0], this->points[2]); +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/linetypes/quadrilateral.h --- a/src/linetypes/quadrilateral.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/linetypes/quadrilateral.h Wed Jan 22 00:23:29 2020 +0200 @@ -21,6 +21,7 @@ SetPropertyResult setProperty(Property id, const QVariant& value) override; QString textRepresentation() const override; void getPolygons(std::vector& polygons, GetPolygonsContext* context) const override; + void invert() override; private: Point3D points[4] = {{}}; }; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/linetypes/subfilereference.cpp --- a/src/linetypes/subfilereference.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/linetypes/subfilereference.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -1,5 +1,6 @@ #include "subfilereference.h" #include "documentmanager.h" +#include "invert.h" linetypes::SubfileReference::SubfileReference(const Matrix4x4& transformation, const QString& referenceName, @@ -53,6 +54,7 @@ Model* model = this->resolve(context->documents); if (model != nullptr) { + const bool needInverting = math::det(this->transformation) < 0; const std::vector modelPolygons = model->getPolygons(context->documents); polygons.reserve(polygons.size() + modelPolygons.size()); for (gl::Polygon polygon : modelPolygons) @@ -61,6 +63,10 @@ { polygon.vertices[i] = math::transform(polygon.vertices[i], this->transformation); } + if (needInverting != this->isInverted) + { + gl::invert(polygon); + } if (polygon.color == colors::main) { polygon.color = this->colorIndex; @@ -76,6 +82,11 @@ return {this->transformation(0, 3), this->transformation(1, 3), this->transformation(2, 3)}; } +void linetypes::SubfileReference::invert() +{ + this->isInverted = not this->isInverted; +} + Model* linetypes::SubfileReference::resolve(DocumentManager* documents) const { return documents->findModelByName(this->referenceName); diff -r 6de5ac1fb471 -r 3a9e761e4faa src/linetypes/subfilereference.h --- a/src/linetypes/subfilereference.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/linetypes/subfilereference.h Wed Jan 22 00:23:29 2020 +0200 @@ -22,8 +22,10 @@ QString textRepresentation() const override; void getPolygons(std::vector& polygons, GetPolygonsContext* context) const override; Point3D position() const; + void invert() override; private: Model* resolve(DocumentManager* documents) const; Matrix4x4 transformation; QString referenceName; + bool isInverted = false; }; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/linetypes/triangle.cpp --- a/src/linetypes/triangle.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/linetypes/triangle.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -68,5 +68,12 @@ this->points[1], this->points[2], this->colorIndex, - this->id)); + this->id)); } + +void linetypes::Triangle::invert() +{ + // 0 1 2 + // -> 1 0 2 + std::swap(this->points[0], this->points[1]); +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/linetypes/triangle.h --- a/src/linetypes/triangle.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/linetypes/triangle.h Wed Jan 22 00:23:29 2020 +0200 @@ -19,6 +19,7 @@ SetPropertyResult setProperty(Property id, const QVariant& value) override; QString textRepresentation() const override; void getPolygons(std::vector& polygons, GetPolygonsContext* context) const override; + void invert() override; private: Point3D points[3] = {{}}; }; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/main.h --- a/src/main.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/main.h Wed Jan 22 00:23:29 2020 +0200 @@ -37,6 +37,10 @@ struct Id { unsigned int value; + constexpr bool operator<(linetypes::Id other) const + { + return this->value < other.value; + } }; } @@ -44,3 +48,30 @@ { return static_cast(x); } + +inline QString operator""_q(const char* string, const unsigned long int length) +{ + Q_UNUSED(length) + return QString{string}; +} + +inline QPointF pointToPointF(const QPoint& point) +{ + return {static_cast(point.x()), static_cast(point.y())}; +} + +inline QPoint pointFToPoint(const QPointF& point) +{ + return {static_cast(std::round(point.x())), static_cast(std::round(point.y()))}; +} + +/** + * \brief Hints to the specified vector that a certain amount of new elements are going to be added. + * \param vector vector to consider + * \param amount amount of new elements to expect + */ +template +void reserveMore(std::vector& vector, std::size_t amount) +{ + vector.reserve(vector.size() + amount); +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/mainwindow.cpp --- a/src/mainwindow.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/mainwindow.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -135,7 +135,7 @@ void MainWindow::openModelForEditing(const QString& modelName) { - Document* document = new Document{this->documents.findModelByName(modelName), &this->documents}; + Document* document = new Document{this->documents.findModelByName(modelName), &this->documents, this->colorTable}; this->ui->tabs->addTab(document, modelName); this->ui->tabs->setCurrentWidget(document); document->restoreSplitterState(this->documentSplitterState); @@ -254,6 +254,7 @@ changeLanguage(defaultLocale.toString()); this->libraries.restoreFromSettings(&this->settings); this->updateRecentlyOpenedDocumentsMenu(); + this->loadColors(); } QString MainWindow::pathToTranslation(const QString& localeCode) @@ -261,3 +262,9 @@ QDir dir {":/locale"}; return dir.filePath(localeCode + ".qm"); } + +void MainWindow::loadColors() +{ + QTextStream errors; + this->colorTable = this->libraries.loadColorTable(errors); +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/mainwindow.h --- a/src/mainwindow.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/mainwindow.h Wed Jan 22 00:23:29 2020 +0200 @@ -54,6 +54,7 @@ uiutilities::KeySequenceMap defaultKeyboardShortcuts; static constexpr int maxRecentlyOpenedFiles = 10; QStringList recentlyOpenedFiles; + ColorTable colorTable; void updateTitle(); void saveSettings(); void restoreSettings(); @@ -61,4 +62,5 @@ void addRecentlyOpenedFile(const QString& path); void openModelForEditing(const QString& modelName); static QString pathToTranslation(const QString& localeCode); + void loadColors(); }; diff -r 6de5ac1fb471 -r 3a9e761e4faa src/maths.h --- a/src/maths.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/maths.h Wed Jan 22 00:23:29 2020 +0200 @@ -71,4 +71,22 @@ } constexpr double infinity = std::numeric_limits::infinity(); constexpr long double pi = M_PIl; + + /* + * Returns the empty sum. (recursion base) + */ + template + constexpr T sum() + { + return {}; + } + + /* + * Returns the sum of n arguments. + */ + template + constexpr auto sum(const T& arg, Rest&&... rest) + { + return arg + sum(rest...); + } } diff -r 6de5ac1fb471 -r 3a9e761e4faa src/matrix.cpp --- a/src/matrix.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/matrix.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -28,3 +28,54 @@ {0, 0, 0, 1} }}; } + +/* + * Computes the determinant of a 3×3 matrix with each variable passed in row-major order. + */ +qreal math::det(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f, qreal g, qreal h, qreal i) +{ + return a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g; +} + +/* + * Computes the determinant of a 2×2 matrix. + */ +qreal math::det(const Matrix<2, 2>& matrix) +{ + return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0); +} + +/* + * Computes the determinant of a 3×3 matrix. + */ +qreal math::det(const Matrix3x3& matrix) +{ + return math::sum( + +matrix(0, 0) * matrix(1, 1) * matrix(2, 2), + -matrix(0, 0) * matrix(1, 2) * matrix(2, 1), + -matrix(0, 1) * matrix(1, 0) * matrix(2, 2), + +matrix(0, 1) * matrix(1, 2) * matrix(2, 0), + +matrix(0, 2) * matrix(1, 0) * matrix(2, 1), + -matrix(0, 2) * matrix(1, 1) * matrix(2, 0)); +} + +/* + * Computes the determinant of a 4×4 matrix. + */ +qreal math::det(const Matrix4x4& matrix) +{ + qreal sum = 0; + + for (int column : {0, 1, 2, 3}) + { + int column_1 = (column >= 1) ? 0 : 1; + int column_2 = (column >= 2) ? 1 : 2; + int column_3 = (column >= 3) ? 2 : 3; + sum += ((column % 1) ? -1 : 1) * math::det( + matrix(1, column_1), matrix(1, column_2), matrix(1, column_3), + matrix(2, column_1), matrix(2, column_2), matrix(2, column_3), + matrix(3, column_1), matrix(3, column_2), matrix(3, column_3)); + } + + return sum; +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/matrix.h --- a/src/matrix.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/matrix.h Wed Jan 22 00:23:29 2020 +0200 @@ -168,3 +168,11 @@ static constexpr Matrix3x3 identity3x3 {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}; static constexpr Matrix4x4 identity4x4 {{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}}; + +namespace math +{ + qreal det(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f, qreal g, qreal h, qreal i); + qreal det(const Matrix<2, 2>& matrix); + qreal det(const Matrix3x3& matrix); + qreal det(const Matrix4x4& matrix); +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/model.h --- a/src/model.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/model.h Wed Jan 22 00:23:29 2020 +0200 @@ -50,10 +50,10 @@ private: using ModelObjectPointer = std::unique_ptr; template - T* append(Args&&... args); + linetypes::Id append(Args&&... args); void append(ModelObjectPointer&& object); template - T* insert(int position, Args&&... args); + linetypes::Id insert(int position, Args&&... args); bool modified = false; QString path; LDHeader header; @@ -64,25 +64,25 @@ }; template -T* Model::append(Args&&... args) +linetypes::Id Model::append(Args&&... args) { emit layoutAboutToBeChanged(); this->body.push_back(std::make_unique(args...)); - T* pointer = static_cast(this->body.back().get()); + linetypes::Object* pointer = this->body.back().get(); this->objectsById[pointer->id] = pointer; emit objectAdded(pointer->id, this->body.size() - 1); emit layoutChanged(); - return pointer; + return pointer->id; } template -T* Model::insert(int position, Args&&... args) +linetypes::Id Model::insert(int position, Args&&... args) { emit layoutAboutToBeChanged(); this->body.insert(position, std::make_unique(args...)); - T* pointer = static_cast(this->body[position]); + linetypes::Object* pointer = this->body[position].get(); this->objectsById[pointer->id] = pointer; emit objectAdded(pointer->id, position); emit layoutChanged(); - return pointer; + return pointer->id; } diff -r 6de5ac1fb471 -r 3a9e761e4faa src/modeleditcontext.cpp --- a/src/modeleditcontext.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/modeleditcontext.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -23,9 +23,12 @@ { } -void Model::EditContext::append(std::unique_ptr&& object) +linetypes::Id Model::EditContext::append(std::unique_ptr&& object) { + const linetypes::Id id = object->id; + this->model.objectsById[id] = object.get(); this->model.append(std::move(object)); + return id; } void Model::EditContext::setObjectProperty( @@ -35,3 +38,13 @@ { object->setProperty(property, value); } + +void Model::EditContext::invertObject(linetypes::Id id) +{ + auto it = this->model.objectsById.find(id); + if (it != this->model.objectsById.end()) + { + linetypes::Object* object = it->second; + object->invert(); + } +} diff -r 6de5ac1fb471 -r 3a9e761e4faa src/modeleditcontext.h --- a/src/modeleditcontext.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/modeleditcontext.h Wed Jan 22 00:23:29 2020 +0200 @@ -24,14 +24,15 @@ { public: template - T* append(Args&&... args); - void append(std::unique_ptr&& object); + linetypes::Id append(Args&&... args); + linetypes::Id append(std::unique_ptr&& object); template - T* insert(int position, Args&&... args); + linetypes::Id insert(int position, Args&&... args); void setObjectProperty( linetypes::Object* object, linetypes::Property property, const QVariant &value); + void invertObject(linetypes::Id id); private: EditContext(Model& model); friend class Model; @@ -39,13 +40,13 @@ }; template -T* Model::EditContext::append(Args&&... args) +linetypes::Id Model::EditContext::append(Args&&... args) { return this->model.append(args...); } template -T* Model::EditContext::insert(int position, Args&&... args) +linetypes::Id Model::EditContext::insert(int position, Args&&... args) { return this->model.insert(position, args...); } diff -r 6de5ac1fb471 -r 3a9e761e4faa src/parser.cpp --- a/src/parser.cpp Sun Jan 19 14:25:57 2020 +0200 +++ b/src/parser.cpp Wed Jan 22 00:23:29 2020 +0200 @@ -256,11 +256,11 @@ continue; } std::unique_ptr object = parseFromString(line); + auto id = editor.append(std::move(object)); if (invertNext) { - editor.setObjectProperty(object.get(), linetypes::Property::IsInverted, true); + editor.invertObject(id); } - editor.append(std::move(object)); invertNext = false; } } diff -r 6de5ac1fb471 -r 3a9e761e4faa src/ring.h --- a/src/ring.h Sun Jan 19 14:25:57 2020 +0200 +++ b/src/ring.h Wed Jan 22 00:23:29 2020 +0200 @@ -22,15 +22,15 @@ { namespace _imp { - template + template class RingAdapter; } template - _imp::RingAdapter ring(T& collection); + _imp::RingAdapter ring(T&& collection); - template - _imp::RingAdapter ring(T& collection, int count); + template + _imp::RingAdapter ring(T&& collection, Tint count); } /* @@ -44,21 +44,20 @@ * ring(A)[5] == A[5 % 4] == A[1] * ring(A)[-1] == ring(A)[-1 + 4] == A[3] */ -template +template class iter::_imp::RingAdapter { private: // The private section must come first because _collection is used in decltype() below. - T& collection; - const int count; + T&& collection; + const Tint count; public: - RingAdapter(T& collection, int count) : + RingAdapter(T&& collection, Tint count) : collection {collection}, count {count} {} - template - decltype(collection[IndexType()]) operator[](IndexType index) + decltype(collection[Tint{}]) operator[](Tint index) { if (count == 0) { @@ -77,7 +76,7 @@ } } - int size() const + Tint size() const { return this->count; } @@ -88,7 +87,7 @@ * to be the amount of elements in the collection. */ template -iter::_imp::RingAdapter iter::ring(T& collection) +iter::_imp::RingAdapter iter::ring(T&& collection) { return {collection, countof(collection)}; } @@ -96,14 +95,14 @@ /* * Version of ring() that allows manual specification of the count. */ -template -iter::_imp::RingAdapter iter::ring(T& collection, int count) +template +iter::_imp::RingAdapter iter::ring(T&& collection, Tint count) { return {collection, count}; } -template -int countof(const iter::_imp::RingAdapter& ring) +template +int countof(const iter::_imp::RingAdapter& ring) { return ring.size(); }