diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 1446f8aaa5..32ddd8b5f8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -988,7 +988,7 @@ void RenderingManager::updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, con void RenderingManager::frameStarted(float dt, bool paused) { - if (mTerrain && mTerrain->getVisible()) + if (mTerrain) mTerrain->update(mRendering.getCamera()->getRealPosition()); if (!paused) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 7faf81a5c8..ed6877f503 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -151,8 +151,15 @@ QuadTreeNode::QuadTreeNode(Terrain* terrain, ChildDirection dir, float size, con for (int i=0; i<4; ++i) mNeighbours[i] = NULL; - mSceneNode = mTerrain->getSceneManager()->getRootSceneNode()->createChildSceneNode( - Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0)); + if (mDirection == Root) + mSceneNode = mTerrain->getRootSceneNode(); + else + mSceneNode = mTerrain->getSceneManager()->createSceneNode(); + Ogre::Vector2 pos (0,0); + if (mParent) + pos = mParent->getCenter(); + pos = mCenter - pos; + mSceneNode->setPosition(Ogre::Vector3(pos.x*8192, pos.y*8192, 0)); mLodLevel = log2(mSize); @@ -221,13 +228,12 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) float dist = distance(mWorldBounds, cameraPos); - if (!mTerrain->getDistantLandEnabled()) + bool distantLand = mTerrain->getDistantLandEnabled(); + + // Make sure our scene node is attached + if (!mSceneNode->isInSceneGraph()) { - if (dist > 8192*2) - { - destroyChunks(); - return; - } + mParent->getSceneNode()->addChild(mSceneNode); } /// \todo implement error metrics or some other means of not using arbitrary values @@ -246,9 +252,22 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) if (dist > 8192*64) wantedLod = 6; + bool hadChunk = hasChunk(); + + if (!distantLand && dist > 8192*2) + { + if (mIsActive) + { + destroyChunks(true); + mIsActive = false; + } + return; + } + + mIsActive = true; + if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod) { - bool hadChunk = hasChunk(); // Wanted LOD is small enough to render this node in one chunk if (!mChunk) { @@ -297,32 +316,36 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) if (!hadChunk && hasChildren()) { - for (int i=0; i<4; ++i) - mChildren[i]->hideChunks(); + // Make sure child scene nodes are detached + mSceneNode->removeAllChildren(); + + // If distant land is enabled, keep the chunks around in case we need them again, + // otherwise, prefer low memory usage + if (!distantLand) + for (int i=0; i<4; ++i) + mChildren[i]->destroyChunks(true); } } else { // Wanted LOD is too detailed to be rendered in one chunk, // so split it up by delegating to child nodes - if (mChunk) - mChunk->setVisible(false); + if (hadChunk) + { + // If distant land is enabled, keep the chunks around in case we need them again, + // otherwise, prefer low memory usage + if (!distantLand) + destroyChunks(false); + else if (mChunk) + mChunk->setVisible(false); + } assert(hasChildren() && "Leaf node's LOD needs to be 0"); for (int i=0; i<4; ++i) mChildren[i]->update(cameraPos); } } -void QuadTreeNode::hideChunks() -{ - if (mChunk) - mChunk->setVisible(false); - else if (hasChildren()) - for (int i=0; i<4; ++i) - mChildren[i]->hideChunks(); -} - -void QuadTreeNode::destroyChunks() +void QuadTreeNode::destroyChunks(bool children) { if (mChunk) { @@ -348,9 +371,9 @@ void QuadTreeNode::destroyChunks() mCompositeMap.setNull(); } } - else if (hasChildren()) + else if (children && hasChildren()) for (int i=0; i<4; ++i) - mChildren[i]->destroyChunks(); + mChildren[i]->destroyChunks(true); } void QuadTreeNode::updateIndexBuffers() @@ -366,7 +389,7 @@ void QuadTreeNode::updateIndexBuffers() bool QuadTreeNode::hasChunk() { - return mChunk && mChunk->getVisible(); + return mSceneNode->isInSceneGraph() && mChunk && mChunk->getVisible(); } size_t QuadTreeNode::getActualLodLevel() diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index bccd26642b..fe646f3bfd 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -72,6 +72,8 @@ namespace Terrain QuadTreeNode* getParent() { return mParent; } + Ogre::SceneNode* getSceneNode() { return mSceneNode; } + int getSize() { return mSize; } Ogre::Vector2 getCenter() { return mCenter; } @@ -100,11 +102,8 @@ namespace Terrain /// Call after QuadTreeNode::update! void updateIndexBuffers(); - /// Hide chunks rendered by this node and all its children - void hideChunks(); - - /// Destroy chunks rendered by this node and all its children - void destroyChunks(); + /// Destroy chunks rendered by this node *and* its children (if param is true) + void destroyChunks(bool children); /// Get the effective LOD level if this node was rendered in one chunk /// with ESM::Land::LAND_SIZE^2 vertices @@ -127,6 +126,10 @@ namespace Terrain // Stored here for convenience in case we need layer list again MaterialGenerator* mMaterialGenerator; + /// Is this node (or any of its child nodes) currently configured to render itself? + /// (only relevant when distant land is disabled, otherwise whole terrain is always rendered) + bool mIsActive; + bool mIsDummy; float mSize; size_t mLodLevel; // LOD if we were to render this node in one chunk diff --git a/components/terrain/terrain.cpp b/components/terrain/terrain.cpp index 3d65fbfc12..da1ad11418 100644 --- a/components/terrain/terrain.cpp +++ b/components/terrain/terrain.cpp @@ -59,6 +59,7 @@ namespace Terrain , mVisibilityFlags(visibilityFlags) , mDistantLand(distantLand) , mShaders(shaders) + , mVisible(true) { mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); @@ -81,6 +82,8 @@ namespace Terrain // Adjust the center according to the new size Ogre::Vector3 center = mBounds.getCenter() + Ogre::Vector3((size-origSizeX)/2.f, (size-origSizeY)/2.f, 0); + mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL); buildQuadTree(mRootNode); mRootNode->initAabb(); @@ -142,6 +145,8 @@ namespace Terrain void Terrain::update(const Ogre::Vector3& cameraPos) { + if (!mVisible) + return; mRootNode->update(cameraPos); mRootNode->updateIndexBuffers(); } @@ -379,8 +384,12 @@ namespace Terrain void Terrain::setVisible(bool visible) { + if (visible && !mVisible) + mSceneMgr->getRootSceneNode()->addChild(mRootSceneNode); + else if (!visible && mVisible) + mSceneMgr->getRootSceneNode()->removeChild(mRootSceneNode); + mVisible = visible; - mRootNode->setVisible(visible); } bool Terrain::getVisible() diff --git a/components/terrain/terrain.hpp b/components/terrain/terrain.hpp index 37ea2742a5..4d329f9e19 100644 --- a/components/terrain/terrain.hpp +++ b/components/terrain/terrain.hpp @@ -55,6 +55,8 @@ namespace Terrain Ogre::SceneManager* getSceneManager() { return mSceneMgr; } + Ogre::SceneNode* getRootSceneNode() { return mRootSceneNode; } + Storage* getStorage() { return mStorage; } /// Show or hide the whole terrain @@ -83,6 +85,7 @@ namespace Terrain bool mVisible; QuadTreeNode* mRootNode; + Ogre::SceneNode* mRootSceneNode; Storage* mStorage; int mVisibilityFlags;