From ce4e8be9ac468a4297d8f2bf59d846b98190a20f Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 1/6] Move traverse methods to the QuadTreeNode --- components/terrain/quadtreenode.cpp | 45 +++++++++++++++++++++++ components/terrain/quadtreenode.hpp | 8 +++++ components/terrain/quadtreeworld.cpp | 54 +++------------------------- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 141803cf7..6072ee904 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -109,6 +109,51 @@ void QuadTreeNode::initNeighbours() getChild(i)->initNeighbours(); } +void QuadTreeNode::traverse(ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f& viewPoint, bool visible, float maxDist) +{ + if (!hasValidBounds()) + return; + + if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + visible = visible && !static_cast(nv)->isCulled(mBoundingBox); + + float dist = distance(viewPoint); + if (dist > maxDist) + return; + + bool stopTraversal = (mLodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); + + if (stopTraversal) + vd->add(this, visible); + else + { + for (unsigned int i=0; itraverse(vd, nv, viewPoint, visible, maxDist); + } +} + +void QuadTreeNode::traverseTo(ViewData* vd, float size, const osg::Vec2f& center) +{ + if (!hasValidBounds()) + return; + + if (getCenter().x() + getSize()/2.f <= center.x() - size/2.f + || getCenter().x() - getSize()/2.f >= center.x() + size/2.f + || getCenter().y() + getSize()/2.f <= center.y() - size/2.f + || getCenter().y() - getSize()/2.f >= center.y() + size/2.f) + return; + + bool stopTraversal = (getSize() == size); + + if (stopTraversal) + vd->add(this, true); + else + { + for (unsigned int i=0; itraverseTo(vd, size, center); + } +} + void QuadTreeNode::traverse(osg::NodeVisitor &nv) { if (!hasValidBounds()) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 9f7c7bbb7..aca05cfed 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -72,8 +72,16 @@ namespace Terrain /// center in cell coordinates const osg::Vec2f& getCenter() const; + /// Traverse the child tree and populate the ViewData virtual void traverse(osg::NodeVisitor& nv); + /// Optimized version of traverse() that doesn't incur the overhead of NodeVisitor double-dispatch or fetching the various variables. + /// Note this doesn't do any culling for non-cull visitors (e.g. intersections) so it shouldn't be used for those. + void traverse(ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f& viewPoint, bool visible, float maxDist); + + /// Traverse to a specific node and add only that node. + void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); + /// Set the Lod callback to use for determining when to stop traversing further down the quad tree. void setLodCallback(LodCallback* lodCallback); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 499e268dc..430fcd025 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -246,52 +246,6 @@ QuadTreeWorld::~QuadTreeWorld() mViewDataMap->clear(); } - -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& viewPoint, bool visible, float maxDist) -{ - if (!node->hasValidBounds()) - return; - - if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) - visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); - - float dist = node->distance(viewPoint); - if (dist > maxDist) - return; - - bool stopTraversal = (lodCallback && lodCallback->isSufficientDetail(node, dist)) || !node->getNumChildren(); - - if (stopTraversal) - vd->add(node, visible); - else - { - for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, lodCallback, viewPoint, visible, maxDist); - } -} - -void traverseToCell(QuadTreeNode* node, ViewData* vd, int cellX, int cellY) -{ - if (!node->hasValidBounds()) - return; - - if (node->getCenter().x() + node->getSize()/2.f <= cellX - || node->getCenter().x() - node->getSize()/2.f >= cellX+1 - || node->getCenter().y() + node->getSize()/2.f <= cellY - || node->getCenter().y() - node->getSize()/2.f >= cellY+1) - return; - - bool stopTraversal = !node->getNumChildren(); - - if (stopTraversal) - vd->add(node, true); - else - { - for (unsigned int i=0; igetNumChildren(); ++i) - traverseToCell(node->getChild(i), vd, cellX, cellY); - } -} - /// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set. unsigned int getVertexLod(QuadTreeNode* node, int vertexLodMod) { @@ -400,10 +354,10 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) int x,y; stream >> x; stream >> y; - traverseToCell(mRootNode.get(), vd, x,y); + mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); } else - traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); + mRootNode->traverse(vd, &nv, cv->getViewPoint(), true, mViewDistance); } else mRootNode->traverse(nv); @@ -483,7 +437,7 @@ void QuadTreeWorld::cacheCell(View *view, int x, int y) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverseToCell(mRootNode.get(), vd, x, y); + mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5f,y+0.5f)); for (unsigned int i=0; igetNumEntries(); ++i) { @@ -503,7 +457,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); - traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), viewPoint, false, mViewDistance); + mRootNode->traverse(vd, nullptr, viewPoint, false, mViewDistance); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { From cb6d27fb12af0016aa6bc7504d0569a657a892f4 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 2/6] Use the custom LineSegmentIntersector for QuadTree to simplify traversal code --- components/terrain/quadtreenode.cpp | 13 ++++++++----- components/terrain/quadtreenode.hpp | 25 ++++++++++++++++++++++--- components/terrain/quadtreeworld.cpp | 13 ++++++++++++- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 6072ee904..b842c172d 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -154,18 +154,21 @@ void QuadTreeNode::traverseTo(ViewData* vd, float size, const osg::Vec2f& center } } -void QuadTreeNode::traverse(osg::NodeVisitor &nv) +void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector* intersector) { if (!hasValidBounds()) return; - bool needsUpdate = true; - ViewData* vd = getView(nv, needsUpdate); + if (!intersector->intersectAndClip(getBoundingBox())) + return; - if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getViewPoint()))) || !getNumChildren()) + if (getNumChildren() == 0) vd->add(this, true); else - osg::Group::traverse(nv); + { + for (unsigned int i=0; iintersect(vd, intersector); + } } void QuadTreeNode::setLodCallback(LodCallback *lodCallback) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index aca05cfed..e52d2db93 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -2,12 +2,31 @@ #define OPENMW_COMPONENTS_TERRAIN_QUADTREENODE_H #include +#include #include "defs.hpp" namespace Terrain { + class TerrainLineIntersector : public osgUtil::LineSegmentIntersector + { + public: + TerrainLineIntersector(osgUtil::LineSegmentIntersector* intersector, osg::Matrix& matrix) : + osgUtil::LineSegmentIntersector(intersector->getStart() * matrix, intersector->getEnd() * matrix) + { + setPrecisionHint(intersector->getPrecisionHint()); + _intersectionLimit = intersector->getIntersectionLimit(); + _parent = intersector; + } + + bool intersectAndClip(const osg::BoundingBox& bbInput) + { + osg::Vec3d s(_start), e(_end); + return osgUtil::LineSegmentIntersector::intersectAndClip(s, e, bbInput); + } + }; + enum ChildDirection { NW = 0, @@ -72,9 +91,6 @@ namespace Terrain /// center in cell coordinates const osg::Vec2f& getCenter() const; - /// Traverse the child tree and populate the ViewData - virtual void traverse(osg::NodeVisitor& nv); - /// Optimized version of traverse() that doesn't incur the overhead of NodeVisitor double-dispatch or fetching the various variables. /// Note this doesn't do any culling for non-cull visitors (e.g. intersections) so it shouldn't be used for those. void traverse(ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f& viewPoint, bool visible, float maxDist); @@ -82,6 +98,9 @@ namespace Terrain /// Traverse to a specific node and add only that node. void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); + /// Adds all leaf nodes which intersect the line from start to end + void intersect(ViewData* vd, TerrainLineIntersector* intersector); + /// Set the Lod callback to use for determining when to stop traversing further down the quad tree. void setLodCallback(LodCallback* lodCallback); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 430fcd025..899e9c7f1 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -360,7 +360,18 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->traverse(vd, &nv, cv->getViewPoint(), true, mViewDistance); } else - mRootNode->traverse(nv); + { + osgUtil::IntersectionVisitor* iv = static_cast(&nv); + osgUtil::LineSegmentIntersector* lineIntsersector = dynamic_cast(iv->getIntersector()); + if (!lineIntsersector) + throw std::runtime_error("Can not update QuadTreeWorld - the LineSegmentIntersector expected"); + + osg::Matrix matrix = osg::Matrix::identity(); + if (lineIntsersector->getCoordinateFrame() == osgUtil::Intersector::CoordinateFrame::MODEL && iv->getModelMatrix() == 0) + matrix = lineIntsersector->getTransformation(*iv, osgUtil::Intersector::CoordinateFrame::MODEL); + osg::ref_ptr terrainIntersector (new TerrainLineIntersector(lineIntsersector, matrix)); + mRootNode->intersect(vd, terrainIntersector); + } } else if (isCullVisitor) { From ebcf8ca062857da2bc3bed52395329464612896d Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 3/6] Do not store a ViewDataMap in the every QuadTreeNode --- components/terrain/quadtreenode.cpp | 29 ---------------------------- components/terrain/quadtreenode.hpp | 10 ---------- components/terrain/quadtreeworld.cpp | 21 +++++++++++--------- 3 files changed, 12 insertions(+), 48 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index b842c172d..10f46c0eb 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -61,7 +61,6 @@ QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float , mValidBounds(false) , mSize(size) , mCenter(center) - , mViewDataMap(nullptr) { for (unsigned int i=0; i<4; ++i) mNeighbours[i] = 0; @@ -181,34 +180,6 @@ LodCallback *QuadTreeNode::getLodCallback() return mLodCallback; } -void QuadTreeNode::setViewDataMap(ViewDataMap *map) -{ - mViewDataMap = map; -} - -ViewDataMap *QuadTreeNode::getViewDataMap() -{ - return mViewDataMap; -} - -ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv, bool& needsUpdate) -{ - ViewData* vd = NULL; - if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) - { - osgUtil::CullVisitor* cv = static_cast(&nv); - vd = mViewDataMap->getViewData(cv->getCurrentCamera(), nv.getViewPoint(), needsUpdate); - } - else // INTERSECTION_VISITOR - { - osg::Vec3f viewPoint = nv.getViewPoint(); - static osg::ref_ptr dummyObj = new osg::DummyObject; - vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate); - needsUpdate = true; - } - return vd; -} - void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index e52d2db93..96b08842f 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -106,14 +106,6 @@ namespace Terrain LodCallback* getLodCallback(); - /// Set the view data map that the finally used nodes for a given camera/intersection are pushed onto. - void setViewDataMap(ViewDataMap* map); - - ViewDataMap* getViewDataMap(); - - /// Create or retrieve a view for the given traversal. - ViewData* getView(osg::NodeVisitor& nv, bool& needsUpdate); - private: QuadTreeNode* mParent; @@ -127,8 +119,6 @@ namespace Terrain osg::Vec2f mCenter; osg::ref_ptr mLodCallback; - - ViewDataMap* mViewDataMap; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 899e9c7f1..6fc41b04a 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -97,12 +97,11 @@ private: class QuadTreeBuilder { public: - QuadTreeBuilder(Terrain::Storage* storage, ViewDataMap* viewDataMap, float lodFactor, float minSize) + QuadTreeBuilder(Terrain::Storage* storage, float lodFactor, float minSize) : mStorage(storage) , mLodFactor(lodFactor) , mMinX(0.f), mMaxX(0.f), mMinY(0.f), mMaxY(0.f) , mMinSize(minSize) - , mViewDataMap(viewDataMap) { } @@ -120,7 +119,6 @@ public: float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); - mRootNode->setViewDataMap(mViewDataMap); mRootNode->setLodCallback(new DefaultLodCallback(mLodFactor, mMinSize)); addChildren(mRootNode); @@ -171,7 +169,6 @@ public: osg::ref_ptr node = new QuadTreeNode(parent, direction, size, center); node->setLodCallback(parent->getLodCallback()); - node->setViewDataMap(mViewDataMap); if (center.x() - halfSize > mMaxX || center.x() + halfSize < mMinX @@ -220,7 +217,6 @@ private: float mLodFactor; float mMinX, mMaxX, mMinY, mMaxY; float mMinSize; - ViewDataMap* mViewDataMap; osg::ref_ptr mRootNode; }; @@ -337,8 +333,15 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) return; } - bool needsUpdate = false; - ViewData* vd = mRootNode->getView(nv, needsUpdate); + bool needsUpdate = true; + ViewData* vd = nullptr; + if (isCullVisitor) + vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), needsUpdate); + else + { + static ViewData sIntersectionViewData; + vd = &sIntersectionViewData; + } if (needsUpdate) { @@ -403,7 +406,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) } if (!isCullVisitor) - vd->reset(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. + vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. vd->markUnchanged(); @@ -422,7 +425,7 @@ void QuadTreeWorld::ensureQuadTreeBuilt() return; const float minSize = 1/8.f; - QuadTreeBuilder builder(mStorage, mViewDataMap.get(), mLodFactor, minSize); + QuadTreeBuilder builder(mStorage, mLodFactor, minSize); builder.build(); mRootNode = builder.getRootNode(); From a61c0aaee1a1473cc45951fb91deb26a42bd91d1 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 4/6] Do not load height data to the qued tree since we do not need it now --- components/terrain/quadtreenode.cpp | 9 +++------ components/terrain/quadtreenode.hpp | 4 ++-- components/terrain/quadtreeworld.cpp | 25 ++++++++++++------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 10f46c0eb..b92c37313 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -108,14 +108,11 @@ void QuadTreeNode::initNeighbours() getChild(i)->initNeighbours(); } -void QuadTreeNode::traverse(ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f& viewPoint, bool visible, float maxDist) +void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, float maxDist) { if (!hasValidBounds()) return; - if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) - visible = visible && !static_cast(nv)->isCulled(mBoundingBox); - float dist = distance(viewPoint); if (dist > maxDist) return; @@ -123,11 +120,11 @@ void QuadTreeNode::traverse(ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f bool stopTraversal = (mLodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); if (stopTraversal) - vd->add(this, visible); + vd->add(this, true); else { for (unsigned int i=0; itraverse(vd, nv, viewPoint, visible, maxDist); + getChild(i)->traverse(vd, viewPoint, maxDist); } } diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 96b08842f..391c2b75f 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -92,8 +92,8 @@ namespace Terrain const osg::Vec2f& getCenter() const; /// Optimized version of traverse() that doesn't incur the overhead of NodeVisitor double-dispatch or fetching the various variables. - /// Note this doesn't do any culling for non-cull visitors (e.g. intersections) so it shouldn't be used for those. - void traverse(ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f& viewPoint, bool visible, float maxDist); + /// Note this doesn't do any culling. + void traverse(ViewData* vd, const osg::Vec3f& viewPoint, float maxDist); /// Traverse to a specific node and add only that node. void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 6fc41b04a..bd81c8637 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -188,15 +188,14 @@ public: if (node->getSize() <= mMinSize) { - // We arrived at a leaf - float minZ,maxZ; - if (mStorage->getMinMaxHeights(size, center, minZ, maxZ)) - { - float cellWorldSize = mStorage->getCellWorldSize(); - osg::BoundingBox boundingBox(osg::Vec3f((center.x()-halfSize)*cellWorldSize, (center.y()-halfSize)*cellWorldSize, minZ), - osg::Vec3f((center.x()+halfSize)*cellWorldSize, (center.y()+halfSize)*cellWorldSize, maxZ)); - node->setBoundingBox(boundingBox); - } + // We arrived at a leaf. + // Since the tree is used for LOD level selection instead of culling, we do not need to load an actual height data here. + float minZ = -std::numeric_limits::max(); + float maxZ = std::numeric_limits::max(); + float cellWorldSize = mStorage->getCellWorldSize(); + osg::BoundingBox boundingBox(osg::Vec3f((center.x()-halfSize)*cellWorldSize, (center.y()-halfSize)*cellWorldSize, minZ), + osg::Vec3f((center.x()+halfSize)*cellWorldSize, (center.y()+halfSize)*cellWorldSize, maxZ)); + node->setBoundingBox(boundingBox); return node; } else @@ -360,7 +359,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); } else - mRootNode->traverse(vd, &nv, cv->getViewPoint(), true, mViewDistance); + mRootNode->traverse(vd, cv->getViewPoint(), mViewDistance); } else { @@ -376,9 +375,9 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->intersect(vd, terrainIntersector); } } - else if (isCullVisitor) + + if (isCullVisitor) { - // view point is the same, but must still update visible status in case the camera has rotated for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); @@ -471,7 +470,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); - mRootNode->traverse(vd, nullptr, viewPoint, false, mViewDistance); + mRootNode->traverse(vd, viewPoint, mViewDistance); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { From e4ba6ecf15cbe63d055f855df6b2f2e22431b4fc Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 5/6] Do not store a LOD callback in the every QuadTreeNode --- components/terrain/quadtreenode.cpp | 16 +++------------- components/terrain/quadtreenode.hpp | 12 ++---------- components/terrain/quadtreeworld.cpp | 13 ++++++------- components/terrain/quadtreeworld.hpp | 2 ++ 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index b92c37313..cb921631b 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -108,7 +108,7 @@ void QuadTreeNode::initNeighbours() getChild(i)->initNeighbours(); } -void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, float maxDist) +void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist) { if (!hasValidBounds()) return; @@ -117,14 +117,14 @@ void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, float max if (dist > maxDist) return; - bool stopTraversal = (mLodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); + bool stopTraversal = (lodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); if (stopTraversal) vd->add(this, true); else { for (unsigned int i=0; itraverse(vd, viewPoint, maxDist); + getChild(i)->traverse(vd, viewPoint, lodCallback, maxDist); } } @@ -167,16 +167,6 @@ void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector* intersector) } } -void QuadTreeNode::setLodCallback(LodCallback *lodCallback) -{ - mLodCallback = lodCallback; -} - -LodCallback *QuadTreeNode::getLodCallback() -{ - return mLodCallback; -} - void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 391c2b75f..26eecd109 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -91,9 +91,8 @@ namespace Terrain /// center in cell coordinates const osg::Vec2f& getCenter() const; - /// Optimized version of traverse() that doesn't incur the overhead of NodeVisitor double-dispatch or fetching the various variables. - /// Note this doesn't do any culling. - void traverse(ViewData* vd, const osg::Vec3f& viewPoint, float maxDist); + /// Traverse nodes according to LOD selection. + void traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); /// Traverse to a specific node and add only that node. void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); @@ -101,11 +100,6 @@ namespace Terrain /// Adds all leaf nodes which intersect the line from start to end void intersect(ViewData* vd, TerrainLineIntersector* intersector); - /// Set the Lod callback to use for determining when to stop traversing further down the quad tree. - void setLodCallback(LodCallback* lodCallback); - - LodCallback* getLodCallback(); - private: QuadTreeNode* mParent; @@ -117,8 +111,6 @@ namespace Terrain bool mValidBounds; float mSize; osg::Vec2f mCenter; - - osg::ref_ptr mLodCallback; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index bd81c8637..8b25b3e2c 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -97,9 +97,8 @@ private: class QuadTreeBuilder { public: - QuadTreeBuilder(Terrain::Storage* storage, float lodFactor, float minSize) + QuadTreeBuilder(Terrain::Storage* storage, float minSize) : mStorage(storage) - , mLodFactor(lodFactor) , mMinX(0.f), mMaxX(0.f), mMinY(0.f), mMaxY(0.f) , mMinSize(minSize) { @@ -119,7 +118,6 @@ public: float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); - mRootNode->setLodCallback(new DefaultLodCallback(mLodFactor, mMinSize)); addChildren(mRootNode); mRootNode->initNeighbours(); @@ -168,7 +166,6 @@ public: } osg::ref_ptr node = new QuadTreeNode(parent, direction, size, center); - node->setLodCallback(parent->getLodCallback()); if (center.x() - halfSize > mMaxX || center.x() + halfSize < mMinX @@ -218,6 +215,7 @@ private: float mMinSize; osg::ref_ptr mRootNode; + osg::ref_ptr mLodCallback; }; QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize) @@ -359,7 +357,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); } else - mRootNode->traverse(vd, cv->getViewPoint(), mViewDistance); + mRootNode->traverse(vd, cv->getViewPoint(), mLodCallback, mViewDistance); } else { @@ -424,7 +422,8 @@ void QuadTreeWorld::ensureQuadTreeBuilt() return; const float minSize = 1/8.f; - QuadTreeBuilder builder(mStorage, mLodFactor, minSize); + mLodCallback = new DefaultLodCallback(mLodFactor, minSize); + QuadTreeBuilder builder(mStorage, minSize); builder.build(); mRootNode = builder.getRootNode(); @@ -470,7 +469,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); - mRootNode->traverse(vd, viewPoint, mViewDistance); + mRootNode->traverse(vd, viewPoint, mLodCallback, mViewDistance); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 349f27b64..bcb671ee1 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -15,6 +15,7 @@ namespace Terrain { class RootNode; class ViewDataMap; + class LodCallback; /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell) @@ -48,6 +49,7 @@ namespace Terrain osg::ref_ptr mRootNode; osg::ref_ptr mViewDataMap; + osg::ref_ptr mLodCallback; OpenThreads::Mutex mQuadTreeMutex; bool mQuadTreeBuilt; From a730365ea16dbc890721d14b6788fcfd0e0c8513 Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 6/6] Creanup Distant Terrain code - Cull terrain in the stock osg::CullVisitor - Do not compile composite maps for shadow camera - Do not abuse userdata for composite maps --- components/terrain/chunkmanager.cpp | 13 ++------- components/terrain/quadtreenode.cpp | 6 ++-- components/terrain/quadtreeworld.cpp | 38 ++++++-------------------- components/terrain/terraindrawable.cpp | 12 +++++--- components/terrain/terraindrawable.hpp | 11 +++++++- components/terrain/terraingrid.cpp | 5 ---- components/terrain/viewdata.cpp | 12 ++++---- components/terrain/viewdata.hpp | 5 ++-- 8 files changed, 39 insertions(+), 63 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 41b1fdbe1..881397936 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -30,7 +30,6 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T , mCompositeMapSize(512) , mCompositeMapLevel(1.f) , mMaxCompGeometrySize(1.f) - , mCullingActive(true) { } @@ -208,7 +207,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve mCompositeMapRenderer->addCompositeMap(compositeMap.get(), false); - transform->getOrCreateUserDataContainer()->setUserData(compositeMap); + geometry->setCompositeMap(compositeMap); + geometry->setCompositeMapRenderer(mCompositeMapRenderer); TextureLayer layer; layer.mDiffuseMap = compositeMap->mTexture; @@ -222,14 +222,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve } transform->addChild(geometry); - - if (!mCullingActive) - { - transform->setCullingActive(false); - geometry->setCullingActive(false); - } - else - transform->getBound(); + transform->getBound(); if (mSceneManager->getIncrementalCompileOperation()) { diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index cb921631b..69f9b3fa4 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -120,7 +120,7 @@ void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallba bool stopTraversal = (lodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); if (stopTraversal) - vd->add(this, true); + vd->add(this); else { for (unsigned int i=0; iadd(this, true); + vd->add(this); else { for (unsigned int i=0; iadd(this, true); + vd->add(this); else { for (unsigned int i=0; igetSize() <= mMinSize) { // We arrived at a leaf. - // Since the tree is used for LOD level selection instead of culling, we do not need to load an actual height data here. + // Since the tree is used for LOD level selection instead of culling, we do not need to load the actual height data here. float minZ = -std::numeric_limits::max(); float maxZ = std::numeric_limits::max(); float cellWorldSize = mStorage->getCellWorldSize(); @@ -226,9 +226,6 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour , mVertexLodMod(vertexLodMod) , mViewDistance(std::numeric_limits::max()) { - // No need for culling on the Drawable / Transform level as the quad tree performs the culling already. - mChunkManager->setCullingActive(false); - mChunkManager->setCompositeMapSize(compMapResolution); mChunkManager->setCompositeMapLevel(compMapLevel); mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize); @@ -362,44 +359,25 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) else { osgUtil::IntersectionVisitor* iv = static_cast(&nv); - osgUtil::LineSegmentIntersector* lineIntsersector = dynamic_cast(iv->getIntersector()); - if (!lineIntsersector) - throw std::runtime_error("Can not update QuadTreeWorld - the LineSegmentIntersector expected"); + osgUtil::LineSegmentIntersector* lineIntersector = dynamic_cast(iv->getIntersector()); + if (!lineIntersector) + throw std::runtime_error("Cannot update QuadTreeWorld: node visitor is not LineSegmentIntersector"); osg::Matrix matrix = osg::Matrix::identity(); - if (lineIntsersector->getCoordinateFrame() == osgUtil::Intersector::CoordinateFrame::MODEL && iv->getModelMatrix() == 0) - matrix = lineIntsersector->getTransformation(*iv, osgUtil::Intersector::CoordinateFrame::MODEL); - osg::ref_ptr terrainIntersector (new TerrainLineIntersector(lineIntsersector, matrix)); + if (lineIntersector->getCoordinateFrame() == osgUtil::Intersector::CoordinateFrame::MODEL && iv->getModelMatrix() == 0) + matrix = lineIntersector->getTransformation(*iv, osgUtil::Intersector::CoordinateFrame::MODEL); + osg::ref_ptr terrainIntersector (new TerrainLineIntersector(lineIntersector, matrix)); mRootNode->intersect(vd, terrainIntersector); } } - if (isCullVisitor) - { - for (unsigned int i=0; igetNumEntries(); ++i) - { - ViewData::Entry& entry = vd->getEntry(i); - entry.set(entry.mNode, !static_cast(&nv)->isCulled(entry.mNode->getBoundingBox())); - } - } - for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); - if (entry.mVisible) - { - osg::UserDataContainer* udc = entry.mRenderingNode->getUserDataContainer(); - if (udc && udc->getUserData()) - { - mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); - udc->setUserData(nullptr); - - } - entry.mRenderingNode->accept(nv); - } + entry.mRenderingNode->accept(nv); } if (!isCullVisitor) diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index f216bb33b..151977cd4 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -4,12 +4,10 @@ #include -namespace Terrain -{ +#include "compositemaprenderer.hpp" -TerrainDrawable::TerrainDrawable() +namespace Terrain { -} TerrainDrawable::TerrainDrawable(const TerrainDrawable ©, const osg::CopyOp ©op) : osg::Geometry(copy, copyop) @@ -63,6 +61,12 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) return; } + if (mCompositeMap) + { + mCompositeMapRenderer->setImmediate(mCompositeMap); + mCompositeMap = nullptr; + } + bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv); for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp index 6bef60bc7..b77b6b784 100644 --- a/components/terrain/terraindrawable.hpp +++ b/components/terrain/terraindrawable.hpp @@ -16,6 +16,9 @@ namespace SceneUtil namespace Terrain { + class CompositeMap; + class CompositeMapRenderer; + /** * Subclass of Geometry that supports built in multi-pass rendering and built in LightListCallback. */ @@ -28,7 +31,8 @@ namespace Terrain virtual const char* className() const { return "TerrainDrawable"; } virtual const char* libraryName() const { return "Terrain"; } - TerrainDrawable(); + TerrainDrawable() = default; + ~TerrainDrawable() = default; TerrainDrawable(const TerrainDrawable& copy, const osg::CopyOp& copyop); virtual void accept(osg::NodeVisitor &nv); @@ -41,10 +45,15 @@ namespace Terrain virtual void compileGLObjects(osg::RenderInfo& renderInfo) const; + void setCompositeMap(CompositeMap* map) { mCompositeMap = map; } + void setCompositeMapRenderer(CompositeMapRenderer* renderer) { mCompositeMapRenderer = renderer; } + private: PassVector mPasses; osg::ref_ptr mLightListCallback; + osg::ref_ptr mCompositeMap; + osg::ref_ptr mCompositeMapRenderer; }; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index f8ce2019c..c91240334 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -61,11 +61,6 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu return nullptr; if (parent) parent->addChild(node); - - osg::UserDataContainer* udc = node->getUserDataContainer(); - if (udc && udc->getUserData()) - mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); - return node; } } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index d1870abaa..d07a0e356 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -26,7 +26,7 @@ void ViewData::copyFrom(const ViewData& other) mViewPoint = other.mViewPoint; } -void ViewData::add(QuadTreeNode *node, bool visible) +void ViewData::add(QuadTreeNode *node) { unsigned int index = mNumEntries++; @@ -34,7 +34,7 @@ void ViewData::add(QuadTreeNode *node, bool visible) mEntries.resize(index+1); Entry& entry = mEntries[index]; - if (entry.set(node, visible)) + if (entry.set(node)) mChanged = true; } @@ -73,7 +73,7 @@ void ViewData::reset() { // clear any unused entries for (unsigned int i=mNumEntries; i mRenderingNode;