Fri, 07 Feb 2020 23:59:06 +0200
selection works now
src/basics.h | file | annotate | diff | comparison | revisions | |
src/document.cpp | file | annotate | diff | comparison | revisions | |
src/document.ui | file | annotate | diff | comparison | revisions | |
src/gl/compiler.cpp | file | annotate | diff | comparison | revisions | |
src/gl/compiler.h | file | annotate | diff | comparison | revisions | |
src/gl/partrenderer.cpp | file | annotate | diff | comparison | revisions | |
src/gl/partrenderer.h | file | annotate | diff | comparison | revisions | |
src/main.h | file | annotate | diff | comparison | revisions | |
src/model.cpp | file | annotate | diff | comparison | revisions | |
src/model.h | file | annotate | diff | comparison | revisions | |
src/ui/canvas.cpp | file | annotate | diff | comparison | revisions | |
src/ui/canvas.h | file | annotate | diff | comparison | revisions |
--- a/src/basics.h Fri Feb 07 02:02:16 2020 +0200 +++ b/src/basics.h Fri Feb 07 23:59:06 2020 +0200 @@ -85,5 +85,25 @@ return N; } +/** + * @brief casts @c x to a suitable unsigned integer + */ +template<typename T> +constexpr auto unsigned_cast(T x) + -> std::enable_if_t<std::is_integral_v<T>, std::make_unsigned_t<T>> +{ + return static_cast<std::make_unsigned_t<T>>(x); +} + +/** + * @brief casts @c x to a suitable signed integer + */ +template<typename T> +constexpr auto signed_cast(T x) + -> std::enable_if_t<std::is_integral_v<T>, std::make_signed_t<T>> +{ + return static_cast<std::make_signed_t<T>>(x); +} + Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::mat4)
--- a/src/document.cpp Fri Feb 07 02:02:16 2020 +0200 +++ b/src/document.cpp Fri Feb 07 23:59:06 2020 +0200 @@ -41,6 +41,35 @@ this->setMouseTracking(true); connect(this->ui.splitter, &QSplitter::splitterMoved, this, &Document::splitterChanged); connect(this->renderer, &Canvas::newStatusText, this, &Document::newStatusText); + connect(this->renderer, &Canvas::selectionChanged, [&](const QSet<ldraw::Id>& newSelection) + { + QItemSelectionModel* selectionModel = this->ui.listView->selectionModel(); + QItemSelection selection; + for (ldraw::Id id : newSelection) + { + QModelIndex index = this->model->lookup(id); + if (index != QModelIndex{}) + { + selection.select(index, index); + } + } + selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); + }); + connect(this->ui.listView->selectionModel(), &QItemSelectionModel::selectionChanged, + [&](const QItemSelection& selected, const QItemSelection& deselected) + { + QSet<ldraw::Id> selectedIds; + QSet<ldraw::Id> deselectedIds; + for (const QModelIndex& index : selected.indexes()) + { + selectedIds.insert(this->model->resolve(index)); + } + for (const QModelIndex& index : deselected.indexes()) + { + deselectedIds.insert(this->model->resolve(index)); + } + this->renderer->handleSelectionChange(selectedIds, deselectedIds); + }); } Document::~Document()
--- a/src/document.ui Fri Feb 07 02:02:16 2020 +0200 +++ b/src/document.ui Fri Feb 07 23:59:06 2020 +0200 @@ -19,7 +19,17 @@ <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <widget class="QListView" name="listView"/> + <widget class="QListView" name="listView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + </widget> <widget class="QFrame" name="viewportFrame"> <property name="frameShape"> <enum>QFrame::StyledPanel</enum>
--- a/src/gl/compiler.cpp Fri Feb 07 02:02:16 2020 +0200 +++ b/src/gl/compiler.cpp Fri Feb 07 23:59:06 2020 +0200 @@ -32,6 +32,7 @@ layout(location=1) in vec4 color; layout(location=2) in vec3 normal; layout(location=3) in int id; +layout(location=4) in int selected; out vec4 vColor; out vec3 vFragPos; out vec3 vNormal; @@ -62,6 +63,10 @@ int b = id % 0x100; vColor = vec4(r / 255.0, g / 255.0, b / 255.0, 1.0); } + else if (selected == 1) + { + vColor = vec4(selectedColor, 1.0); + } else { if (fragmentStyle == FRAGSTYLE_BfcGreen) @@ -158,7 +163,7 @@ object.buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); object.vertexArray.create(); object.vertexArray.bind(); - for (int k : {0, 1, 2, 3}) + for (int k : {0, 1, 2, 3, 4}) { object.program->enableAttributeArray(k); } @@ -167,6 +172,7 @@ object.program->setAttributeBuffer(1, GL_FLOAT, offsetof(gl::Vertex, color), 4, stride); object.program->setAttributeBuffer(2, GL_FLOAT, offsetof(gl::Vertex, normal), 3, stride); glVertexAttribIPointer(3, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(gl::Vertex, id))); + glVertexAttribIPointer(4, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(gl::Vertex, selected))); object.vertexArray.release(); object.buffer.release(); object.program->release(); @@ -189,6 +195,7 @@ auto& buffer = this->glObjects[arrayId].buffer; auto& vector = vboData[arrayId]; this->storedVertexCounts[arrayId] = vector.size(); + this->glObjects[arrayId].cachedData = vector; // todo: get rid of this copy buffer.bind(); buffer.allocate(vector.data(), static_cast<int>(vector.size() * sizeof vector[0])); buffer.release(); @@ -266,9 +273,9 @@ return boxCenter(this->boundingBox); } -float gl::Compiler::modelDistance() const +double gl::Compiler::modelDistance() const { - return longestMeasure(this->boundingBox); + return static_cast<double>(longestMeasure(this->boundingBox)); } void gl::Compiler::bindVertexArray(gl::ArrayClass arrayClass) @@ -285,6 +292,21 @@ object.vertexArray.release(); } +void gl::Compiler::setSelectedObjects(const QSet<ldraw::Id> ids) +{ + for (int i = 0; i < gl::NUM_ARRAY_CLASSES; i += 1) + { + auto& vector = this->glObjects[i].cachedData; + for (gl::Vertex& vertex : vector) + { + vertex.selected = (ids.contains({vertex.id})) ? 1 : 0; + } + const GLsizeiptr size = static_cast<int>(vector.size() * sizeof vector[0]); + this->glObjects[i].buffer.bind(); + glBufferSubData(GL_ARRAY_BUFFER, 0, size, vector.data()); + } +} + std::size_t gl::Compiler::vertexCount(const gl::ArrayClass arrayClass) const { return this->storedVertexCounts[static_cast<int>(arrayClass)];
--- a/src/gl/compiler.h Fri Feb 07 02:02:16 2020 +0200 +++ b/src/gl/compiler.h Fri Feb 07 23:59:06 2020 +0200 @@ -41,6 +41,7 @@ glm::vec4 color; glm::vec3 normal; glm::int32 id; + glm::int32 selected = 0; }; } @@ -55,11 +56,12 @@ std::size_t vertexCount(gl::ArrayClass arrayClass) const; QColor getColorForPolygon(const gl::Polygon& polygon, const RenderPreferences& preferences); glm::vec3 modelCenter() const; - float modelDistance() const; + double modelDistance() const; void initialize(); void bindVertexArray(gl::ArrayClass arrayClass); void releaseVertexArray(gl::ArrayClass arrayClass); void buildShaders(int arrayId); + void setSelectedObjects(const QSet<ldraw::Id> ids); static ldraw::Id idFromColor(const std::array<GLbyte, 3>& data); @@ -95,6 +97,7 @@ QOpenGLShaderProgram* pickSceneProgram = nullptr; QOpenGLBuffer buffer{QOpenGLBuffer::VertexBuffer}; QOpenGLVertexArrayObject vertexArray; + std::vector<gl::Vertex> cachedData; } glObjects[gl::NUM_ARRAY_CLASSES]; };
--- a/src/gl/partrenderer.cpp Fri Feb 07 02:02:16 2020 +0200 +++ b/src/gl/partrenderer.cpp Fri Feb 07 23:59:06 2020 +0200 @@ -115,15 +115,16 @@ static_cast<float>(backgroundColor.greenF()), static_cast<float>(backgroundColor.blueF()), 1.0f); - this->compiler->setUniform("useLighting", true); + this->compiler->setUniform("useLighting", GL_TRUE); } else { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - this->compiler->setUniform("useLighting", false); + this->compiler->setUniform("useLighting", GL_FALSE); } this->compiler->setUniform("selectedColor", vec3FromQColor(this->renderPreferences.selectedColor)); this->compiler->setUniform("highlighted", this->highlighted.value); + this->checkForGLErrors(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_POLYGON_OFFSET_FILL); @@ -187,7 +188,9 @@ { this->compiler->bindVertexArray(arrayClass); const std::size_t vertexCount = this->compiler->vertexCount(arrayClass); + this->checkForGLErrors(); glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast<GLsizei>(vertexCount)); + this->checkForGLErrors(); this->compiler->releaseVertexArray(arrayClass); this->checkForGLErrors(); } @@ -250,7 +253,9 @@ this->makeCurrent(); this->renderScene(); std::array<GLbyte, 3> data; + this->checkForGLErrors(); glReadPixels(where.x(), this->height() - where.y(), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &data[0]); + this->checkForGLErrors(); this->renderPreferences.style = oldRenderStyle; this->update(); return gl::Compiler::idFromColor(data); @@ -280,9 +285,3 @@ } this->update(); } - -void PartRenderer::setHighlight(ldraw::Id highlightedId) -{ - this->highlighted = highlightedId; -} -
--- a/src/gl/partrenderer.h Fri Feb 07 02:02:16 2020 +0200 +++ b/src/gl/partrenderer.h Fri Feb 07 23:59:06 2020 +0200 @@ -22,7 +22,6 @@ QWidget* parent = nullptr); ~PartRenderer() override; void setRenderPreferences(const gl::RenderPreferences& newPreferences); - void setHighlight(ldraw::Id highlightedId); protected: ldraw::Id pick(const QPoint& where); void initializeGL() override; @@ -30,21 +29,21 @@ void paintGL() override; void mouseMoveEvent(QMouseEvent* event) override; void wheelEvent(QWheelEvent* event) override; + Model* const model; + DocumentManager* const documents; + const ldraw::ColorTable& colorTable; + gl::Compiler* const compiler; + ldraw::Id highlighted = ldraw::NULL_ID; private: void setFragmentStyle(gl::FragmentStyle fragStyle); void renderAllArrays(); void renderScene(); void updateViewMatrix(); - Model* const model; - DocumentManager* const documents; - const ldraw::ColorTable& colorTable; QPointF lastMousePosition; - gl::Compiler* compiler; gl::RenderPreferences renderPreferences; glm::mat4 projectionMatrix; glm::mat4 viewMatrix; glm::quat modelQuaternion; - ldraw::Id highlighted = ldraw::NULL_ID; static constexpr double MIN_ZOOM = 0.0; static constexpr double MAX_ZOOM = 3.0; double zoom = 1.0;
--- a/src/main.h Fri Feb 07 02:02:16 2020 +0200 +++ b/src/main.h Fri Feb 07 23:59:06 2020 +0200 @@ -41,6 +41,14 @@ { return this->value < other.value; } + friend constexpr unsigned int qHash(ldraw::Id id) + { + return qHash(id.value); + } + friend bool operator==(ldraw::Id one, ldraw::Id other) + { + return one.value == other.value; + } }; using id_t = Id; constexpr id_t NULL_ID = id_t{0};
--- a/src/model.cpp Fri Feb 07 02:02:16 2020 +0200 +++ b/src/model.cpp Fri Feb 07 23:59:06 2020 +0200 @@ -26,7 +26,7 @@ int Model::size() const { - return this->body.size(); + return static_cast<int>(this->body.size()); } Model::EditContext Model::edit() @@ -41,8 +41,7 @@ QVariant Model::data(const QModelIndex& index, int role) const { - const int row = index.row(); - ldraw::Object* object = this->body[row].get(); + const ldraw::Object* object = this->objectAt(index); switch(role) { case Qt::DisplayRole: @@ -64,14 +63,13 @@ { case HeaderProperty::Name: return header.name; - default: - return {}; } + return {}; } QVariant Model::getObjectProperty(const int index, const ldraw::Property property) const { - const ldraw::Object* object = this->body[index].get(); + const ldraw::Object* object = this->body[unsigned_cast(index)].get(); return object->getProperty(property); } @@ -90,12 +88,30 @@ return this->cachedPolygons; } +QModelIndex Model::lookup(ldraw::Id id) const +{ + // FIXME: This linear search will probably cause performance issues + for (std::size_t i = 0; i < this->body.size(); i += 1) + { + if (this->body[i]->id == id) + { + return this->index(static_cast<int>(i)); + } + } + return {}; +} + +ldraw::Id Model::resolve(const QModelIndex& index) const +{ + return this->objectAt(index)->id; +} + void Model::getObjectPolygons( const int index, std::vector<gl::Polygon>& polygons_out, ldraw::GetPolygonsContext* context) const { - const ldraw::Object* object = this->body[index].get(); + const ldraw::Object* object = this->body[unsigned_cast(index)].get(); object->getPolygons(polygons_out, context); } @@ -103,3 +119,13 @@ { this->body.push_back(std::move(object)); } + +ldraw::Object* Model::objectAt(const QModelIndex& index) +{ + return this->body[unsigned_cast(index.row())].get(); +} + +const ldraw::Object* Model::objectAt(const QModelIndex& index) const +{ + return this->body[unsigned_cast(index.row())].get(); +}
--- a/src/model.h Fri Feb 07 02:02:16 2020 +0200 +++ b/src/model.h Fri Feb 07 23:59:06 2020 +0200 @@ -44,7 +44,8 @@ const QString& getName() const; QVariant getObjectProperty(const int index, const ldraw::Property property) const; std::vector<gl::Polygon> getPolygons(class DocumentManager* documents) const; - void getObjectPolygons(const int index, std::vector<gl::Polygon>& polygons_out, ldraw::GetPolygonsContext* context) const; + QModelIndex lookup(ldraw::Id id) const; + ldraw::Id resolve(const QModelIndex& index) const; signals: void objectAdded(ldraw::Id id, int position); private: @@ -54,6 +55,12 @@ void append(ModelObjectPointer&& object); template<typename T, typename... Args> ldraw::Id insert(int position, Args&&... args); + ldraw::Object* objectAt(const QModelIndex& index); + const ldraw::Object* objectAt(const QModelIndex& index) const; + void getObjectPolygons( + const int index, + std::vector<gl::Polygon>& polygons_out, + ldraw::GetPolygonsContext* context) const; bool modified = false; QString path; LDHeader header;
--- a/src/ui/canvas.cpp Fri Feb 07 02:02:16 2020 +0200 +++ b/src/ui/canvas.cpp Fri Feb 07 23:59:06 2020 +0200 @@ -11,10 +11,39 @@ this->setMouseTracking(true); } +void Canvas::handleSelectionChange(const QSet<ldraw::Id>& selectedIds, const QSet<ldraw::Id>& deselectedIds) +{ + this->selection.subtract(deselectedIds); + this->selection.unite(selectedIds); + this->compiler->setSelectedObjects(this->selection); + this->update(); +} + void Canvas::mouseMoveEvent(QMouseEvent* event) { const ldraw::Id id = this->pick(event->pos()); this->newStatusText("Selected: %1"_q.arg(id.value)); - this->setHighlight(id); + this->highlighted = id; + this->totalMouseMove += (event->pos() - this->lastMousePosition).manhattanLength(); + this->lastMousePosition = event->pos(); PartRenderer::mouseMoveEvent(event); } + +void Canvas::mousePressEvent(QMouseEvent* event) +{ + this->totalMouseMove = 0; + this->lastMousePosition = event->pos(); + PartRenderer::mousePressEvent(event); +} + +void Canvas::mouseReleaseEvent(QMouseEvent* event) +{ + if (this->totalMouseMove < (2.0 / sqrt(2)) * 5.0) + { + this->selection = {this->highlighted}; + this->compiler->setSelectedObjects(this->selection); + emit selectionChanged(this->selection); + this->update(); + } + PartRenderer::mouseReleaseEvent(event); +}
--- a/src/ui/canvas.h Fri Feb 07 02:02:16 2020 +0200 +++ b/src/ui/canvas.h Fri Feb 07 23:59:06 2020 +0200 @@ -10,8 +10,17 @@ DocumentManager* documents, const ldraw::ColorTable& colorTable, QWidget* parent = nullptr); +public slots: + void handleSelectionChange(const QSet<ldraw::Id>& selectedIds, const QSet<ldraw::Id>& deselectedIds); protected: void mouseMoveEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; signals: void newStatusText(const QString& newStatusText); + void selectionChanged(const QSet<ldraw::Id>& newSelection); +private: + QPoint lastMousePosition; + int totalMouseMove = 0; + QSet<ldraw::Id> selection; };