From c67b4d316067904523353c8d4a22af8119664b35 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 26 Dec 2015 02:52:06 -0500 Subject: [PATCH 1/3] Improvements on the mesh drawing by caching the structure (bug #161). --- MM.h | 3 ++ ShapeGraphicsItem.cpp | 71 ++++++++++++++++++++++++++++++++----------- ShapeGraphicsItem.h | 21 +++++++++++-- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/MM.h b/MM.h index de1faf3..f58601f 100644 --- a/MM.h +++ b/MM.h @@ -74,6 +74,9 @@ public: static const qreal ZOOM_FACTOR; static const qreal ZOOM_MIN; static const qreal ZOOM_MAX; + + // Misc. + static const int MESH_SUBDIVISION_MIN_AREA = 400; }; #endif diff --git a/ShapeGraphicsItem.cpp b/ShapeGraphicsItem.cpp index 4fea58f..44c226d 100644 --- a/ShapeGraphicsItem.cpp +++ b/ShapeGraphicsItem.cpp @@ -309,6 +309,12 @@ PolygonTextureGraphicsItem::PolygonTextureGraphicsItem(Mapping::ptr mapping, boo _controlPainter.reset(new PolygonControlPainter(this)); } +MeshTextureGraphicsItem::MeshTextureGraphicsItem(Mapping::ptr mapping, bool output) : PolygonTextureGraphicsItem(mapping, output) { + _controlPainter.reset(new MeshControlPainter(this)); + _nHorizontalQuads = _nVerticalQuads = -1; +} + + void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter) { Q_UNUSED(painter); @@ -318,25 +324,59 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter) QSharedPointer inputMesh = qSharedPointerCast(_inputShape); QVector > outputQuads = outputMesh->getQuads2d(); QVector > inputQuads = inputMesh->getQuads2d(); + + // Check if we increased or decreased number of columns/rows in mesh. + bool dirty = false; + if (_nHorizontalQuads != outputMesh->nHorizontalQuads() || + _nVerticalQuads != outputMesh->nVerticalQuads()) + { + dirty = true; + _cachedQuadItems.resize(_nHorizontalQuads = outputMesh->nHorizontalQuads()); + for (int i=0; i<_nHorizontalQuads; i++) + _cachedQuadItems[i].resize(_nVerticalQuads = outputMesh->nVerticalQuads()); + } + + // Go through the mesh quad by quad. for (int x = 0; x < outputMesh->nHorizontalQuads(); x++) { for (int y = 0; y < outputMesh->nVerticalQuads(); y++) { - QSizeF size = mapFromScene(outputQuads[x][y].toPolygon()).boundingRect().size(); - float area = size.width() * size.height(); - _drawQuad(*_texture.toStrongRef(), inputQuads[x][y], outputQuads[x][y], area); + Quad& inputQuad = inputQuads[x][y]; + Quad& outputQuad = outputQuads[x][y]; + + // Verify if item needs recomputing. + CacheQuadItem& item = _cachedQuadItems[x][y]; + if (dirty || + item.parent.input.toPolygon() != inputQuad.toPolygon() || + item.parent.output.toPolygon() != outputQuad.toPolygon()) { + + item.parent.input = inputQuad; + item.parent.output = outputQuad; + item.subQuads.clear(); + + QSizeF size = mapFromScene(outputQuad.toPolygon()).boundingRect().size(); + float area = size.width() * size.height(); + + // Rebuild cache quad item. + _buildCacheQuadItem(item, inputQuad, outputQuad, area); + } + + // Draw all the cached items. + foreach (CacheQuadMapping m, item.subQuads) + { + glBegin(GL_QUADS); + for (int i = 0; i < outputQuad.nVertices(); i++) + { + Util::setGlTexPoint(*_texture.toStrongRef(), m.input.getVertex(i), mapFromScene(m.output.getVertex(i))); + } + glEnd(); + } } } } - } -MeshTextureGraphicsItem::MeshTextureGraphicsItem(Mapping::ptr mapping, bool output) : PolygonTextureGraphicsItem(mapping, output) { - _controlPainter.reset(new MeshControlPainter(this)); -} - - -void MeshTextureGraphicsItem::_drawQuad(const Texture& texture, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshod, float outputThreshold) +void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshod, float outputThreshold) { QPointF oa = mapFromScene(outputQuad.getVertex(0)); QPointF ob = mapFromScene(outputQuad.getVertex(1)); @@ -361,18 +401,13 @@ void MeshTextureGraphicsItem::_drawQuad(const Texture& texture, const Quad& inpu float inputV2dotV3 = QPointF::dotProduct(ic-ib, ic-id); // Stopping criterion. - if (outputArea < 200 || + if (outputArea < MM::MESH_SUBDIVISION_MIN_AREA || (fabs(outputV1dotV2 - outputV3dotV4) < outputThreshold && fabs(outputV1dotV4 - outputV2dotV3) < outputThreshold && fabs(inputV1dotV2 - inputV3dotV4) < inputThreshod && fabs(inputV1dotV4 - inputV2dotV3) < inputThreshod)) { - glBegin(GL_QUADS); - for (int i = 0; i < outputQuad.nVertices(); i++) - { - Util::setGlTexPoint(texture, inputQuad.getVertex(i), mapFromScene(outputQuad.getVertex(i))); - } - glEnd(); + item.subQuads.append( { inputQuad, outputQuad } ); } else // subdivide { @@ -380,7 +415,7 @@ void MeshTextureGraphicsItem::_drawQuad(const Texture& texture, const Quad& inpu QList outputSubQuads = _split(outputQuad); for (int i = 0; i < inputSubQuads.size(); i++) { - _drawQuad(texture, inputSubQuads[i], outputSubQuads[i], outputArea*0.25, inputThreshod, outputThreshold); + _buildCacheQuadItem(item, inputSubQuads[i], outputSubQuads[i], outputArea*0.25, inputThreshod, outputThreshold); } } } diff --git a/ShapeGraphicsItem.h b/ShapeGraphicsItem.h index f372d24..0b0ca87 100644 --- a/ShapeGraphicsItem.h +++ b/ShapeGraphicsItem.h @@ -201,9 +201,22 @@ public: }; -/// Graphics item for textured mesh. +/** + * Graphics item for textured mesh. + * The drawing technique recursively subdivides the quad to approximate projective mapping and thus + * avoiding artifacts on the diagonals. Subdivided structure is cached to increase performance. + * Source: Oliveira, M. "Correcting Texture Mapping Errors Introduced by Graphics Hardware" + */ class MeshTextureGraphicsItem : public PolygonTextureGraphicsItem { + struct CacheQuadMapping { + Quad input; + Quad output; + }; + struct CacheQuadItem { + CacheQuadMapping parent; + QList subQuads; + }; public: MeshTextureGraphicsItem(Mapping::ptr mapping, bool output=true); virtual ~MeshTextureGraphicsItem(){} @@ -212,8 +225,12 @@ public: private: // Draws quad recursively using the technique described in Oliveira, M. "Correcting Texture Mapping Errors Introduced by Graphics Hardware" - void _drawQuad(const Texture& texture, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshold = 0.0001f, float outputThreshold = 0.001f); + void _buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshold = 0.0001f, float outputThreshold = 0.001f); QList _split(const Quad& quad); + + QVector > _cachedQuadItems; + int _nHorizontalQuads; + int _nVerticalQuads; }; /// Graphics item for textured mesh. From 02f411bc0e376ecae0195e16888339ececf78bc0 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 26 Dec 2015 19:25:16 -0500 Subject: [PATCH 2/3] Further improvements on mesh drawing performance: resolution is heightened on mouse release while during edit resolution is kept lower (closes #161). --- MapperGLCanvas.h | 3 ++ ShapeGraphicsItem.cpp | 97 +++++++++++++++++++++++++++++-------------- ShapeGraphicsItem.h | 5 ++- 3 files changed, 72 insertions(+), 33 deletions(-) diff --git a/MapperGLCanvas.h b/MapperGLCanvas.h index 9829b3b..0124686 100644 --- a/MapperGLCanvas.h +++ b/MapperGLCanvas.h @@ -83,6 +83,9 @@ public: /// Set the currently active vertex void setActiveVertexIndex(int activeVertex) { _activeVertex = activeVertex; } + bool shapeGrabbed() const { return _shapeGrabbed; } + bool vertexGrabbed() const { return _vertexGrabbed; } + qreal getZoomFactor() const { return qBound(qPow(MM::ZOOM_FACTOR, _zoomLevel), MM::ZOOM_MIN, MM::ZOOM_MAX); } /// This function needs to be called after a shape inside the canvas has been changed for appropriate signals to be activated. diff --git a/ShapeGraphicsItem.cpp b/ShapeGraphicsItem.cpp index 44c226d..5c8bc10 100644 --- a/ShapeGraphicsItem.cpp +++ b/ShapeGraphicsItem.cpp @@ -312,9 +312,9 @@ PolygonTextureGraphicsItem::PolygonTextureGraphicsItem(Mapping::ptr mapping, boo MeshTextureGraphicsItem::MeshTextureGraphicsItem(Mapping::ptr mapping, bool output) : PolygonTextureGraphicsItem(mapping, output) { _controlPainter.reset(new MeshControlPainter(this)); _nHorizontalQuads = _nVerticalQuads = -1; + _wasGrabbing = false; } - void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter) { Q_UNUSED(painter); @@ -326,16 +326,29 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter) QVector > inputQuads = inputMesh->getQuads2d(); // Check if we increased or decreased number of columns/rows in mesh. - bool dirty = false; + bool forceRebuild = false; if (_nHorizontalQuads != outputMesh->nHorizontalQuads() || _nVerticalQuads != outputMesh->nVerticalQuads()) { - dirty = true; + forceRebuild = true; _cachedQuadItems.resize(_nHorizontalQuads = outputMesh->nHorizontalQuads()); for (int i=0; i<_nHorizontalQuads; i++) _cachedQuadItems[i].resize(_nVerticalQuads = outputMesh->nVerticalQuads()); } + // Keep track of whether we are currently grabbing the shape or a vertex so as to + // reduce resolution when editing (to prevent lags). + bool grabbing = (getCanvas()->shapeGrabbed() || getCanvas()->vertexGrabbed()); + + // Max depth is adjusted to draw less quads during click & drag. + int maxDepth = (grabbing ? 4 : -1); + + // Force rebuild on shape/vertex release. + if (_wasGrabbing && !grabbing) { + forceRebuild = true; + } + _wasGrabbing = grabbing; + // Go through the mesh quad by quad. for (int x = 0; x < outputMesh->nHorizontalQuads(); x++) { @@ -346,19 +359,22 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter) // Verify if item needs recomputing. CacheQuadItem& item = _cachedQuadItems[x][y]; - if (dirty || + if (forceRebuild || item.parent.input.toPolygon() != inputQuad.toPolygon() || item.parent.output.toPolygon() != outputQuad.toPolygon()) { + // Copy input and output quads for verification purposes. item.parent.input = inputQuad; item.parent.output = outputQuad; + + // Recompute sub quads. item.subQuads.clear(); QSizeF size = mapFromScene(outputQuad.toPolygon()).boundingRect().size(); float area = size.width() * size.height(); // Rebuild cache quad item. - _buildCacheQuadItem(item, inputQuad, outputQuad, area); + _buildCacheQuadItem(item, inputQuad, outputQuad, area, 0.0001f, 0.001f, MM::MESH_SUBDIVISION_MIN_AREA, maxDepth); } // Draw all the cached items. @@ -376,38 +392,55 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter) } } -void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshod, float outputThreshold) +void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshod, float outputThreshold, int minArea, int maxDepth) { - QPointF oa = mapFromScene(outputQuad.getVertex(0)); - QPointF ob = mapFromScene(outputQuad.getVertex(1)); - QPointF oc = mapFromScene(outputQuad.getVertex(2)); - QPointF od = mapFromScene(outputQuad.getVertex(3)); + bool stop = false; + if (maxDepth == 0 || outputArea < minArea) + stop = true; + else { + QPointF oa = mapFromScene(outputQuad.getVertex(0)); + QPointF ob = mapFromScene(outputQuad.getVertex(1)); + QPointF oc = mapFromScene(outputQuad.getVertex(2)); + QPointF od = mapFromScene(outputQuad.getVertex(3)); - QPointF ia = inputQuad.getVertex(0); - QPointF ib = inputQuad.getVertex(1); - QPointF ic = inputQuad.getVertex(2); - QPointF id = inputQuad.getVertex(3); + QPointF ia = inputQuad.getVertex(0); + QPointF ib = inputQuad.getVertex(1); + QPointF ic = inputQuad.getVertex(2); + QPointF id = inputQuad.getVertex(3); - // compute the dot products for the polygon - float outputV1dotV2 = QPointF::dotProduct(oa-ob, oc-ob); - float outputV3dotV4 = QPointF::dotProduct(oc-od, oa-od); - float outputV1dotV4 = QPointF::dotProduct(oa-ob, oa-od); - float outputV2dotV3 = QPointF::dotProduct(oc-ob, oc-od); + QPointF outputV1 = oa-ob; + QPointF outputV2 = oc-ob; + QPointF outputV3 = oc-od; + QPointF outputV4 = oa-od; - // compute the dot products for the texture - float inputV1dotV2 = QPointF::dotProduct(ia-ib, ic-ib); - float inputV3dotV4 = QPointF::dotProduct(ic-id, ia-id); - float inputV1dotV4 = QPointF::dotProduct(ia-ib, ia-id); - float inputV2dotV3 = QPointF::dotProduct(ic-ib, ic-id); + QPointF inputV1 = ia-ib; + QPointF inputV2 = ic-ib; + QPointF inputV3 = ic-id; + QPointF inputV4 = ia-id; - // Stopping criterion. - if (outputArea < MM::MESH_SUBDIVISION_MIN_AREA || - (fabs(outputV1dotV2 - outputV3dotV4) < outputThreshold && - fabs(outputV1dotV4 - outputV2dotV3) < outputThreshold && - fabs(inputV1dotV2 - inputV3dotV4) < inputThreshod && - fabs(inputV1dotV4 - inputV2dotV3) < inputThreshod)) + // compute the dot products for the polygon + float outputV1dotV2 = QPointF::dotProduct(outputV1, outputV2); + float outputV3dotV4 = QPointF::dotProduct(outputV3, outputV4); + float outputV1dotV4 = QPointF::dotProduct(outputV1, outputV4); + float outputV2dotV3 = QPointF::dotProduct(outputV2, outputV3); + + // compute the dot products for the texture + float inputV1dotV2 = QPointF::dotProduct(inputV1, inputV2); + float inputV3dotV4 = QPointF::dotProduct(inputV3, inputV4); + float inputV1dotV4 = QPointF::dotProduct(inputV1, inputV4); + float inputV2dotV3 = QPointF::dotProduct(inputV2, inputV3); + + // Stopping criterion. + stop = (fabs(outputV1dotV2 - outputV3dotV4) < outputThreshold && + fabs(outputV1dotV4 - outputV2dotV3) < outputThreshold && + fabs(inputV1dotV2 - inputV3dotV4) < inputThreshod && + fabs(inputV1dotV4 - inputV2dotV3) < inputThreshod); + } + + // + if (stop) { - item.subQuads.append( { inputQuad, outputQuad } ); + item.subQuads.append( (CacheQuadMapping){ inputQuad, outputQuad } ); } else // subdivide { @@ -415,7 +448,7 @@ void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Qua QList outputSubQuads = _split(outputQuad); for (int i = 0; i < inputSubQuads.size(); i++) { - _buildCacheQuadItem(item, inputSubQuads[i], outputSubQuads[i], outputArea*0.25, inputThreshod, outputThreshold); + _buildCacheQuadItem(item, inputSubQuads[i], outputSubQuads[i], outputArea*0.25, inputThreshod, outputThreshold, minArea, (maxDepth == -1 ? -1 : maxDepth - 1)); } } } diff --git a/ShapeGraphicsItem.h b/ShapeGraphicsItem.h index 0b0ca87..fc20b20 100644 --- a/ShapeGraphicsItem.h +++ b/ShapeGraphicsItem.h @@ -225,12 +225,15 @@ public: private: // Draws quad recursively using the technique described in Oliveira, M. "Correcting Texture Mapping Errors Introduced by Graphics Hardware" - void _buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshold = 0.0001f, float outputThreshold = 0.001f); + void _buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad, + float outputArea, float inputThreshold = 0.0001f, float outputThreshold = 0.001f, + int minArea=MM::MESH_SUBDIVISION_MIN_AREA, int maxDepth=-1); QList _split(const Quad& quad); QVector > _cachedQuadItems; int _nHorizontalQuads; int _nVerticalQuads; + bool _wasGrabbing; }; /// Graphics item for textured mesh. From fc627b230606644809ce9bf515adf36ea96c7b6f Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 26 Dec 2015 19:25:37 -0500 Subject: [PATCH 3/3] Compilation errors fix in MainWindow. --- MainWindow.cpp | 6 +++--- MainWindow.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 8765ed1..c846626 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -2734,7 +2734,7 @@ bool MainWindow::setTextureRate(int texture_id, double rate) bool MainWindow::setTextureVolume(int texture_id, double volume) { Paint::ptr paint = this->mappingManager->getPaintById(texture_id); - if (paint.get() == NULL) + if (paint.isNull()) { std::cout << "No such texture paint id " << texture_id << std::endl; return false; @@ -2743,7 +2743,7 @@ bool MainWindow::setTextureVolume(int texture_id, double volume) { if (paint->getType() == "media") { - Media *media = (Media *) paint.get(); // FIXME: use sharedptr cast + Media *media = (Media *) paint.data(); // FIXME: use sharedptr cast videoTimer->stop(); media->setVolume(volume); videoTimer->start(); @@ -2760,7 +2760,7 @@ bool MainWindow::setTextureVolume(int texture_id, double volume) void MainWindow::setTexturePlayState(int texture_id, bool played) { Paint::ptr paint = this->mappingManager->getPaintById(texture_id); - if (paint.get() == NULL) + if (paint.isNull()) { std::cout << "No such texture paint id " << texture_id << std::endl; } diff --git a/MainWindow.h b/MainWindow.h index 90d61f1..e881907 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -89,6 +89,7 @@ private slots: void openRecentFile(); void clearRecentFileList(); void openRecentVideo(); + void quitMapMap(); // Edit menu. void deleteItem(); // Context menu for mappings.