From 7d004bf757e24dd15436cfdb2390d99a848dbf60 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 22:19:48 +0100 Subject: [PATCH] Preliminary rendering of QuadTreeWorld --- components/terrain/quadtreenode.cpp | 90 +++++++++++++--------------- components/terrain/quadtreenode.hpp | 28 +++++++++ components/terrain/quadtreeworld.cpp | 75 +++++++++++++++++++++-- 3 files changed, 140 insertions(+), 53 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 518274df5..99f60827d 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -5,46 +5,7 @@ #include #include "defs.hpp" - -namespace -{ - - float distance(const osg::BoundingBox& box, const osg::Vec3f& v) - { - if (box.contains(v)) - return 0; - else - { - osg::Vec3f maxDist(0,0,0); - - if (v.x() < box.xMin()) - maxDist.x() = box.xMin() - v.x(); - else if (v.x() > box.xMax()) - maxDist.x() = v.x() - box.xMax(); - - if (v.y() < box.yMin()) - maxDist.y() = box.yMin() - v.y(); - else if (v.y() > box.yMax()) - maxDist.y() = v.y() - box.yMax(); - - if (v.z() < box.zMin()) - maxDist.z() = box.zMin() - v.z(); - else if (v.z() > box.zMax()) - maxDist.z() = v.z() - box.zMax(); - - return maxDist.length(); - } - } - - int Log2( int n ) - { - assert(n > 0); - int targetlevel = 0; - while (n >>= 1) ++targetlevel; - return targetlevel; - } - -} +#include "viewdata.hpp" namespace Terrain { @@ -104,6 +65,10 @@ QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float mNeighbours[i] = 0; } +QuadTreeNode::~QuadTreeNode() +{ +} + QuadTreeNode* QuadTreeNode::getParent() { return mParent; @@ -125,18 +90,47 @@ void QuadTreeNode::initNeighbours() void QuadTreeNode::traverse(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) - return; + if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + { + osgUtil::CullVisitor* cv = static_cast(&nv); + + // do another culling test against bounding box as its much more accurate than the bounding sphere. + if (cv->isCulled(mBoundingBox)) + return; + } - osgUtil::CullVisitor* cv = static_cast(&nv); + if ((mLodCallback && mLodCallback->isSufficientDetail(this, nv)) || !getNumChildren()) + getView(nv)->add(this); + else + osg::Group::traverse(nv); +} - // do another culling test against bounding box as its much more accurate than the bounding sphere. - if (cv->isCulled(mBoundingBox)) - return; +void QuadTreeNode::setLodCallback(LodCallback *lodCallback) +{ + mLodCallback = lodCallback; +} - //float dist = distance(getBoundingBox(), nv.getEyePoint()); +void QuadTreeNode::setViewDataMap(ViewDataMap *map) +{ + mViewDataMap = map; +} - osg::Group::traverse(nv); +ViewDataMap *QuadTreeNode::getViewDataMap() +{ + return mViewDataMap; +} + +ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) +{ + if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + { + osgUtil::CullVisitor* cv = static_cast(&nv); + return mViewDataMap->getViewData(cv->getCurrentCamera(), true); + } + else // INTERSECTION_VISITOR + { + return mViewDataMap->getViewData(&nv, false); + } } void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index aab54c69c..0f7eb05ef 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -17,10 +17,23 @@ namespace Terrain Root }; + class QuadTreeNode; + class LodCallback : public osg::Referenced + { + public: + virtual ~LodCallback() {} + + virtual bool isSufficientDetail(QuadTreeNode *node, osg::NodeVisitor &nv) = 0; + }; + + class ViewDataMap; + class ViewData; + class QuadTreeNode : public osg::Group { public: QuadTreeNode(QuadTreeNode* parent, ChildDirection dir, float size, const osg::Vec2f& center); + virtual ~QuadTreeNode(); QuadTreeNode* getParent(); @@ -49,6 +62,17 @@ namespace Terrain virtual void traverse(osg::NodeVisitor& nv); + /// Set the Lod callback to use for determining when to stop traversing further down the quad tree. + void setLodCallback(LodCallback* lodCallback); + + /// 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); + private: QuadTreeNode* mParent; @@ -59,6 +83,10 @@ namespace Terrain osg::BoundingBox mBoundingBox; float mSize; osg::Vec2f mCenter; + + osg::ref_ptr mLodCallback; + + osg::ref_ptr mViewDataMap; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 25aecd2a7..24f24ff37 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -4,10 +4,8 @@ #include "quadtreenode.hpp" #include "storage.hpp" - - -#include -#include +#include "viewdata.hpp" +#include "chunkmanager.hpp" namespace { @@ -29,11 +27,57 @@ namespace return 1 << depth; } + int Log2( unsigned int n ) + { + int targetlevel = 0; + while (n >>= 1) ++targetlevel; + return targetlevel; + } + + float distance(const osg::BoundingBox& box, const osg::Vec3f& v) + { + if (box.contains(v)) + return 0; + else + { + osg::Vec3f maxDist(0,0,0); + + if (v.x() < box.xMin()) + maxDist.x() = box.xMin() - v.x(); + else if (v.x() > box.xMax()) + maxDist.x() = v.x() - box.xMax(); + + if (v.y() < box.yMin()) + maxDist.y() = box.yMin() - v.y(); + else if (v.y() > box.yMax()) + maxDist.y() = v.y() - box.yMax(); + + if (v.z() < box.zMin()) + maxDist.z() = box.zMin() - v.z(); + else if (v.z() > box.zMax()) + maxDist.z() = v.z() - box.zMax(); + + return maxDist.length(); + } + } + } namespace Terrain { +class DefaultLodCallback : public LodCallback +{ +public: + virtual bool isSufficientDetail(QuadTreeNode* node, osg::NodeVisitor& nv) + { + float dist = distance(node->getBoundingBox(), nv.getEyePoint()); + int nativeLodLevel = Log2(static_cast(node->getSize()*4)); + int lodLevel = Log2(static_cast(dist/2048.0)); + + return nativeLodLevel <= lodLevel; + } +}; class RootNode : public QuadTreeNode { @@ -84,6 +128,7 @@ public: float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); + mRootNode->setViewDataMap(new ViewDataMap); addChildren(mRootNode); mRootNode->initNeighbours(); @@ -125,6 +170,8 @@ public: } osg::ref_ptr node = new QuadTreeNode(parent, direction, size, center); + node->setLodCallback(new DefaultLodCallback); + node->setViewDataMap(parent->getViewDataMap()); parent->addChild(node); if (center.x() - size > mMaxX @@ -190,10 +237,28 @@ QuadTreeWorld::~QuadTreeWorld() void QuadTreeWorld::accept(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) + if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)// && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) return; mRootNode->traverse(nv); + + ViewData* vd = mRootNode->getView(nv); + + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + if (!entry.mRenderingNode) + { + int lod = Log2(int(entry.mNode->getSize())); + entry.mRenderingNode = mChunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), lod); + } + + entry.mRenderingNode->accept(nv); + } + + vd->reset(nv.getTraversalNumber()); + + mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber()); } void QuadTreeWorld::loadCell(int x, int y)