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 141803cf7..69f9b3fa4 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; @@ -109,56 +108,63 @@ void QuadTreeNode::initNeighbours() getChild(i)->initNeighbours(); } -void QuadTreeNode::traverse(osg::NodeVisitor &nv) +void QuadTreeNode::traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist) { if (!hasValidBounds()) return; - bool needsUpdate = true; - ViewData* vd = getView(nv, needsUpdate); + float dist = distance(viewPoint); + if (dist > maxDist) + return; - if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getViewPoint()))) || !getNumChildren()) - vd->add(this, true); + bool stopTraversal = (lodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); + + if (stopTraversal) + vd->add(this); else - osg::Group::traverse(nv); -} - -void QuadTreeNode::setLodCallback(LodCallback *lodCallback) -{ - mLodCallback = lodCallback; -} - -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); + for (unsigned int i=0; itraverse(vd, viewPoint, lodCallback, maxDist); } - else // INTERSECTION_VISITOR +} + +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); + else { - osg::Vec3f viewPoint = nv.getViewPoint(); - static osg::ref_ptr dummyObj = new osg::DummyObject; - vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate); - needsUpdate = true; + for (unsigned int i=0; itraverseTo(vd, size, center); + } +} + +void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector* intersector) +{ + if (!hasValidBounds()) + return; + + if (!intersector->intersectAndClip(getBoundingBox())) + return; + + if (getNumChildren() == 0) + vd->add(this); + else + { + for (unsigned int i=0; iintersect(vd, intersector); } - return vd; } void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 9f7c7bbb7..26eecd109 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,20 +91,14 @@ namespace Terrain /// center in cell coordinates const osg::Vec2f& getCenter() const; - virtual void traverse(osg::NodeVisitor& nv); + /// Traverse nodes according to LOD selection. + void traverse(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); - /// Set the Lod callback to use for determining when to stop traversing further down the quad tree. - void setLodCallback(LodCallback* lodCallback); + /// Traverse to a specific node and add only that node. + void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); - 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); + /// Adds all leaf nodes which intersect the line from start to end + void intersect(ViewData* vd, TerrainLineIntersector* intersector); private: QuadTreeNode* mParent; @@ -98,10 +111,6 @@ namespace Terrain bool mValidBounds; float mSize; osg::Vec2f mCenter; - - osg::ref_ptr mLodCallback; - - ViewDataMap* mViewDataMap; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 499e268dc..b2f119b88 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -97,12 +97,10 @@ private: class QuadTreeBuilder { public: - QuadTreeBuilder(Terrain::Storage* storage, ViewDataMap* viewDataMap, 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) - , mViewDataMap(viewDataMap) { } @@ -120,8 +118,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); mRootNode->initNeighbours(); @@ -170,8 +166,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 @@ -191,15 +185,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 the 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 @@ -220,9 +213,9 @@ private: float mLodFactor; float mMinX, mMaxX, mMinY, mMaxY; float mMinSize; - ViewDataMap* mViewDataMap; 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) @@ -233,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); @@ -246,52 +236,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) { @@ -383,8 +327,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) { @@ -400,21 +351,23 @@ 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, cv->getViewPoint(), mLodCallback, mViewDistance); } else - mRootNode->traverse(nv); - } - else 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); - entry.set(entry.mNode, !static_cast(&nv)->isCulled(entry.mNode->getBoundingBox())); + osgUtil::IntersectionVisitor* iv = static_cast(&nv); + 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 (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); } } @@ -424,21 +377,11 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) 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) - 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(); @@ -457,7 +400,8 @@ void QuadTreeWorld::ensureQuadTreeBuilt() return; const float minSize = 1/8.f; - QuadTreeBuilder builder(mStorage, mViewDataMap.get(), mLodFactor, minSize); + mLodCallback = new DefaultLodCallback(mLodFactor, minSize); + QuadTreeBuilder builder(mStorage, minSize); builder.build(); mRootNode = builder.getRootNode(); @@ -483,7 +427,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 +447,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, 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; 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,13 +4,11 @@ #include +#include "compositemaprenderer.hpp" + namespace Terrain { -TerrainDrawable::TerrainDrawable() -{ -} - TerrainDrawable::TerrainDrawable(const TerrainDrawable ©, const osg::CopyOp ©op) : osg::Geometry(copy, copyop) , mPasses(copy.mPasses) @@ -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;