diff --git a/ACG/GL/DrawMesh.hh b/ACG/GL/DrawMesh.hh index 43f6a0000bbd9b67eb161ccae3dfee2962015175..8176feabd9c2e0a8d03b782307ef5c6ff7336a77 100644 --- a/ACG/GL/DrawMesh.hh +++ b/ACG/GL/DrawMesh.hh @@ -293,6 +293,21 @@ public: */ void updateFull() {rebuild_ |= REBUILD_FULL;} + /** \brief enable fast update for vertex-only changes + * + * Updates after changing halfedge attributes might introduce new vertex splits. + * In that case the fast update mode adds these splits to the end of the vbo. + * This might introduce more splits than necessary in favor for speed. + * + */ + void enableFastVertexUpdate() { enableFastVertexUpdate_ = true; } + + /** \brief disable fast update for vertex-only changes + * + * Do a full rebuild if per halfedge attributes are changed. + */ + void disableFastVertexUpdate() { enableFastVertexUpdate_ = false; } + /** \brief returns the number of used textured of this mesh * * @return Number of different textures used in the mesh @@ -828,6 +843,9 @@ private: /// hint on what to rebuild unsigned int rebuild_; + /// fast updates are possible at the cost of potentially more splits + bool enableFastVertexUpdate_; + /** used to track mesh changes, that require a full rebuild * values directly taken from Mesh template */ diff --git a/ACG/GL/DrawMeshT.cc b/ACG/GL/DrawMeshT.cc index 11a50374de742dfcad0de5bc7dcf964bcf01ac37..4b8d459bddd6d78d710469572038e5914d1cfc5a 100644 --- a/ACG/GL/DrawMeshT.cc +++ b/ACG/GL/DrawMeshT.cc @@ -81,6 +81,7 @@ template <class Mesh> DrawMeshT<Mesh>::DrawMeshT(Mesh& _mesh) : mesh_(_mesh), rebuild_(REBUILD_NONE), + enableFastVertexUpdate_(false), prevNumFaces_(0), prevNumVerts_(0), colorMode_(1), curVBOColorMode_(1), @@ -542,9 +543,14 @@ DrawMeshT<Mesh>::rebuild() } - // full rebuild: - delete meshComp_; - meshComp_ = new MeshCompiler(*vertexDecl_); + bool doFastUpdate = !(rebuild_ & (REBUILD_TOPOLOGY | REBUILD_FULL)) && enableFastVertexUpdate_ && meshComp_ != 0; + + if (!doFastUpdate) + { + // full rebuild: + delete meshComp_; + meshComp_ = new MeshCompiler(*vertexDecl_); + } // search for convenient attribute indices @@ -640,8 +646,17 @@ DrawMeshT<Mesh>::rebuild() } - // compile draw buffers - meshComp_->build(true, true, true, true); + + if (doFastUpdate) + { + // just update vertices + meshComp_->fastVertexUpdate(); + } + else + { + // compile draw buffers + meshComp_->build(true, true, true, true); + } // create inverse vertex map diff --git a/ACG/GL/MeshCompiler.cc b/ACG/GL/MeshCompiler.cc index cebb60a297ca69e20ff21535f9455af1251788cb..0db4aef2e979137765303d9b677db606aafad4dc 100644 --- a/ACG/GL/MeshCompiler.cc +++ b/ACG/GL/MeshCompiler.cc @@ -1539,7 +1539,7 @@ bool MeshCompiler::reconstructTriangulation(int _face, std::vector<int>& _triang int fsize = getFaceSize(_face); int ftris = fsize - 2; - if (int(_triangulation.size() < ftris) * 3) + if (int(_triangulation.size()) < ftris * 3) _triangulation.resize(ftris * 3); if (fsize == 3) @@ -1930,24 +1930,23 @@ void MeshCompiler::build(bool _weldVertices, bool _optimizeVCache, bool _needPer void MeshCompiler::fastVertexUpdate() { /* + basic idea: + + Check if the current index mapping for each (face, corner) has conflicting vertex data. + Resolve conflicts by adding new split vertices at the end of the vbo. - 1. alloc vertex array that stores which (face, corner) is using it. init to all 0 (unused) - 2. for each face and each corner - vertexID = mapToDrawVertex(face, corner); - - if (unused[vertexID]) - writeVertexData(vertexID, face, corner) - else - - char oldVertexData[] = getVertexDataFromVBO(vertexID) - char newVertexData[] = getInputVertexData(face, corner) - - if (!compareVertex(oldVertexData, newVertexData)) - add new vertex // might be optimized to reduce split count. - // use VertexSplitter to check if there is no previous split with the same vertex data - update mappings + The mapping given by mapToOriginalVertexID is not changed for all old vertices. + Those (face, corner) pairs that make use of the vertex but aren't returned by mapToOriginalVertexID, + will have new vertex ids if there is a conflict. + + Conflicts are found by comparing full vertex data. */ + // potential optimizations: + // - let user provide vbo data instead of querying it with getInputFaceVertexData (bottleneck) + // - skip comparison of position elements for each vertex + + // memory storage for comparing two vertices int vstride = getVertexDeclaration()->getVertexStride(); std::vector<char> vdata0(vstride, 0); @@ -1958,9 +1957,6 @@ void MeshCompiler::fastVertexUpdate() int numSplits = 0; - // alloc buffer to store the triangulation of a polygon - std::vector<int> triangulation((maxFaceSize_ - 2) * 3); - // keep references from vertexID -> (face, corner) as they are in mapToOriginalVertexID at the moment for (int faceID = 0; faceID < numFaces_; ++faceID) @@ -2005,52 +2001,13 @@ void MeshCompiler::fastVertexUpdate() // - this is done in createVertexMap() later - // update indices_ : - - - // need to know which indices are associated with the (face, corner) - // -> there might be more than one! - // this is no easy task because of the triangulation of n-polys - // might have to precompute this information somehow - // mapToDrawTriID(); - -// if (!triangulationReady) -// { -// if (!reconstructTriangulation(faceID, triangulation)) -// { -// std::cerr << "error: MeshCompiler::partialVertexUpdate() can't reconstruct triangulation for face " << faceID << std::endl; -// -// // todo: error handling and rollback -// } -// else -// triangulationReady = true; -// } -// -// -// // compute mapping corner -> index offsets into ibo -// -// // there is probably a better solution than doing a linear search in the triangulation here -// for (int tri = 0; tri < fsize - 2; ++tri) -// { -// for (int triCorner = 0; triCorner < 3; ++triCorner) -// { -// int triFaceCorner = triangulation[tri * 3 + triCorner]; -// -// if (triFaceCorner == cornerID) -// { -// int drawTriID = mapToDrawTriID(faceID, tri); -// -// // offset into index buffer of the corner of the triangle -// int triFaceCornerOffset = drawTriID * 3 + triCorner; -// -// assert(indices_[triFaceCornerOffset] == vertexID); -// indices_[triFaceCornerOffset] = ; -// } -// } -// } + // update index buffer (indices_), which contains a triangulation of the polygon : - // without reconstructing the triangulation: + // the current (face, corner) vertex might have multiple instances! + // this depends on the triangulation result + // search for these instances without reconstructing the triangulation + int numChangedIndices = 0; for (int tri = 0; tri < fsize - 2; ++tri) { int drawTriID = mapToDrawTriID(faceID, tri); @@ -2064,10 +2021,15 @@ void MeshCompiler::fastVertexUpdate() { // change index to to new split vertex indices_[triIndexOffset] = newVertexID; + ++numChangedIndices; } } } +#ifdef _DEBUG + if (!numChangedIndices) + std::cout << "error: MeshCompiler::fastVertexUpdate - failed to update index buffer with new split id" << std::endl; +#endif } } } diff --git a/ACG/GL/MeshCompiler.hh b/ACG/GL/MeshCompiler.hh index 39129896930fb89ddb6bc485896639d88ab28724..1d5d0da7fccc02c03d42fe854258144452f35a73 100644 --- a/ACG/GL/MeshCompiler.hh +++ b/ACG/GL/MeshCompiler.hh @@ -628,6 +628,16 @@ public: */ int getIndex(int _i) const; + /** Get the triangulation of a face + * + * The triangulation is an array of 3 local corner ids for each triangle of the face. + * This data is not directly available, but has to be reconstructed from the final index buffer. + * This is done by matching the vertex ids with the (face, corner) mapping, so there is some overhead. + * @param _face face ID in input data + * @param _triangulation target buffer receiving the triangulation + * @return report if matching was successful + */ + bool reconstructTriangulation(int _face, std::vector<int>& _triangulation) const; /** @} */ @@ -1027,10 +1037,6 @@ private: // resolve triangulation void resolveTriangulation(); - // reconstruct triangulation of a face from the final index buffer. - // the triangulation is an array of 3 local corner ids for each triangle of the face. - bool reconstructTriangulation(int _face, std::vector<int>& _triangulation) const; - // sort input faces by group ids void sortFacesByGroup(); diff --git a/ObjectTypes/MeshObject/MeshObjectT.cc b/ObjectTypes/MeshObject/MeshObjectT.cc index 1a37cd40c90683f1b8fd7116ddfb26e2f782975c..cc57d8edd9304f1aae8b92780c89ce20c2bcb892 100644 --- a/ObjectTypes/MeshObject/MeshObjectT.cc +++ b/ObjectTypes/MeshObject/MeshObjectT.cc @@ -221,6 +221,15 @@ meshNode_ = new ACG::SceneGraph::MeshNodeT<MeshT>(*mesh_, shaderNode_, "NEW MeshNode"); + if (meshNode_->getDrawMesh()) + { + bool fastUpdate = OpenFlipperSettings().value("Core/Debug/FastVertexUpdate", false).toBool(); + if (fastUpdate) + meshNode_->getDrawMesh()->enableFastVertexUpdate(); + else + meshNode_->getDrawMesh()->disableFastVertexUpdate(); + } + QString shaderDir = OpenFlipper::Options::shaderDirStr() + OpenFlipper::Options::dirSeparator(); std::string shaderDirectory = std::string( shaderDir.toUtf8() ); diff --git a/OpenFlipper/widgets/optionsWidget/optionsWidget.cc b/OpenFlipper/widgets/optionsWidget/optionsWidget.cc index a1aa5da01580a7a30a9ef733804617173e05d5d8..ccde828632a839ad74452ebad4f1fb14468a6f40 100644 --- a/OpenFlipper/widgets/optionsWidget/optionsWidget.cc +++ b/OpenFlipper/widgets/optionsWidget/optionsWidget.cc @@ -341,6 +341,8 @@ void OptionsWidget::showEvent ( QShowEvent * /*event*/ ) { // debugging slotDebugging->setChecked(OpenFlipper::Options::doSlotDebugging()); + fastVertexUpdate->setChecked(OpenFlipperSettings().value("Core/Debug/FastVertexUpdate", false).toBool()); + //keyBindings initKeyTree(); @@ -666,6 +668,7 @@ void OptionsWidget::slotApply() { // Debugging OpenFlipper::Options::doSlotDebugging(slotDebugging->isChecked()); + OpenFlipperSettings().setValue("Core/Debug/FastVertexUpdate", fastVertexUpdate->isChecked()); //viewer defaults for (int i=0; i < PluginFunctions::viewers(); i++){ diff --git a/OpenFlipper/widgets/optionsWidget/optionsWidget.ui b/OpenFlipper/widgets/optionsWidget/optionsWidget.ui index b6278e7e4bd2c12ad1d3af0edcf0d71eef9a1988..893790a80a3d9e755c2f78079bbfdf4c2ad383cb 100644 --- a/OpenFlipper/widgets/optionsWidget/optionsWidget.ui +++ b/OpenFlipper/widgets/optionsWidget/optionsWidget.ui @@ -50,7 +50,7 @@ <string/> </property> <property name="currentIndex"> - <number>1</number> + <number>0</number> </property> <property name="usesScrollButtons"> <bool>true</bool> @@ -1744,6 +1744,13 @@ p, li { white-space: pre-wrap; } </item> </layout> </item> + <item> + <widget class="QCheckBox" name="fastVertexUpdate"> + <property name="text"> + <string>Enable Fast Vertex Update (experimental)</string> + </property> + </widget> + </item> <item> <spacer name="verticalSpacer_3"> <property name="orientation">