From ed20d869b4e8a8283bd726b82da2ca6a074c8afc Mon Sep 17 00:00:00 2001 From: bzzt Date: Thu, 13 Jun 2019 13:37:00 +0000 Subject: [PATCH] waterculling for both terrain --- apps/openmw/mwrender/renderingmanager.cpp | 8 ++++-- apps/openmw/mwrender/water.cpp | 28 ++++++++++++++++++ apps/openmw/mwrender/water.hpp | 4 +++ components/terrain/quadtreeworld.cpp | 35 +++++++++++++++++++++++ components/terrain/terraingrid.cpp | 11 +++++++ components/terrain/terraingrid.hpp | 3 +- components/terrain/world.cpp | 8 ++++++ components/terrain/world.hpp | 35 +++++++++++++++++++++++ 8 files changed, 129 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 31af55a443..c966a5d67a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -282,8 +282,6 @@ namespace MWRender mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem)); - mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); - DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); @@ -322,6 +320,9 @@ namespace MWRender mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mTerrain->setWorkQueue(mWorkQueue.get()); + // water goes after terrain for correct waterculling order + mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); + mCamera.reset(new Camera(mViewer->getCamera())); mViewer->setLightingMode(osgViewer::View::NO_LIGHT); @@ -541,6 +542,8 @@ namespace MWRender void RenderingManager::enableTerrain(bool enable) { + if (!enable) + mWater->setCullCallback(nullptr); mTerrain->enable(enable); } @@ -740,6 +743,7 @@ namespace MWRender void RenderingManager::setWaterHeight(float height) { + mWater->setCullCallback(mTerrain->getHeightCullCallback(height, Mask_Water)); mWater->setHeight(height); mSky->setWaterHeight(height); } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 6d230d36e2..c9d16b7280 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -436,6 +436,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem , mToggled(true) , mTop(0) , mInterior(false) + , mCullCallback(nullptr) { mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem)); @@ -466,6 +467,29 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem ico->add(mWaterNode); } +void Water::setCullCallback(osg::Callback* callback) +{ + if (mCullCallback) + { + mWaterNode->removeCullCallback(mCullCallback); + if (mReflection) + mReflection->removeCullCallback(mCullCallback); + if (mRefraction) + mRefraction->removeCullCallback(mCullCallback); + } + + mCullCallback = callback; + + if (callback) + { + mWaterNode->addCullCallback(callback); + if (mReflection) + mReflection->addCullCallback(callback); + if (mRefraction) + mRefraction->addCullCallback(callback); + } +} + osg::Uniform *Water::getRainIntensityUniform() { return mRainIntensityUniform.get(); @@ -491,6 +515,8 @@ void Water::updateWaterMaterial() mReflection = new Reflection(mInterior); mReflection->setWaterLevel(mTop); mReflection->setScene(mSceneRoot); + if (mCullCallback) + mReflection->addCullCallback(mCullCallback); mParent->addChild(mReflection); if (Settings::Manager::getBool("refraction", "Water")) @@ -498,6 +524,8 @@ void Water::updateWaterMaterial() mRefraction = new Refraction; mRefraction->setWaterLevel(mTop); mRefraction->setScene(mSceneRoot); + if (mCullCallback) + mRefraction->addCullCallback(mCullCallback); mParent->addChild(mRefraction); } diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 5d99413c68..3787ef4268 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -71,6 +71,8 @@ namespace MWRender float mTop; bool mInterior; + osg::Callback* mCullCallback; + osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY); void updateVisible(); @@ -88,6 +90,8 @@ namespace MWRender const std::string& resourcePath); ~Water(); + void setCullCallback(osg::Callback* callback); + void listAssetsToPreload(std::vector& textures); void setEnabled(bool enabled); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 396ac11383..1d968ddc49 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -307,6 +307,38 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags); } +void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld) +{ + if (!(cv->getTraversalMask() & callback->getCullMask())) + return; + float lowZ = FLT_MAX; + float highZ = callback->getHighZ(); + if (cv->getEyePoint().z() <= highZ || outofworld) + { + callback->setLowZ(-FLT_MAX); + return; + } + cv->pushCurrentMask(); + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + osg::BoundingBox bb = static_cast(entry.mRenderingNode->asGroup()->getChild(0))->getBoundingBox(); + float minZ = bb._min.z(); + if (minZ > highZ) + continue; + osg::Vec3f ofs (entry.mNode->getCenter().x()*cellworldsize, entry.mNode->getCenter().y()*cellworldsize, 0.f); + bb._min += ofs; bb._max += ofs; + bb._min.z() = highZ; + bb._max.z() = highZ; + if (cv->isCulled(bb)) + continue; + lowZ = minZ; + break; + } + callback->setLowZ(lowZ); + cv->popCurrentMask(); +} + void QuadTreeWorld::accept(osg::NodeVisitor &nv) { bool isCullVisitor = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR; @@ -384,6 +416,9 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) entry.mRenderingNode->accept(nv); } + if (isCullVisitor) + updateWaterCullingView(mHeightCullCallback, vd, static_cast(&nv), mStorage->getCellWorldSize(), !mGrid.empty()); + if (!isCullVisitor) vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 7310846c21..a0e5e4718b 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "chunkmanager.hpp" #include "compositemaprenderer.hpp" @@ -80,6 +81,7 @@ void TerrainGrid::loadCell(int x, int y) mTerrainRoot->addChild(terrainNode); mGrid[std::make_pair(x,y)] = terrainNode; + updateWaterCulling(); } void TerrainGrid::unloadCell(int x, int y) @@ -94,6 +96,15 @@ void TerrainGrid::unloadCell(int x, int y) mTerrainRoot->removeChild(terrainNode); mGrid.erase(it); + updateWaterCulling(); +} + +void TerrainGrid::updateWaterCulling() +{ + osg::ComputeBoundsVisitor computeBoundsVisitor; + mTerrainRoot->accept(computeBoundsVisitor); + float lowZ = computeBoundsVisitor.getBoundingBox()._min.z(); + mHeightCullCallback->setLowZ(lowZ); } View *TerrainGrid::createView() diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index eb30fb97d2..e633a258f0 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -27,8 +27,9 @@ namespace Terrain View* createView(); - private: + protected: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); + void updateWaterCulling(); // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks unsigned int mNumSplits; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index da3bdb5c22..2d53f40908 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -19,6 +19,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst , mParent(parent) , mResourceSystem(resourceSystem) , mBorderVisible(false) + , mHeightCullCallback(new HeightCullCallback) { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); @@ -120,4 +121,11 @@ void World::clearAssociatedCaches() mChunkManager->clearCache(); } +osg::Callback* World::getHeightCullCallback(float highz, unsigned int mask) +{ + mHeightCullCallback->setHighZ(highz); + mHeightCullCallback->setCullMask(mask); + return mHeightCullCallback; +} + } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 0402b8197f..92eb69b80b 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,37 @@ namespace Terrain class ChunkManager; class CompositeMapRenderer; +class HeightCullCallback : public osg::NodeCallback +{ +public: + HeightCullCallback() : mLowZ(-FLT_MAX), mHighZ(FLT_MAX), mMask(~0) {} + + void setLowZ(float z) + { + mLowZ = z; + } + float getLowZ() const { return mLowZ; } + + void setHighZ(float highZ) + { + mHighZ = highZ; + } + float getHighZ() const { return mHighZ; } + + void setCullMask(unsigned int mask) { mMask = mask; } + unsigned int getCullMask() const { return mMask; } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (mLowZ <= mHighZ) + traverse(node, nv); + } +private: + float mLowZ; + float mHighZ; + unsigned int mMask; +}; + /** * @brief A View is a collection of rendering objects that are visible from a given camera/intersection. * The base View class is part of the interface for usage in conjunction with preload feature. @@ -116,6 +148,8 @@ namespace Terrain Storage* getStorage() { return mStorage; } + osg::Callback* getHeightCullCallback(float highz, unsigned int mask); + protected: Storage* mStorage; @@ -135,6 +169,7 @@ namespace Terrain bool mBorderVisible; std::set> mLoadedCells; + osg::ref_ptr mHeightCullCallback; }; }