From ce5ea6d7d20930d7425deb25038c83f2ba42a751 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Aug 2013 16:42:24 +0200 Subject: [PATCH 1/5] Use a proper node hierarchy; disconnect the root when entering interior --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- components/terrain/quadtreenode.cpp | 75 +++++++++++++++-------- components/terrain/quadtreenode.hpp | 13 ++-- components/terrain/terrain.cpp | 11 +++- components/terrain/terrain.hpp | 3 + 5 files changed, 71 insertions(+), 33 deletions(-) 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; From 43313437dc98d0732c8f44dbdb145f4dc6644673 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Aug 2013 15:19:11 +0200 Subject: [PATCH 2/5] Fix composite map for cells without land data --- components/terrain/quadtreenode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index ed6877f503..ed56738770 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -423,7 +423,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) std::vector layer; layer.push_back("_land_default.dds"); matGen.setLayerList(layer); - makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generate(Ogre::MaterialPtr())); + makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT(Ogre::MaterialPtr())); return; } if (mSize > 1) From 5f7e6f7b104da9ba53e0061db9970958fef53129 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Aug 2013 16:01:32 +0200 Subject: [PATCH 3/5] Fix a material issue, layers per pass wasn't entirely correct --- components/terrain/material.cpp | 98 ++++++++++++++++----------------- components/terrain/material.hpp | 4 -- files/materials/terrain.shader | 4 +- 3 files changed, 51 insertions(+), 55 deletions(-) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 4a91ad99a6..ebf6046ff6 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -40,29 +40,6 @@ namespace Terrain } - int MaterialGenerator::getMaxLayersPerPass () - { - // count the texture units free - Ogre::uint8 freeTextureUnits = 16; - - // first layer doesn't need blendmap - --freeTextureUnits; - - if (mSplitShadows) - freeTextureUnits -= 3; - else if (mShadows) - --freeTextureUnits; - - // each layer needs 1.25 units (1xdiffusespec, 0.25xblend) - return static_cast(freeTextureUnits / (1.25f)) + 1; - } - - int MaterialGenerator::getRequiredPasses () - { - int maxLayersPerPass = getMaxLayersPerPass(); - return std::max(1.f, std::ceil(static_cast(mLayerList.size()) / maxLayersPerPass)); - } - Ogre::MaterialPtr MaterialGenerator::generate(Ogre::MaterialPtr mat) { return create(mat, false, false); @@ -201,45 +178,64 @@ namespace Terrain else { - int numPasses = getRequiredPasses(); - assert(numPasses); - int maxLayersInOnePass = getMaxLayersPerPass(); + bool shadows = mShadows && !renderCompositeMap; - for (int pass=0; pass blendTextures; + int remainingTextureUnits = OGRE_MAX_TEXTURE_LAYERS; + if (shadows) + remainingTextureUnits -= (mSplitShadows ? 3 : 1); + while (remainingTextureUnits && layerOffset + numLayersInThisPass < (int)mLayerList.size()) + { + int layerIndex = numLayersInThisPass + layerOffset; + + int neededTextureUnits=0; + int neededBlendTextures=0; + + if (layerIndex != 0) + { + std::string blendTextureName = mBlendmapList[getBlendmapIndexForLayer(layerIndex)]->getName(); + if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end()) + { + blendTextures.push_back(blendTextureName); + ++neededBlendTextures; + ++neededTextureUnits; // blend texture + } + } + ++neededTextureUnits; // layer texture + if (neededTextureUnits <= remainingTextureUnits) + { + // We can fit another! + remainingTextureUnits -= neededTextureUnits; + numBlendTextures += neededBlendTextures; + ++numLayersInThisPass; + } + else + break; // We're full + } + sh::MaterialInstancePass* p = material->createPass (); p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); - if (pass != 0) + if (layerOffset != 0) { p->setProperty ("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); // Only write if depth is equal to the depth value written by the previous pass. p->setProperty ("depth_func", sh::makeProperty(new sh::StringValue("equal"))); } - p->mShaderProperties.setProperty ("is_first_pass", sh::makeProperty(new sh::BooleanValue(pass == 0))); p->mShaderProperties.setProperty ("render_composite_map", sh::makeProperty(new sh::BooleanValue(renderCompositeMap))); p->mShaderProperties.setProperty ("display_composite_map", sh::makeProperty(new sh::BooleanValue(displayCompositeMap))); - Ogre::uint numLayersInThisPass = std::min(maxLayersInOnePass, (int)mLayerList.size()-layerOffset); - - // a blend map might be shared between two passes - Ogre::uint numBlendTextures=0; - std::vector blendTextures; - for (unsigned int layer=blendmapOffset; layergetName(); - if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end()) - { - blendTextures.push_back(blendTextureName); - ++numBlendTextures; - } - } - p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numLayersInThisPass)))); p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); @@ -250,7 +246,7 @@ namespace Terrain blendmapStart = 0; else blendmapStart = getBlendmapIndexForLayer(layerOffset+blendmapOffset); - for (Ogre::uint i = 0; i < numBlendTextures; ++i) + for (int i = 0; i < numBlendTextures; ++i) { sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(mBlendmapList[blendmapStart+i]->getName()))); @@ -258,7 +254,7 @@ namespace Terrain } // layer maps - for (Ogre::uint i = 0; i < numLayersInThisPass; ++i) + for (int i = 0; i < numLayersInThisPass; ++i) { sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue("textures\\"+mLayerList[layerOffset+i]))); @@ -280,7 +276,7 @@ namespace Terrain } // shadow - if (mShadows) + if (shadows) { for (Ogre::uint i = 0; i < (mSplitShadows ? 3 : 1); ++i) { @@ -292,7 +288,9 @@ namespace Terrain Ogre::StringConverter::toString(numBlendTextures + numLayersInThisPass)))); // Make sure the pass index is fed to the permutation handler, because blendmap components may be different - p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(pass))); + p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(layerOffset))); + + layerOffset += numLayersInThisPass; } } } diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index 2788b79c49..330ed3d147 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -43,10 +43,6 @@ namespace Terrain private: Ogre::MaterialPtr create (Ogre::MaterialPtr mat, bool renderCompositeMap, bool displayCompositeMap); - int getRequiredPasses (); - int getMaxLayersPerPass (); - - int mNumLayers; std::vector mLayerList; std::vector mBlendmapList; std::string mCompositeMap; diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 2bff6d58ff..861841a84c 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -39,7 +39,6 @@ #if LIGHTING @shAllocatePassthrough(3, lightResult) @shAllocatePassthrough(3, directionalResult) -#endif #if SHADOWS @shAllocatePassthrough(4, lightSpacePos0) @@ -49,6 +48,7 @@ @shAllocatePassthrough(4, lightSpacePos@shIterator) @shEndForeach #endif +#endif #ifdef SH_VERTEX_SHADER @@ -200,6 +200,7 @@ @shPassthroughFragmentInputs +#if LIGHTING #if SHADOWS shSampler2D(shadowMap0) shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, @shPropertyString(shadowtexture_offset)) @@ -215,6 +216,7 @@ #if SHADOWS || SHADOWS_PSSM shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) #endif +#endif #if (UNDERWATER) || (FOG) shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) From 758d989a036d613ae1e33206a999aa59ad568ab7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Aug 2013 17:24:24 +0200 Subject: [PATCH 4/5] If multiple plugins have land data for the same cell, the last plugin should win --- apps/openmw/mwworld/store.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 2c10e3edc8..2ee23dbd61 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -466,6 +466,18 @@ namespace MWWorld ESM::Land *ptr = new ESM::Land(); ptr->load(esm); + // Same area defined in multiple plugins? -> last plugin wins + // Can't use search() because we aren't sorted yet - is there any other way to speed this up? + for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) + { + if ((*it)->mX == ptr->mX && (*it)->mY == ptr->mY) + { + delete *it; + mStatic.erase(it); + break; + } + } + mStatic.push_back(ptr); } From d086346b0747845c3243a61a9daa22afd7b41b03 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Aug 2013 17:25:29 +0200 Subject: [PATCH 5/5] Fix loading of some cells in TR --- components/esm/loadcell.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index dbd1fed6f0..d8d0c12912 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -153,6 +153,14 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // That should be it, I haven't seen any other fields yet. } + // NAM0 sometimes appears here, sometimes further on + ref.mNam0 = 0; + if (esm.isNextSub("NAM0")) + { + esm.getHT(ref.mNam0); + //esm.getHNOT(NAM0, "NAM0"); + } + esm.getHNT(ref.mRefnum, "FRMR"); ref.mRefID = esm.getHNString("NAME"); @@ -243,7 +251,6 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // Update: Well, maybe not completely useless. This might actually be // number_of_references + number_of_references_moved_here_Across_boundaries, // and could be helpful for collecting these weird moved references. - ref.mNam0 = 0; if (esm.isNextSub("NAM0")) { esm.getHT(ref.mNam0);