From d684f1a78f165ad51643cceef49626da2810709f Mon Sep 17 00:00:00 2001 From: bzzt Date: Thu, 13 Jun 2019 13:37:00 +0000 Subject: [PATCH 01/31] terrainbased objectpaging Signed-off-by: Bret Curtis --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/objectpaging.cpp | 302 ++++++++++++++++++++++ apps/openmw/mwrender/objectpaging.hpp | 44 ++++ apps/openmw/mwrender/renderingmanager.cpp | 13 + apps/openmw/mwrender/renderingmanager.hpp | 4 + apps/openmw/mwworld/cellpreloader.cpp | 8 +- apps/openmw/mwworld/cellpreloader.hpp | 6 +- apps/openmw/mwworld/esmstore.cpp | 4 + apps/openmw/mwworld/esmstore.hpp | 10 + apps/openmw/mwworld/scene.cpp | 80 +++--- apps/openmw/mwworld/scene.hpp | 13 +- apps/openmw/mwworld/store.cpp | 25 ++ apps/openmw/mwworld/store.hpp | 2 + components/resource/scenemanager.cpp | 11 +- components/resource/scenemanager.hpp | 2 +- components/sceneutil/optimizer.cpp | 92 ++++--- components/sceneutil/optimizer.hpp | 27 +- components/sceneutil/riggeometry.cpp | 2 +- components/sceneutil/riggeometry.hpp | 2 +- components/terrain/chunkmanager.cpp | 14 +- components/terrain/chunkmanager.hpp | 12 +- components/terrain/quadtreeworld.cpp | 53 +++- components/terrain/quadtreeworld.hpp | 13 +- components/terrain/terraingrid.cpp | 14 +- components/terrain/viewdata.cpp | 12 +- components/terrain/viewdata.hpp | 6 +- components/terrain/world.cpp | 2 +- components/terrain/world.hpp | 6 +- files/settings-default.cfg | 9 + 29 files changed, 648 insertions(+), 142 deletions(-) create mode 100644 apps/openmw/mwrender/objectpaging.cpp create mode 100644 apps/openmw/mwrender/objectpaging.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 31ac64e9ec..a9f3d24e29 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager + renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp new file mode 100644 index 0000000000..6453889bcf --- /dev/null +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -0,0 +1,302 @@ +#include "objectpaging.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "apps/openmw/mwworld/esmstore.hpp" +#include "apps/openmw/mwbase/environment.hpp" +#include "apps/openmw/mwbase/world.hpp" + +#include "vismask.hpp" + +namespace MWRender +{ + + bool typeFilter(int type, bool far) + { + switch (type) + { + case ESM::REC_STAT: + case ESM::REC_ACTI: + case ESM::REC_DOOR: + return true; + case ESM::REC_CONT: + return far ? false : true; + default: + return false; + } + } + + const std::string& getModel(int type, const std::string& id, const MWWorld::ESMStore& store) + { + switch (type) + { + case ESM::REC_STAT: + return store.get().searchStatic(id)->mModel; + case ESM::REC_ACTI: + return store.get().searchStatic(id)->mModel; + case ESM::REC_DOOR: + return store.get().searchStatic(id)->mModel; + case ESM::REC_CONT: + return store.get().searchStatic(id)->mModel; + default: throw std::exception(); + } + } + + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) + { + if (!far)return nullptr; + ChunkId id = std::make_tuple(center, size); + + osg::ref_ptr obj = mCache->getRefFromObjectCache(id); + if (obj) + return obj->asNode(); + else + { + osg::ref_ptr node = createChunk(size, center, viewPoint); + mCache->addEntryToObjectCache(id, node.get()); + return node; + } + } + + class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback + { + public: + virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const + { + return true; + } + virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const + { + return true; + } + }; + + class CopyOp : public osg::CopyOp + { + public: + CopyOp() : mDistance(0.f) { + setCopyFlags(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES + #if OSG_MIN_VERSION_REQUIRED(3,5,6) + |osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES // damned vbogarbage racing + #endif + ); + } + + float mDistance; + + virtual osg::Node* operator() (const osg::Node* node) const + { + if (dynamic_cast(node)) + return nullptr; + if (dynamic_cast(node)) + return nullptr; + + if (const osg::Switch* sw = node->asSwitch()) + { + osg::Group* n = new osg::Group; + for (unsigned int i=0; igetNumChildren(); ++i) + if (sw->getValue(i)) + n->addChild(operator()(sw->getChild(i))); + n->setDataVariance(osg::Object::STATIC); + return n; + } + if (const osg::LOD* lod = dynamic_cast(node)) + { + osg::Group* n = new osg::Group; + for (unsigned int i=0; igetNumChildren(); ++i) + if (lod->getMinRange(i) <= mDistance && mDistance < lod->getMaxRange(i)) + n->addChild(operator()(lod->getChild(i))); + n->setDataVariance(osg::Object::STATIC); + return n; + } + + osg::Node* n = osg::CopyOp::operator()(node); + if (n) { + n->setDataVariance(osg::Object::STATIC); + n->setUserDataContainer(nullptr); + n->setName(""); + } + return n; + } + virtual osg::Drawable* operator() (const osg::Drawable* drawable) const + { + if (dynamic_cast(drawable)) + return nullptr; + + if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) + return osg::CopyOp::operator()(rig->getSourceGeometry()); + if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) + return osg::CopyOp::operator()(morph->getSourceGeometry()); + + return osg::CopyOp::operator()(drawable); + } + virtual osg::Callback* operator() (const osg::Callback* callback) const + { + return nullptr; + } + }; + + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) + : GenericResourceManager(nullptr) + , mSceneManager(sceneManager) + { + mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain"); + mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); + } + + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint) + { + osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); + + osg::ref_ptr group = new osg::Group; + + osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; + osg::Vec3f relativeViewPoint = viewPoint - worldCenter; + + std::vector refs; + std::vector esm; + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX) + { + for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) + { + const ESM::Cell* cell = store.get().searchStatic(cellX, cellY); + if (!cell) continue; + for (size_t i=0; imContextList.size(); ++i) + { + try + { + unsigned int index = cell->mContextList.at(i).index; + if (esm.size()<=index) + esm.resize(index+1); + cell->restore(esm[index], i); + ESM::CellRef ref; + ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; + bool deleted = false; + while(cell->getNextRef(esm[index], ref, deleted)) + { + if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; + int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + if (!typeFilter(type,size>=2)) continue; + if (deleted) continue; + refs.push_back(ref); + } + } + catch (std::exception& e) + { + continue; + } + } + for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) + { + ESM::CellRef ref = it->first; + bool deleted = it->second; + if (deleted) continue; + int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + if (!typeFilter(type,size>=2)) continue; + refs.push_back(ref); + } + } + } + + osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); + osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); + for (const ESM::CellRef& ref : refs) + { + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + + int type = store.findStatic(id); + std::string model = "meshes/" + getModel(type, id, store); +/* + bool useAnim = type != ESM::REC_STAT; + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); +*/ + if (model.empty()) continue; + + osg::Vec3f pos = ref.mPos.asVec3(); + if (size < 1.f) + { + osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; + cellPos.x() = std::max(cellPos.x(), std::floor(minBound.x())); + cellPos.x() = std::min(cellPos.x(), std::ceil(maxBound.x())); + cellPos.y() = std::max(cellPos.y(), std::floor(minBound.y())); + cellPos.y() = std::min(cellPos.y(), std::ceil(maxBound.y())); + if (cellPos.x() < minBound.x() || cellPos.x() > maxBound.x() || cellPos.y() < minBound.y() || cellPos.y() > maxBound.y()) + continue; + } + + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + + float d = (viewPoint - pos).length(); + + if (cnode->getBound().radius() * ref.mScale < d*mMinSize) + continue; + + CopyOp co = CopyOp(); + co.mDistance = d; + osg::ref_ptr node = osg::clone(cnode.get(), co); + node->setUserDataContainer(nullptr); + + osg::Matrixf matrix; + matrix.preMultTranslate(pos - worldCenter); + matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * + osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); + matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); + osg::ref_ptr trans = new osg::MatrixTransform(matrix); + trans->addChild(node); + trans->setDataVariance(osg::Object::STATIC); + + group->addChild(trans); + } + + if (mMergeGeometry) + { + SceneUtil::Optimizer optimizer; + if ((relativeViewPoint - group->getBound().center()).length2() > group->getBound().radius2()) + { + optimizer.setViewPoint(relativeViewPoint); + optimizer.setMergeAlphaBlending(true); + } + optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); + unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; + optimizer.optimize(group, options); + } + + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (ico) ico->add(group); + else group->getBound(); + + group->setNodeMask(Mask_Static); + + return group; + } + + unsigned int ObjectPaging::getNodeMask() + { + return Mask_Static; + } + +} diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp new file mode 100644 index 0000000000..6d66d078df --- /dev/null +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -0,0 +1,44 @@ +#ifndef OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H +#define OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H + +#include +#include +#include + +namespace Resource +{ + class SceneManager; +} +namespace MWWorld +{ + class ESMStore; +} + +namespace MWRender +{ + + typedef std::tuple ChunkId; // Center, Size + + class ObjectPaging : public Resource::GenericResourceManager, public Terrain::QuadTreeWorld::ChunkManager + { + public: + ObjectPaging(Resource::SceneManager* sceneManager); + ~ObjectPaging() = default; + + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) override; + + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint); + + virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } + + virtual unsigned int getNodeMask() override; + + private: + Resource::SceneManager* mSceneManager; + bool mMergeGeometry; + float mMinSize; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 462f9fbb6e..9c92da767a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -70,6 +70,8 @@ #include "actorspaths.hpp" #include "recastmesh.hpp" #include "fogmanager.hpp" +#include "objectpaging.hpp" + namespace MWRender { @@ -286,6 +288,12 @@ namespace MWRender mTerrain.reset(new Terrain::QuadTreeWorld( sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); + if (Settings::Manager::getBool("object paging", "Terrain")) + { + mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager())); + static_cast(mTerrain.get())->addChunkManager(mObjectPaging.get()); + mResourceSystem->addResourceManager(mObjectPaging.get()); + } } else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); @@ -1467,4 +1475,9 @@ namespace MWRender mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings()); } + + void RenderingManager::setActiveGrid(const osg::Vec4i &grid) + { + mTerrain->setActiveGrid(grid); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 13f09b359e..dff76f95ec 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -84,6 +84,7 @@ namespace MWRender class NavMesh; class ActorsPaths; class RecastMesh; + class ObjectPaging; class RenderingManager : public MWRender::RenderingInterface { @@ -237,6 +238,8 @@ namespace MWRender void setNavMeshNumber(const std::size_t value); + void setActiveGrid(const osg::Vec4i &grid); + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -275,6 +278,7 @@ namespace MWRender std::unique_ptr mWater; std::unique_ptr mTerrain; TerrainStorage* mTerrainStorage; + std::unique_ptr mObjectPaging; std::unique_ptr mSky; std::unique_ptr mFog; std::unique_ptr mEffectManager; diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 9a96e98060..60b9a3220e 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -172,7 +172,7 @@ namespace MWWorld class TerrainPreloadItem : public SceneUtil::WorkItem { public: - TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) + TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) , mTerrainViews(views) , mWorld(world) @@ -191,7 +191,7 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort); } } @@ -204,7 +204,7 @@ namespace MWWorld std::atomic mAbort; std::vector > mTerrainViews; Terrain::World* mWorld; - std::vector mPreloadPositions; + std::vector mPreloadPositions; }; /// Worker thread item: update the resource system's cache, effectively deleting unused entries. @@ -415,7 +415,7 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) return; diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 0501a4f9be..ef28170190 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace Resource @@ -68,7 +69,8 @@ namespace MWWorld void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue); - void setTerrainPreloadPositions(const std::vector& positions); + typedef std::pair PositionCellGrid; + void setTerrainPreloadPositions(const std::vector& positions); private: Resource::ResourceSystem* mResourceSystem; @@ -105,7 +107,7 @@ namespace MWWorld PreloadMap mPreloadCells; std::vector > mTerrainViews; - std::vector mTerrainPreloadPositions; + std::vector mTerrainPreloadPositions; osg::ref_ptr mTerrainPreloadItem; osg::ref_ptr mUpdateCacheItem; }; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index f862266404..278b8532e9 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -136,6 +136,10 @@ void ESMStore::setUp(bool validateRecords) mIds[*record] = storeIt->first; } } + + if (mStaticIds.empty()) + mStaticIds = mIds; + mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index fe1cfc7087..24364bfb13 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -68,6 +68,8 @@ namespace MWWorld // Lookup of all IDs. Makes looking up references faster. Just // maps the id name to the record type. std::map mIds; + std::map mStaticIds; + std::map mStores; ESM::NPC mPlayerTemplate; @@ -99,6 +101,14 @@ namespace MWWorld } return it->second; } + int findStatic(const std::string &id) const + { + std::map::const_iterator it = mStaticIds.find(id); + if (it == mStaticIds.end()) { + return 0; + } + return it->second; + } ESMStore() : mDynamicCount(0) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1e1d39bec9..127b56c94f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -286,28 +286,6 @@ namespace MWWorld ::updateObjectScale(ptr, *mPhysics, mRendering); } - void Scene::getGridCenter(int &cellX, int &cellY) - { - int maxX = std::numeric_limits::min(); - int maxY = std::numeric_limits::min(); - int minX = std::numeric_limits::max(); - int minY = std::numeric_limits::max(); - CellStoreCollection::iterator iter = mActiveCells.begin(); - while (iter!=mActiveCells.end()) - { - assert ((*iter)->getCell()->isExterior()); - int x = (*iter)->getCell()->getGridX(); - int y = (*iter)->getCell()->getGridY(); - maxX = std::max(x, maxX); - maxY = std::max(y, maxY); - minX = std::min(x, minX); - minY = std::min(y, minY); - ++iter; - } - cellX = (minX + maxX) / 2; - cellY = (minY + maxY) / 2; - } - void Scene::update (float duration, bool paused) { mPreloadTimer += duration; @@ -488,6 +466,27 @@ namespace MWWorld mPreloader->clear(); } + osg::Vec4i Scene::gridCenterToBounds(const osg::Vec2i& centerCell) const + { + return osg::Vec4i(centerCell.x()-mHalfGridSize,centerCell.y()-mHalfGridSize,centerCell.x()+mHalfGridSize+1,centerCell.y()+mHalfGridSize+1); + } + + osg::Vec2i Scene::getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i* currentGridCenter) const + { + if (currentGridCenter) + { + float centerX, centerY; + MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); + float distance = std::max(std::abs(centerY-pos.x()), std::abs(centerY-pos.y())); + const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold + if (distance <= maxDistance) + return *currentGridCenter; + } + osg::Vec2i newCenter; + MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newCenter.x(), newCenter.y()); + return newCenter; + } + void Scene::playerMoved(const osg::Vec3f &pos) { const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); @@ -497,19 +496,9 @@ namespace MWWorld if (!mCurrentCell || !mCurrentCell->isExterior()) return; - // figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in) - int cellX, cellY; - getGridCenter(cellX, cellY); - float centerX, centerY; - MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); - const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold - float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); - if (distance > maxDistance) - { - int newX, newY; - MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newX, newY); - changeCellGrid(newX, newY); - } + osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); + if (newCell != mCurrentGridCenter) + changeCellGrid(newCell.x(), newCell.y()); } void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) @@ -612,6 +601,9 @@ namespace MWWorld CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY); MWBase::Environment::get().getWindowManager()->changeCell(current); + mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); + mRendering.setActiveGrid(gridCenterToBounds(mCurrentGridCenter)); + if (changeEvent) mCellChanged = true; } @@ -983,7 +975,7 @@ namespace MWWorld void Scene::preloadCells(float dt) { - std::vector exteriorPositions; + std::vector exteriorPositions; const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); @@ -991,7 +983,7 @@ namespace MWWorld osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime; if (mCurrentCell->isExterior()) - exteriorPositions.push_back(predictedPos); + exteriorPositions.emplace_back(predictedPos, gridCenterToBounds(getNewGridCenter(predictedPos, &mCurrentGridCenter))); mLastPlayerPos = playerPos; @@ -1008,7 +1000,7 @@ namespace MWWorld mPreloader->setTerrainPreloadPositions(exteriorPositions); } - void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions) + void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions) { std::vector teleportDoors; for (const MWWorld::CellStore* cellStore : mActiveCells) @@ -1042,7 +1034,7 @@ namespace MWWorld int x,y; MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); - exteriorPositions.push_back(pos); + exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); } } catch (std::exception& e) @@ -1062,7 +1054,7 @@ namespace MWWorld int cellX,cellY; - getGridCenter(cellX,cellY); + cellX = mCurrentGridCenter.x(); cellY = mCurrentGridCenter.y(); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); @@ -1110,8 +1102,8 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { - std::vector vec; - vec.push_back(pos); + std::vector vec; + vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); mPreloader->setTerrainPreloadPositions(vec); } @@ -1145,7 +1137,7 @@ namespace MWWorld std::vector mList; }; - void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time + void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time { const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); @@ -1166,7 +1158,7 @@ namespace MWWorld int x,y; MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); - exteriorPositions.push_back(pos); + exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); } } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index da795f84b7..9b8403c389 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -1,6 +1,9 @@ #ifndef GAME_MWWORLD_SCENE_H #define GAME_MWWORLD_SCENE_H +#include +#include + #include "ptr.hpp" #include "globals.hpp" @@ -86,16 +89,20 @@ namespace MWWorld osg::Vec3f mLastPlayerPos; void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); + osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true); - void getGridCenter(int& cellX, int& cellY); + typedef std::pair PositionCellGrid; void preloadCells(float dt); - void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); - void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + + osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; + osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; public: diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index a414585efe..93f11d4fd9 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -151,6 +151,19 @@ namespace MWWorld return 0; } + template + const T *Store::searchStatic(const std::string &id) const + { + std::string idLower = Misc::StringUtils::lowerCase(id); + typename std::map::const_iterator it = mStatic.find(idLower); + + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + return &(it->second); + } + + return 0; + } + template bool Store::isDynamic(const std::string &id) const { @@ -582,6 +595,18 @@ namespace MWWorld return 0; } + const ESM::Cell *Store::searchStatic(int x, int y) const + { + ESM::Cell cell; + cell.mData.mX = x, cell.mData.mY = y; + + std::pair key(x, y); + DynamicExt::const_iterator it = mExt.find(key); + if (it != mExt.end()) { + return &(it->second); + } + return 0; + } const ESM::Cell *Store::searchOrCreate(int x, int y) { std::pair key(x, y); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 64c01589e3..f119fa928a 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -167,6 +167,7 @@ namespace MWWorld void setUp(); const T *search(const std::string &id) const; + const T *searchStatic(const std::string &id) const; /** * Does the record with this ID come from the dynamic store? @@ -297,6 +298,7 @@ namespace MWWorld const ESM::Cell *search(const std::string &id) const; const ESM::Cell *search(int x, int y) const; + const ESM::Cell *searchStatic(int x, int y) const; const ESM::Cell *searchOrCreate(int x, int y); const ESM::Cell *find(const std::string &id) const; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 61a40ee4b3..1709a3d149 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -466,7 +466,7 @@ namespace Resource return options; } - osg::ref_ptr SceneManager::getTemplate(const std::string &name) + osg::ref_ptr SceneManager::getTemplate(const std::string &name, bool compile) { std::string normalized = name; mVFS->normalizeFilename(normalized); @@ -529,7 +529,7 @@ namespace Resource optimizer.optimize(loaded, options); } - if (mIncrementalCompileOperation) + if (compile && mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); else loaded->getBound(); @@ -713,6 +713,13 @@ namespace Resource mSharedStateMutex.lock(); mSharedStateManager->prune(); mSharedStateMutex.unlock(); + + if (mIncrementalCompileOperation) + { + OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); + while (mIncrementalCompileOperation->getToCompile().size() > 1000) + mIncrementalCompileOperation->getToCompile().pop_front(); + } } void SceneManager::clearCache() diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 1c1c60a58d..bbe88c9a0e 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -81,7 +81,7 @@ namespace Resource /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// If even the error marker mesh can not be found, an exception is thrown. /// @note Thread safe. - osg::ref_ptr getTemplate(const std::string& name); + osg::ref_ptr getTemplate(const std::string& name, bool compile=true); /// Create an instance of the given scene template and cache it for later use, so that future calls to getInstance() can simply /// return this cached object instead of creating a new one. diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 487126627c..7bcccdbe33 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -103,7 +103,9 @@ void Optimizer::optimize(osg::Node* node, unsigned int options) osg::Timer_t startTick = osg::Timer::instance()->tick(); MergeGeometryVisitor mgv(this); - mgv.setTargetMaximumNumberOfVertices(10000); + mgv.setTargetMaximumNumberOfVertices(1000000); + mgv.setMergeAlphaBlending(_mergeAlphaBlending); + mgv.setViewPoint(_viewPoint); node->accept(mgv); osg::Timer_t endTick = osg::Timer::instance()->tick(); @@ -988,6 +990,17 @@ struct LessGeometry } }; +struct LessGeometryViewPoint +{ + osg::Vec3f _viewPoint; + bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const + { + float len1 = (lhs->getBoundingBox().center() - _viewPoint).length2(); + float len2 = (rhs->getBoundingBox().center() - _viewPoint).length2(); + return len2 < len1; + } +}; + struct LessGeometryPrimitiveType { bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) const @@ -1055,16 +1068,16 @@ bool isAbleToMerge(const osg::Geometry& g1, const osg::Geometry& g2) void Optimizer::MergeGeometryVisitor::pushStateSet(osg::StateSet *stateSet) { _stateSetStack.push_back(stateSet); - checkAllowedToMerge(); + checkAlphaBlendingActive(); } void Optimizer::MergeGeometryVisitor::popStateSet() { _stateSetStack.pop_back(); - checkAllowedToMerge(); + checkAlphaBlendingActive(); } -void Optimizer::MergeGeometryVisitor::checkAllowedToMerge() +void Optimizer::MergeGeometryVisitor::checkAlphaBlendingActive() { int renderingHint = 0; bool override = false; @@ -1080,7 +1093,7 @@ void Optimizer::MergeGeometryVisitor::checkAllowedToMerge() override = true; } // Can't merge Geometry that are using a transparent sorting bin as that would cause the sorting to break. - _allowedToMerge = renderingHint != osg::StateSet::TRANSPARENT_BIN; + _alphaBlendingActive = renderingHint == osg::StateSet::TRANSPARENT_BIN; } void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) @@ -1088,7 +1101,7 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) if (group.getStateSet()) pushStateSet(group.getStateSet()); - if (_allowedToMerge) + if (!_alphaBlendingActive || _mergeAlphaBlending) mergeGroup(group); traverse(group); @@ -1097,6 +1110,14 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) popStateSet(); } +osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps) +{ + if (ps->referenceCount() <= 1) + return ps; + ps = dynamic_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); + return ps; +} + bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (!isOperationPermissibleForObject(&group)) return false; @@ -1120,7 +1141,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) osg::Geometry* geom = child->asGeometry(); if (geom) { - if (!geometryContainsSharedArrays(*geom) && + if ( geom->getDataVariance()!=osg::Object::DYNAMIC && isOperationPermissibleForObject(geom)) { @@ -1254,6 +1275,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) DuplicateList& duplicateList = *mitr; if (!duplicateList.empty()) { + if (_alphaBlendingActive) + { + LessGeometryViewPoint lgvp; + lgvp._viewPoint = _viewPoint; + std::sort(duplicateList.begin(), duplicateList.end(), lgvp); + } DuplicateList::iterator ditr = duplicateList.begin(); osg::ref_ptr lhs = *ditr++; group.addChild(lhs.get()); @@ -1290,10 +1317,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (prim->getNumIndices()==3) { + prim = clonePrimitive(prim); (*itr) = prim; prim->setMode(osg::PrimitiveSet::TRIANGLES); } else if (prim->getNumIndices()==4) { + prim = clonePrimitive(prim); (*itr) = prim; prim->setMode(osg::PrimitiveSet::QUADS); } } @@ -1320,6 +1349,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) #if 1 bool doneCombine = false; + std::set toremove; + osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList(); unsigned int lhsNo=0; unsigned int rhsNo=1; @@ -1348,6 +1379,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { + lhs = clonePrimitive(lhs); + primitives[lhsNo] = lhs; switch(lhs->getType()) { @@ -1375,7 +1408,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { // make this primitive set as invalid and needing cleaning up. - rhs->setMode(0xffffff); + toremove.insert(rhs); doneCombine = true; ++rhsNo; } @@ -1390,7 +1423,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (doneCombine) { // now need to clean up primitiveset so it no longer contains the rhs combined primitives. - // first swap with a empty primitiveSet to empty it completely. osg::Geometry::PrimitiveSetList oldPrimitives; primitives.swap(oldPrimitives); @@ -1400,7 +1432,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) pitr != oldPrimitives.end(); ++pitr) { - if ((*pitr)->getMode()!=0xffffff) primitives.push_back(*pitr); + if (!toremove.count(*pitr)) primitives.push_back(*pitr); } } #endif @@ -1479,34 +1511,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) return false; } -bool Optimizer::MergeGeometryVisitor::geometryContainsSharedArrays(osg::Geometry& geom) -{ - if (geom.getVertexArray() && geom.getVertexArray()->referenceCount()>1) return true; - if (geom.getNormalArray() && geom.getNormalArray()->referenceCount()>1) return true; - if (geom.getColorArray() && geom.getColorArray()->referenceCount()>1) return true; - if (geom.getSecondaryColorArray() && geom.getSecondaryColorArray()->referenceCount()>1) return true; - if (geom.getFogCoordArray() && geom.getFogCoordArray()->referenceCount()>1) return true; - - - for(unsigned int unit=0;unitreferenceCount()>1) return true; - } - - // shift the indices of the incoming primitives to account for the pre existing geometry. - for(osg::Geometry::PrimitiveSetList::iterator primItr=geom.getPrimitiveSetList().begin(); - primItr!=geom.getPrimitiveSetList().end(); - ++primItr) - { - if ((*primItr)->referenceCount()>1) return true; - } - - - return false; -} - - class MergeArrayVisitor : public osg::ArrayVisitor { protected: @@ -1574,6 +1578,8 @@ class MergeArrayVisitor : public osg::ArrayVisitor bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs) { + if (lhs.containsSharedArrays()) + lhs.duplicateSharedArrays(); MergeArrayVisitor merger; @@ -1661,7 +1667,6 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } } - // shift the indices of the incoming primitives to account for the pre existing geometry. osg::Geometry::PrimitiveSetList::iterator primItr; for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr) @@ -1697,7 +1702,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } else { - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); } } break; @@ -1722,7 +1728,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom } else { - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); } } break; @@ -1731,7 +1738,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType): case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): default: - primitive->offsetIndices(base); + (*primItr) = clonePrimitive(primitive); + (*primItr)->offsetIndices(base); break; } } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 6dd4394d18..d7c83e898b 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -65,7 +65,7 @@ class Optimizer public: - Optimizer() {} + Optimizer() : _mergeAlphaBlending(false) {} virtual ~Optimizer() {} enum OptimizationOptions @@ -118,6 +118,9 @@ class Optimizer STATIC_OBJECT_DETECTION }; + void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } + void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; } + /** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/ void reset(); @@ -252,6 +255,9 @@ class Optimizer typedef std::map PermissibleOptimizationsMap; PermissibleOptimizationsMap _permissibleOptimizationsMap; + osg::Vec3f _viewPoint; + bool _mergeAlphaBlending; + public: /** Flatten Static Transform nodes by applying their transform to the @@ -371,7 +377,16 @@ class Optimizer /// default to traversing all children. MergeGeometryVisitor(Optimizer* optimizer=0) : BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY), - _targetMaximumNumberOfVertices(10000), _allowedToMerge(true) {} + _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false) {} + + void setMergeAlphaBlending(bool merge) + { + _mergeAlphaBlending = merge; + } + void setViewPoint(const osg::Vec3f& viewPoint) + { + _viewPoint = viewPoint; + } void setTargetMaximumNumberOfVertices(unsigned int num) { @@ -385,15 +400,13 @@ class Optimizer void pushStateSet(osg::StateSet* stateSet); void popStateSet(); - void checkAllowedToMerge(); + void checkAlphaBlendingActive(); virtual void apply(osg::Group& group); virtual void apply(osg::Billboard&) { /* don't do anything*/ } bool mergeGroup(osg::Group& group); - static bool geometryContainsSharedArrays(osg::Geometry& geom); - static bool mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs); static bool mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs); @@ -406,7 +419,9 @@ class Optimizer unsigned int _targetMaximumNumberOfVertices; std::vector _stateSetStack; - bool _allowedToMerge; + bool _alphaBlendingActive; + bool _mergeAlphaBlending; + osg::Vec3f _viewPoint; }; }; diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 97c8e1d391..627353b996 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -106,7 +106,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) } } -osg::ref_ptr RigGeometry::getSourceGeometry() +osg::ref_ptr RigGeometry::getSourceGeometry() const { return mSourceGeometry; } diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index a393530ec2..801c172b36 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -44,7 +44,7 @@ namespace SceneUtil /// @note The source geometry will not be modified. void setSourceGeometry(osg::ref_ptr sourceGeom); - osg::ref_ptr getSourceGeometry(); + osg::ref_ptr getSourceGeometry() const; virtual void accept(osg::NodeVisitor &nv); virtual bool supports(const osg::PrimitiveFunctor&) const { return true; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 95c1ca4914..1dc62cfb80 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include "terraindrawable.hpp" @@ -35,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) { ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -163,10 +162,6 @@ std::vector > ChunkManager::createPasses(float chunk osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags) { - osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); - osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); - transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); - osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); osg::ref_ptr colors (new osg::Vec4ubArray); @@ -224,16 +219,15 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setPasses(createPasses(chunkSize, chunkCenter, false)); } - transform->addChild(geometry); - transform->getBound(); - geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); if (mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } - return transform; + geometry->setNodeMask(mNodeMask); + + return geometry; } } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index be83e158cb..d6f4dd98ec 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -6,6 +6,7 @@ #include #include "buffercache.hpp" +#include "quadtreeworld.hpp" namespace osg { @@ -29,23 +30,28 @@ namespace Terrain typedef std::tuple ChunkId; // Center, Lod, Lod Flags /// @brief Handles loading and caching of terrain chunks - class ChunkManager : public Resource::GenericResourceManager + class ChunkManager : public Resource::GenericResourceManager, public QuadTreeWorld::ChunkManager { public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint); void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } void setMaxCompositeGeometrySize(float maxCompGeometrySize) { mMaxCompGeometrySize = maxCompGeometrySize; } + void setNodeMask(unsigned int mask) { mNodeMask = mask; } + virtual unsigned int getNodeMask() override { return mNodeMask; } + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; void clearCache() override; void releaseGLObjects(osg::State* state) override; + virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } + private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); @@ -61,6 +67,8 @@ namespace Terrain CompositeMapRenderer* mCompositeMapRenderer; BufferCache mBufferCache; + unsigned int mNodeMask; + unsigned int mCompositeMapSize; float mCompositeMapLevel; float mMaxCompGeometrySize; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index c43a9a21b9..842dced208 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "quadtreenode.hpp" #include "storage.hpp" @@ -63,6 +64,12 @@ public: int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); + if (node->getSize()>1 && dist < (8192+1024)*1.41421356237) + { + // to prevent making chunks who will cross the activegrid border + return false; + } + return nativeLodLevel <= lodLevel; } @@ -231,6 +238,7 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour mChunkManager->setCompositeMapSize(compMapResolution); mChunkManager->setCompositeMapLevel(compMapLevel); mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize); + mChunkManagers.push_back(mChunkManager.get()); } QuadTreeWorld::~QuadTreeWorld() @@ -289,7 +297,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewD return lodFlags; } -void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, ChunkManager* chunkManager) +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers) { if (!vd->hasChanged() && entry.mRenderingNode) return; @@ -308,7 +316,20 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C } if (!entry.mRenderingNode) - entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags); + { + auto pat = new SceneUtil::PositionAttitudeTransform; + pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); + + const osg::Vec2f& center = entry.mNode->getCenter(); + bool far = (center.x() <= gridbounds.x() || center.y() <= gridbounds.y() || center.x() >= gridbounds.z() || center.y() >= gridbounds.w()); + + for (QuadTreeWorld::ChunkManager* m : chunkManagers) + { + osg::Node* n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint()); + if (n) pat->addChild(n); + } + entry.mRenderingNode = pat; + } } void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil::CullVisitor* cv, float cellworldsize, bool outofworld) @@ -385,7 +406,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) bool needsUpdate = true; ViewData* vd = nullptr; if (isCullVisitor) - vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), needsUpdate); + vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), mActiveGrid, needsUpdate); else { static ViewData sIntersectionViewData; @@ -432,12 +453,12 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) } } + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); - + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers); entry.mRenderingNode->accept(nv); } @@ -490,13 +511,17 @@ void QuadTreeWorld::enable(bool enabled) void QuadTreeWorld::cacheCell(View *view, int x, int y) { ensureQuadTreeBuilt(); + osg::Vec4i grid (x,y,x+1,y+1); ViewData* vd = static_cast(view); + vd->setActiveGrid(grid); mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5f,y+0.5f)); + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); } } @@ -505,18 +530,21 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); + vd->setActiveGrid(grid); mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance); + const float cellWorldSize = mStorage->getCellWorldSize(); + for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); } vd->markUnchanged(); } @@ -526,7 +554,7 @@ void QuadTreeWorld::storeView(const View* view, double referenceTime) osg::ref_ptr dummy = new osg::DummyObject; const ViewData* vd = static_cast(view); bool needsUpdate = false; - ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), needsUpdate); + ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), vd->getActiveGrid(), needsUpdate); stored->copyFrom(*vd); stored->setLastUsageTimeStamp(referenceTime); } @@ -556,5 +584,10 @@ void QuadTreeWorld::unloadCell(int x, int y) World::unloadCell(x,y); } +void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) +{ + mChunkManagers.push_back(m); + mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); +} } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index bcb671ee13..7634ea868f 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,11 +38,20 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort); void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); + class ChunkManager + { + public: + virtual ~ChunkManager(){} + virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) = 0; + virtual unsigned int getNodeMask() { return 0; } + }; + void addChunkManager(ChunkManager*); + private: void ensureQuadTreeBuilt(); @@ -51,6 +60,8 @@ namespace Terrain osg::ref_ptr mViewDataMap; osg::ref_ptr mLodCallback; + std::vector mChunkManagers; + OpenThreads::Mutex mQuadTreeMutex; bool mQuadTreeBuilt; float mLodFactor; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index a0e5e4718b..4398d1a143 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -5,9 +5,10 @@ #include #include +#include #include "chunkmanager.hpp" #include "compositemaprenderer.hpp" - +#include "storage.hpp" namespace Terrain { @@ -57,12 +58,17 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f()); if (!node) return nullptr; + + const float cellWorldSize = mStorage->getCellWorldSize(); + osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; + pat->setPosition(osg::Vec3f(chunkCenter.x()*cellWorldSize, chunkCenter.y()*cellWorldSize, 0.f)); + pat->addChild(node); if (parent) - parent->addChild(node); - return node; + parent->addChild(pat); + return pat; } } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index d07a0e356a..6399425a40 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -24,6 +24,7 @@ void ViewData::copyFrom(const ViewData& other) mChanged = other.mChanged; mHasViewPoint = other.mHasViewPoint; mViewPoint = other.mViewPoint; + mActiveGrid = other.mActiveGrid; } void ViewData::add(QuadTreeNode *node) @@ -118,12 +119,12 @@ bool ViewData::Entry::set(QuadTreeNode *node) } } -bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist) +bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist, const osg::Vec4i& activeGrid) { - return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist; + return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist && vd->getActiveGrid() == activeGrid; } -ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, bool& needsUpdate) +ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate) { Map::const_iterator found = mViews.find(viewer); ViewData* vd = nullptr; @@ -135,11 +136,11 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo else vd = found->second; - if (!suitable(vd, viewPoint, mReuseDistance)) + if (!suitable(vd, viewPoint, mReuseDistance, activeGrid)) { for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) { - if (suitable(other->second, viewPoint, mReuseDistance) && other->second->getNumEntries()) + if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries()) { vd->copyFrom(*other->second); needsUpdate = false; @@ -147,6 +148,7 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo } } vd->setViewPoint(viewPoint); + vd->setActiveGrid(activeGrid); needsUpdate = true; } else diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 7f9f14af55..87b45f57b1 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -57,6 +57,9 @@ namespace Terrain void setViewPoint(const osg::Vec3f& viewPoint); const osg::Vec3f& getViewPoint() const; + void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} } + const osg::Vec4i &getActiveGrid() const { return mActiveGrid;} + private: std::vector mEntries; unsigned int mNumEntries; @@ -64,6 +67,7 @@ namespace Terrain bool mChanged; osg::Vec3f mViewPoint; bool mHasViewPoint; + osg::Vec4i mActiveGrid; }; class ViewDataMap : public osg::Referenced @@ -75,7 +79,7 @@ namespace Terrain , mExpiryDelay(1.f) {} - ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate); + ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate); ViewData* createOrReuseView(); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 2d53f40908..b57fa1cef8 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -23,7 +23,6 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); - mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); @@ -48,6 +47,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); + mChunkManager->setNodeMask(nodeMask); mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask)); mResourceSystem->addResourceManager(mChunkManager.get()); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 618095a605..e559321693 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -147,7 +147,7 @@ namespace Terrain /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic& abort) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. @@ -161,6 +161,8 @@ namespace Terrain osg::Callback* getHeightCullCallback(float highz, unsigned int mask); + void setActiveGrid(const osg::Vec4i &grid) { mActiveGrid = grid; } + protected: Storage* mStorage; @@ -181,6 +183,8 @@ namespace Terrain std::set> mLoadedCells; osg::ref_ptr mHeightCullCallback; + + osg::Vec4i mActiveGrid; }; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 5b587776c3..2653f2fc42 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -106,6 +106,15 @@ composite map resolution = 512 # Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks. max composite geometry size = 4.0 +# Load far objects on terrain +object paging = true + +# Turn off to save memory but worse FPS +object paging merge geometry = true + +# Cull objects smaller than this size divided by distance +object paging min size = 0.01 + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From c0f128bcb3b084e101c167836a14b6015367a4ea Mon Sep 17 00:00:00 2001 From: bzzt Date: Sat, 3 Aug 2019 13:37:00 +0000 Subject: [PATCH 02/31] disablesupprort Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 31 +++++++++++--- apps/openmw/mwrender/objectpaging.hpp | 8 ++++ apps/openmw/mwrender/renderingmanager.cpp | 7 ++++ apps/openmw/mwrender/renderingmanager.hpp | 3 ++ apps/openmw/mwworld/cellstore.cpp | 49 +++++++++++++---------- apps/openmw/mwworld/worldimp.cpp | 22 ++++++---- 6 files changed, 84 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 6453889bcf..8a80153728 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -171,7 +171,7 @@ namespace MWRender osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; osg::Vec3f relativeViewPoint = viewPoint - worldCenter; - std::vector refs; + std::map refs; std::vector esm; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); @@ -197,8 +197,8 @@ namespace MWRender if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); if (!typeFilter(type,size>=2)) continue; - if (deleted) continue; - refs.push_back(ref); + if (deleted) { refs.erase(ref.mRefNum); continue; } + refs[ref.mRefNum] = ref; } } catch (std::exception& e) @@ -210,18 +210,25 @@ namespace MWRender { ESM::CellRef ref = it->first; bool deleted = it->second; - if (deleted) continue; + if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); if (!typeFilter(type,size>=2)) continue; - refs.push_back(ref); + refs[ref.mRefNum] = ref; } } } + { + OpenThreads::ScopedLock lock(mDisabledMutex); + for (auto disabled : mDisabled) + refs.erase(disabled); + } + osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); - for (const ESM::CellRef& ref : refs) + for (const auto& pair : refs) { + const ESM::CellRef& ref = pair.second; std::string id = Misc::StringUtils::lowerCase(ref.mRefID); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player @@ -299,4 +306,16 @@ namespace MWRender return Mask_Static; } + void ObjectPaging::enableObject(const ESM::RefNum & refnum, bool enabled) + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (enabled) mDisabled.erase(refnum); + else mDisabled.insert(refnum); + } + + void ObjectPaging::clear() + { + OpenThreads::ScopedLock lock(mDisabledMutex); + mDisabled.clear(); + } } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 6d66d078df..8c3c2b4935 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace Resource { class SceneManager; @@ -33,10 +35,16 @@ namespace MWRender virtual unsigned int getNodeMask() override; + void enableObject(const ESM::RefNum & refnum, bool enabled); + void clear(); + private: Resource::SceneManager* mSceneManager; bool mMergeGeometry; float mMinSize; + + OpenThreads::Mutex mDisabledMutex; + std::set mDisabled; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9c92da767a..41cfc56a4d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1119,6 +1119,8 @@ namespace MWRender mSky->setMoonColour(false); notifyWorldSpaceChanged(); + if (mObjectPaging) + mObjectPaging->clear(); } MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) @@ -1480,4 +1482,9 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } + void RenderingManager::pagingEnableObject(const ESM::RefNum & refnum, bool enabled) + { + if (mObjectPaging) + mObjectPaging->enableObject(refnum, enabled); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index dff76f95ec..6bf122232f 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -42,6 +42,7 @@ namespace osgViewer namespace ESM { struct Cell; + struct RefNum; } namespace Terrain @@ -240,6 +241,8 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); + void pagingEnableObject(const ESM::RefNum & refnum, bool enabled); + private: void updateProjectionMatrix(); void updateTextureFiltering(); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 599f345b8a..6b737f2029 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -106,7 +106,7 @@ namespace template void readReferenceCollection (ESM::ESMReader& reader, - MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) + MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap, MWWorld::CellStore* cellstore) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -142,6 +142,11 @@ namespace { // overwrite existing reference iter->load (state); + if (!iter->mData.isEnabled()) + { + iter->mData.enable(); + MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore)); + } return; } @@ -809,107 +814,107 @@ namespace MWWorld { case ESM::REC_ACTI: - readReferenceCollection (reader, mActivators, cref, contentFileMap); + readReferenceCollection (reader, mActivators, cref, contentFileMap, this); break; case ESM::REC_ALCH: - readReferenceCollection (reader, mPotions, cref, contentFileMap); + readReferenceCollection (reader, mPotions, cref, contentFileMap, this); break; case ESM::REC_APPA: - readReferenceCollection (reader, mAppas, cref, contentFileMap); + readReferenceCollection (reader, mAppas, cref, contentFileMap, this); break; case ESM::REC_ARMO: - readReferenceCollection (reader, mArmors, cref, contentFileMap); + readReferenceCollection (reader, mArmors, cref, contentFileMap, this); break; case ESM::REC_BOOK: - readReferenceCollection (reader, mBooks, cref, contentFileMap); + readReferenceCollection (reader, mBooks, cref, contentFileMap, this); break; case ESM::REC_CLOT: - readReferenceCollection (reader, mClothes, cref, contentFileMap); + readReferenceCollection (reader, mClothes, cref, contentFileMap, this); break; case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, cref, contentFileMap); + readReferenceCollection (reader, mContainers, cref, contentFileMap, this); break; case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, cref, contentFileMap); + readReferenceCollection (reader, mCreatures, cref, contentFileMap, this); break; case ESM::REC_DOOR: - readReferenceCollection (reader, mDoors, cref, contentFileMap); + readReferenceCollection (reader, mDoors, cref, contentFileMap, this); break; case ESM::REC_INGR: - readReferenceCollection (reader, mIngreds, cref, contentFileMap); + readReferenceCollection (reader, mIngreds, cref, contentFileMap, this); break; case ESM::REC_LEVC: - readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); + readReferenceCollection (reader, mCreatureLists, cref, contentFileMap, this); break; case ESM::REC_LEVI: - readReferenceCollection (reader, mItemLists, cref, contentFileMap); + readReferenceCollection (reader, mItemLists, cref, contentFileMap, this); break; case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, cref, contentFileMap); + readReferenceCollection (reader, mLights, cref, contentFileMap, this); break; case ESM::REC_LOCK: - readReferenceCollection (reader, mLockpicks, cref, contentFileMap); + readReferenceCollection (reader, mLockpicks, cref, contentFileMap, this); break; case ESM::REC_MISC: - readReferenceCollection (reader, mMiscItems, cref, contentFileMap); + readReferenceCollection (reader, mMiscItems, cref, contentFileMap, this); break; case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, cref, contentFileMap); + readReferenceCollection (reader, mNpcs, cref, contentFileMap, this); break; case ESM::REC_PROB: - readReferenceCollection (reader, mProbes, cref, contentFileMap); + readReferenceCollection (reader, mProbes, cref, contentFileMap, this); break; case ESM::REC_REPA: - readReferenceCollection (reader, mRepairs, cref, contentFileMap); + readReferenceCollection (reader, mRepairs, cref, contentFileMap, this); break; case ESM::REC_STAT: - readReferenceCollection (reader, mStatics, cref, contentFileMap); + readReferenceCollection (reader, mStatics, cref, contentFileMap, this); break; case ESM::REC_WEAP: - readReferenceCollection (reader, mWeapons, cref, contentFileMap); + readReferenceCollection (reader, mWeapons, cref, contentFileMap, this); break; case ESM::REC_BODY: - readReferenceCollection (reader, mBodyParts, cref, contentFileMap); + readReferenceCollection (reader, mBodyParts, cref, contentFileMap, this); break; default: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c51266bab6..0817a42263 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -814,6 +814,9 @@ namespace MWWorld if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->addObjectToScene (reference); + + if (reference.getCellRef().getRefNum().hasContentFile()) + mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), true); } } @@ -838,20 +841,23 @@ namespace MWWorld void World::disable (const Ptr& reference) { + if (!reference.getRefData().isEnabled()) + return; + // disable is a no-op for items in containers if (!reference.isInCell()) return; - if (reference.getRefData().isEnabled()) - { - if (reference == getPlayerPtr()) - throw std::runtime_error("can not disable player object"); + if (reference == getPlayerPtr()) + throw std::runtime_error("can not disable player object"); - reference.getRefData().disable(); + reference.getRefData().disable(); - if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) - mWorldScene->removeObjectFromScene (reference); - } + if (reference.getCellRef().getRefNum().hasContentFile()) + mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), false); + + if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) + mWorldScene->removeObjectFromScene (reference); } void World::advanceTime (double hours, bool incremental) From ce505a9bb355233ff1dfbe4017aa4e66711a86e4 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 30 Apr 2020 13:37:00 +0000 Subject: [PATCH 03/31] crashfix + optimiziation Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 233 +++++++++++++++++++------- apps/openmw/mwrender/objectpaging.hpp | 6 +- apps/openmw/mwworld/scene.cpp | 2 +- components/sceneutil/optimizer.cpp | 102 +++++++++-- components/terrain/chunkmanager.cpp | 8 +- components/terrain/chunkmanager.hpp | 4 +- components/terrain/quadtreeworld.cpp | 42 +++-- components/terrain/quadtreeworld.hpp | 3 +- components/terrain/terraingrid.cpp | 2 +- files/settings-default.cfg | 4 +- 10 files changed, 300 insertions(+), 106 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 8a80153728..b529336eb6 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -1,5 +1,7 @@ #include "objectpaging.hpp" +#include + #include #include #include @@ -14,7 +16,6 @@ #include #include -#include #include #include @@ -60,7 +61,7 @@ namespace MWRender } } - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { if (!far)return nullptr; ChunkId id = std::make_tuple(center, size); @@ -70,7 +71,7 @@ namespace MWRender return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, viewPoint); + osg::ref_ptr node = createChunk(size, center, viewPoint, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -92,12 +93,11 @@ namespace MWRender class CopyOp : public osg::CopyOp { public: - CopyOp() : mDistance(0.f) { - setCopyFlags(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES - #if OSG_MIN_VERSION_REQUIRED(3,5,6) - |osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES // damned vbogarbage racing - #endif - ); + CopyOp(bool deep) : mDistance(0.f) { + unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; + if (deep) + flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; + setCopyFlags(flags); } float mDistance; @@ -128,12 +128,13 @@ namespace MWRender return n; } - osg::Node* n = osg::CopyOp::operator()(node); - if (n) { - n->setDataVariance(osg::Object::STATIC); - n->setUserDataContainer(nullptr); - n->setName(""); - } + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + + osg::Node* n = osg::clone(node, *this); + n->setDataVariance(osg::Object::STATIC); + n->setUserDataContainer(nullptr); + n->setName(""); return n; } virtual osg::Drawable* operator() (const osg::Drawable* drawable) const @@ -142,11 +143,20 @@ namespace MWRender return nullptr; if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) - return osg::CopyOp::operator()(rig->getSourceGeometry()); + return operator()(rig->getSourceGeometry()); if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) - return osg::CopyOp::operator()(morph->getSourceGeometry()); + return operator()(morph->getSourceGeometry()); - return osg::CopyOp::operator()(drawable); + if (getCopyFlags() & DEEP_COPY_DRAWABLES) + { + osg::Drawable* d = osg::clone(drawable, *this); + d->setDataVariance(osg::Object::STATIC); + d->setUserDataContainer(nullptr); + d->setName(""); + return d; + } + else + return osg::CopyOp::operator()(drawable); } virtual osg::Callback* operator() (const osg::Callback* callback) const { @@ -154,20 +164,77 @@ namespace MWRender } }; + class TemplateRef : public osg::Object + { + public: + TemplateRef() {} + TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObjects(copy.mObjects) {} + META_Object(MWRender, TemplateRef) + std::vector> mObjects; + }; + + class AnalyzeVisitor : public osg::NodeVisitor + { + public: + AnalyzeVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mCurrentStateSet(nullptr) {} + + typedef std::unordered_map StateSetCounter; + struct Result + { + StateSetCounter mStateSetCounter; + unsigned int mNumVerts = 0; + }; + + virtual void apply(osg::Node& node) + { + if (node.getStateSet()) + mCurrentStateSet = node.getStateSet(); + traverse(node); + } + virtual void apply(osg::Geometry& geom) + { + mResult.mNumVerts += geom.getVertexArray()->getNumElements(); + ++mResult.mStateSetCounter[mCurrentStateSet]; + ++mGlobalStateSetCounter[mCurrentStateSet]; + } + Result retrieveResult() + { + Result result = mResult; + mResult = Result(); + mCurrentStateSet = nullptr; + return result; + } + float getMergeBenefit(const Result& result) + { + if (result.mStateSetCounter.empty()) return 1; + float mergeBenefit = 0; + for (auto pair : result.mStateSetCounter) + { + mergeBenefit += mGlobalStateSetCounter[pair.first]; + } + mergeBenefit /= result.mStateSetCounter.size(); + return mergeBenefit; + } + + Result mResult; + osg::StateSet* mCurrentStateSet; + StateSetCounter mGlobalStateSetCounter; + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { - mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain"); + mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); } - osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) { osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); - osg::ref_ptr group = new osg::Group; - osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; osg::Vec3f relativeViewPoint = viewPoint - worldCenter; @@ -226,21 +293,18 @@ namespace MWRender osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); + struct InstanceList + { + std::vector mInstances; + AnalyzeVisitor::Result mAnalyzeResult; + }; + typedef std::map, InstanceList> NodeMap; + NodeMap nodes; + AnalyzeVisitor analyzeVisitor; + for (const auto& pair : refs) { const ESM::CellRef& ref = pair.second; - std::string id = Misc::StringUtils::lowerCase(ref.mRefID); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - - int type = store.findStatic(id); - std::string model = "meshes/" + getModel(type, id, store); -/* - bool useAnim = type != ESM::REC_STAT; - if (useAnim) - model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); -*/ - if (model.empty()) continue; osg::Vec3f pos = ref.mPos.asVec3(); if (size < 1.f) @@ -254,50 +318,101 @@ namespace MWRender continue; } - osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - float d = (viewPoint - pos).length(); + int type = store.findStatic(id); + std::string model = getModel(type, id, store); + if (model.empty()) continue; + model = "meshes/" + model; +/* + bool useAnim = type != ESM::REC_STAT; + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); +*/ + osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); + float d = (viewPoint - pos).length(); if (cnode->getBound().radius() * ref.mScale < d*mMinSize) continue; - CopyOp co = CopyOp(); - co.mDistance = d; - osg::ref_ptr node = osg::clone(cnode.get(), co); - node->setUserDataContainer(nullptr); - - osg::Matrixf matrix; - matrix.preMultTranslate(pos - worldCenter); - matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * - osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * - osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); - matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); - osg::ref_ptr trans = new osg::MatrixTransform(matrix); - trans->addChild(node); - trans->setDataVariance(osg::Object::STATIC); - - group->addChild(trans); + auto emplaced = nodes.emplace(cnode, InstanceList()); + if (emplaced.second) + { + const_cast(cnode.get())->accept(analyzeVisitor); + emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); + } + emplaced.first->second.mInstances.push_back(&ref); } - if (mMergeGeometry) + osg::ref_ptr group = new osg::Group; + osg::ref_ptr mergeGroup = new osg::Group; + osg::ref_ptr templateRefs = new TemplateRef; + for (const auto& pair : nodes) + { + const osg::Node* cnode = pair.first; + + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; + + float mergeCost = analyzeResult.mNumVerts * size; + float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; + bool merge = mergeBenefit > mergeCost; + + for (auto cref : pair.second.mInstances) + { + const ESM::CellRef& ref = *cref; + osg::Vec3f pos = ref.mPos.asVec3(); + float d = (viewPoint - pos).length(); + + CopyOp co = CopyOp(merge); + co.mDistance = d; + osg::ref_ptr node = osg::clone(cnode, co); + node->setUserDataContainer(nullptr); + + osg::Matrixf matrix; + matrix.preMultTranslate(pos - worldCenter); + matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * + osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); + matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); + osg::ref_ptr trans = new osg::MatrixTransform(matrix); + trans->addChild(node); + trans->setDataVariance(osg::Object::STATIC); + + if (merge) + mergeGroup->addChild(trans); + else + group->addChild(trans); + } + } + + if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - group->getBound().center()).length2() > group->getBound().radius2()) + if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); } optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; - optimizer.optimize(group, options); - } + optimizer.optimize(mergeGroup, options); + + group->addChild(mergeGroup); - auto ico = mSceneManager->getIncrementalCompileOperation(); - if (ico) ico->add(group); - else group->getBound(); + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (compile && ico) ico->add(mergeGroup); + } + group->getBound(); group->setNodeMask(Mask_Static); + group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + return group; } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 8c3c2b4935..16de6e8bc6 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -27,9 +27,9 @@ namespace MWRender ObjectPaging(Resource::SceneManager* sceneManager); ~ObjectPaging() = default; - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) override; + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } @@ -40,7 +40,7 @@ namespace MWRender private: Resource::SceneManager* mSceneManager; - bool mMergeGeometry; + float mMergeFactor; float mMinSize; OpenThreads::Mutex mDisabledMutex; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 127b56c94f..1dac18eaa9 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -477,7 +477,7 @@ namespace MWWorld { float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); - float distance = std::max(std::abs(centerY-pos.x()), std::abs(centerY-pos.y())); + float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold if (distance <= maxDistance) return *currentGridCenter; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 7bcccdbe33..985fd9ee25 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -18,6 +18,7 @@ #include "optimizer.hpp" +#include #include #include #include @@ -587,17 +588,36 @@ void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node) traverse(node); } +bool needvbo(const osg::Geometry* geom) +{ +#if OSG_MIN_VERSION_REQUIRED(3,5,6) + return true; +#else + return geom->getUseVertexBufferObjects(); +#endif +} + +osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom) +{ + array = osg::clone(array, osg::CopyOp::DEEP_COPY_ALL); + if (!vbo && needvbo(geom)) + vbo = new osg::VertexBufferObject; + if (vbo) + array->setVertexBufferObject(vbo); + return array; +} void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Drawable& drawable) { osg::Geometry *geometry = drawable.asGeometry(); if((geometry) && (isOperationPermissibleForObject(&drawable))) { + osg::VertexBufferObject* vbo = nullptr; if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) { - geometry->setVertexArray(dynamic_cast(geometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setVertexArray(cloneArray(geometry->getVertexArray(), vbo, geometry)); } if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) { - geometry->setNormalArray(dynamic_cast(geometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setNormalArray(cloneArray(geometry->getNormalArray(), vbo, geometry)); } } _drawableSet.insert(&drawable); @@ -1110,14 +1130,30 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) popStateSet(); } -osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps) +osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObject*& ebo, const osg::Geometry* geom) { if (ps->referenceCount() <= 1) return ps; - ps = dynamic_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); + ps = osg::clone(ps, osg::CopyOp::DEEP_COPY_ALL); + + osg::DrawElements* drawElements = ps->getDrawElements(); + if (!drawElements) return ps; + + if (!ebo && needvbo(geom)) + ebo = new osg::ElementBufferObject; + if (ebo) + drawElements->setElementBufferObject(ebo); + return ps; } +bool containsSharedPrimitives(const osg::Geometry* geom) +{ + for (unsigned int i=0; igetNumPrimitiveSets(); ++i) + if (geom->getPrimitiveSet(i)->referenceCount() > 1) return true; + return false; +} + bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (!isOperationPermissibleForObject(&group)) return false; @@ -1305,6 +1341,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList(); @@ -1317,12 +1354,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (prim->getNumIndices()==3) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::TRIANGLES); } else if (prim->getNumIndices()==4) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::QUADS); } } @@ -1337,6 +1374,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { if (geom->getNumPrimitiveSets()>0 && @@ -1379,7 +1417,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { - lhs = clonePrimitive(lhs); + lhs = clonePrimitive(lhs, ebo, geom); primitives[lhsNo] = lhs; switch(lhs->getType()) @@ -1499,6 +1537,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) } } #endif + if (doneCombine && !geom->containsSharedArrays() && !containsSharedPrimitives(geom)) + { + // prefer to use vbo for merged geometries as vbo uses less memory than display lists. + geom->setUseVertexBufferObjects(true); + geom->setUseDisplayList(false); + } } } @@ -1578,16 +1622,14 @@ class MergeArrayVisitor : public osg::ArrayVisitor bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs) { - if (lhs.containsSharedArrays()) - lhs.duplicateSharedArrays(); - MergeArrayVisitor merger; - + osg::VertexBufferObject* vbo = nullptr; unsigned int base = 0; if (lhs.getVertexArray() && rhs.getVertexArray()) { - base = lhs.getVertexArray()->getNumElements(); + if (lhs.getVertexArray()->referenceCount() > 1) + lhs.setVertexArray(cloneArray(lhs.getVertexArray(), vbo, &lhs)); if (!merger.merge(lhs.getVertexArray(),rhs.getVertexArray())) { OSG_DEBUG << "MergeGeometry: vertex array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getNormalArray()->referenceCount() > 1) + lhs.setNormalArray(cloneArray(lhs.getNormalArray(), vbo, &lhs)); if (!merger.merge(lhs.getNormalArray(),rhs.getNormalArray())) { OSG_DEBUG << "MergeGeometry: normal array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getColorArray()->referenceCount() > 1) + lhs.setColorArray(cloneArray(lhs.getColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getColorArray(),rhs.getColorArray())) { OSG_DEBUG << "MergeGeometry: color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getSecondaryColorArray()->referenceCount() > 1) + lhs.setSecondaryColorArray(cloneArray(lhs.getSecondaryColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray())) { OSG_DEBUG << "MergeGeometry: secondary color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getFogCoordArray()->referenceCount() > 1) + lhs.setFogCoordArray(cloneArray(lhs.getFogCoordArray(), vbo, &lhs)); if (!merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray())) { OSG_DEBUG << "MergeGeometry: fog coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setTexCoordArray(unit, cloneArray(lhs.getTexCoordArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit))) { OSG_DEBUG << "MergeGeometry: tex coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setVertexAttribArray(unit, cloneArray(lhs.getVertexAttribArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit))) { OSG_DEBUG << "MergeGeometry: vertex attrib array not merged. Some data may be lost." <getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; @@ -1696,13 +1758,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUShort osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1722,13 +1789,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUInt osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1738,7 +1810,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType): case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): default: - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); break; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 1dc62cfb80..5f80b9d367 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -34,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -42,7 +42,7 @@ osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f &cen return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, lod, lodFlags); + osg::ref_ptr node = createChunk(size, center, lod, lodFlags, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -160,7 +160,7 @@ std::vector > ChunkManager::createPasses(float chunk return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale); } -osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile) { osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); @@ -221,7 +221,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); - if (mSceneManager->getIncrementalCompileOperation()) + if (compile && mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index d6f4dd98ec..e323da6a4f 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -35,7 +35,7 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile); void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } @@ -53,7 +53,7 @@ namespace Terrain virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } private: - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile); osg::ref_ptr createCompositeMapRTT(); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 842dced208..d252149b2e 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -53,9 +53,10 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - DefaultLodCallback(float factor, float minSize) + DefaultLodCallback(float factor, float minSize, const osg::Vec4i& grid) : mFactor(factor) , mMinSize(minSize) + , mActiveGrid(grid) { } @@ -64,20 +65,27 @@ public: int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); - if (node->getSize()>1 && dist < (8192+1024)*1.41421356237) + if (node->getSize()>1) { + float halfSize = node->getSize()/2; + const osg::Vec2f& center = node->getCenter(); + osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); + bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) <= std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) <= std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border - return false; + if (intersects) + return false; } - return nativeLodLevel <= lodLevel; } private: float mFactor; float mMinSize; + osg::Vec4i mActiveGrid; }; +const float MIN_SIZE = 1/8.f; + class RootNode : public QuadTreeNode { public: @@ -297,7 +305,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewD return lodFlags; } -void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers) +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers, bool compile) { if (!vd->hasChanged() && entry.mRenderingNode) return; @@ -325,7 +333,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f for (QuadTreeWorld::ChunkManager* m : chunkManagers) { - osg::Node* n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint()); + osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile); if (n) pat->addChild(n); } entry.mRenderingNode = pat; @@ -411,6 +419,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { static ViewData sIntersectionViewData; vd = &sIntersectionViewData; + vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. } if (needsUpdate) @@ -430,7 +439,10 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); } else - mRootNode->traverseNodes(vd, cv->getViewPoint(), mLodCallback, mViewDistance); + { + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); + mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); + } } else { @@ -458,16 +470,13 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers, false); entry.mRenderingNode->accept(nv); } if (isCullVisitor) updateWaterCullingView(mHeightCullCallback, vd, static_cast(&nv), mStorage->getCellWorldSize(), !isGridEmpty()); - 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. - vd->markUnchanged(); double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0; @@ -484,9 +493,7 @@ void QuadTreeWorld::ensureQuadTreeBuilt() if (mQuadTreeBuilt) return; - const float minSize = 1/8.f; - mLodCallback = new DefaultLodCallback(mLodFactor, minSize); - QuadTreeBuilder builder(mStorage, minSize); + QuadTreeBuilder builder(mStorage, MIN_SIZE); builder.build(); mRootNode = builder.getRootNode(); @@ -521,7 +528,7 @@ void QuadTreeWorld::cacheCell(View *view, int x, int y) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } } @@ -537,14 +544,15 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); vd->setActiveGrid(grid); - mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); + mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7634ea868f..2010548523 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -47,7 +47,7 @@ namespace Terrain { public: virtual ~ChunkManager(){} - virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) = 0; + virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) = 0; virtual unsigned int getNodeMask() { return 0; } }; void addChunkManager(ChunkManager*); @@ -58,7 +58,6 @@ namespace Terrain osg::ref_ptr mRootNode; osg::ref_ptr mViewDataMap; - osg::ref_ptr mLodCallback; std::vector mChunkManagers; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 4398d1a143..5f99cd97eb 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -58,7 +58,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f()); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true); if (!node) return nullptr; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2653f2fc42..ec5e870168 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -109,8 +109,8 @@ max composite geometry size = 4.0 # Load far objects on terrain object paging = true -# Turn off to save memory but worse FPS -object paging merge geometry = true +# Affects the likelyhood of objects being merged. A higher value means merging is more likely and improves FPS at the cost of memory. +object paging merge factor = 1500 # Cull objects smaller than this size divided by distance object paging min size = 0.01 From cf439581e1e84bb1a6a3a7ae0ef7ddba1dc3887c Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sun, 3 May 2020 13:37:00 +0000 Subject: [PATCH 04/31] comply by elsid review Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 12 +++++++----- apps/openmw/mwrender/objectpaging.hpp | 2 -- components/resource/scenemanager.cpp | 10 ++++++++-- components/terrain/chunkmanager.hpp | 2 -- components/terrain/quadtreeworld.cpp | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index b529336eb6..560edfcb4d 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -45,7 +45,7 @@ namespace MWRender } } - const std::string& getModel(int type, const std::string& id, const MWWorld::ESMStore& store) + std::string getModel(int type, const std::string& id, const MWWorld::ESMStore& store) { switch (type) { @@ -57,7 +57,8 @@ namespace MWRender return store.get().searchStatic(id)->mModel; case ESM::REC_CONT: return store.get().searchStatic(id)->mModel; - default: throw std::exception(); + default: + return std::string(); } } @@ -340,7 +341,7 @@ namespace MWRender auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { - const_cast(cnode.get())->accept(analyzeVisitor); + const_cast(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); } emplaced.first->second.mInstances.push_back(&ref); @@ -410,8 +411,9 @@ namespace MWRender group->getBound(); group->setNodeMask(Mask_Static); - - group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); + udc->addUserObject(templateRefs); + udc->addUserObject(mergeGroup); // for ICO ref counting return group; } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 16de6e8bc6..56f393c7b6 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -31,8 +31,6 @@ namespace MWRender osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); - virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } - virtual unsigned int getNodeMask() override; void enableObject(const ESM::RefNum & refnum, bool enabled); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 1709a3d149..f4a605bca9 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -717,8 +717,14 @@ namespace Resource if (mIncrementalCompileOperation) { OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); - while (mIncrementalCompileOperation->getToCompile().size() > 1000) - mIncrementalCompileOperation->getToCompile().pop_front(); + osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile(); + for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();) + { + if ((*it)->_subgraphToCompile->referenceCount() <= 2) + it = sets.erase(it); + else + ++it; + } } } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index e323da6a4f..87770fafb4 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -50,8 +50,6 @@ namespace Terrain void releaseGLObjects(osg::State* state) override; - virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } - private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index d252149b2e..69291f6d8c 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -325,7 +325,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f if (!entry.mRenderingNode) { - auto pat = new SceneUtil::PositionAttitudeTransform; + osg::ref_ptr pat = new SceneUtil::PositionAttitudeTransform; pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); const osg::Vec2f& center = entry.mNode->getCenter(); From 1f891ca46def28a9d6cc4eab51676476cef4f6e2 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 05/31] billboarding support for tree mods Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 87 ++++++++++++++++++++++----- components/terrain/world.cpp | 2 + 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 560edfcb4d..6393bfc76e 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -94,14 +94,16 @@ namespace MWRender class CopyOp : public osg::CopyOp { public: - CopyOp(bool deep) : mDistance(0.f) { + CopyOp(bool deep) : mSqrDistance(0.f) { unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; if (deep) flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; setCopyFlags(flags); } - float mDistance; + float mSqrDistance; + osg::Vec3f mViewVector; + mutable std::vector mNodePath; virtual osg::Node* operator() (const osg::Node* node) const { @@ -123,7 +125,7 @@ namespace MWRender { osg::Group* n = new osg::Group; for (unsigned int i=0; igetNumChildren(); ++i) - if (lod->getMinRange(i) <= mDistance && mDistance < lod->getMaxRange(i)) + if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance && mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) n->addChild(operator()(lod->getChild(i))); n->setDataVariance(osg::Object::STATIC); return n; @@ -132,11 +134,64 @@ namespace MWRender if (const osg::Drawable* d = node->asDrawable()) return operator()(d); - osg::Node* n = osg::clone(node, *this); - n->setDataVariance(osg::Object::STATIC); - n->setUserDataContainer(nullptr); - n->setName(""); - return n; + mNodePath.push_back(node); + + osg::Node* cloned = osg::clone(node, *this); + cloned->setDataVariance(osg::Object::STATIC); + cloned->setUserDataContainer(nullptr); + cloned->setName(""); + + mNodePath.pop_back(); + + handleCallbacks(node, cloned); + + return cloned; + } + void handleCallbacks(const osg::Node* node, osg::Node *cloned) const + { + const osg::Callback* callback = node->getCullCallback(); + while (callback) + { + if (callback->className() == std::string("BillboardCallback")) + handleBillboard(cloned); + callback = callback->getNestedCallback(); + } + } + void handleBillboard(osg::Node* node) const + { + osg::Transform* transform = node->asTransform(); + if (!transform) return; + osg::MatrixTransform* matrixTransform = transform->asMatrixTransform(); + if (!matrixTransform) return; + + osg::Matrix worldToLocal = osg::Matrix::identity(); + for (auto node : mNodePath) + if (const osg::Transform* t = node->asTransform()) + t->computeWorldToLocalMatrix(worldToLocal, nullptr); + worldToLocal = osg::Matrix::orthoNormal(worldToLocal); + + osg::Matrix billboardMatrix; + osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans()); + viewVector.normalize(); + osg::Vec3f right = viewVector ^ osg::Vec3f(0,0,1); + right.normalize(); + osg::Vec3f up = right ^ viewVector; + up.normalize(); + billboardMatrix.makeLookAt(osg::Vec3f(0,0,0), viewVector, up); + billboardMatrix.invert(billboardMatrix); + + const osg::Matrix& oldMatrix = matrixTransform->getMatrix(); + float mag[3]; // attempt to preserve scale + for (int i=0;i<3;++i) + mag[i] = std::sqrt(oldMatrix(0,i) * oldMatrix(0,i) + oldMatrix(1,i) * oldMatrix(1,i) + oldMatrix(2,i) * oldMatrix(2,i)); + osg::Matrix newMatrix; + worldToLocal.setTrans(0,0,0); + newMatrix *= worldToLocal; + newMatrix.preMult(billboardMatrix); + newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2])); + newMatrix.setTrans(oldMatrix.getTrans()); + + matrixTransform->setMatrix(newMatrix); } virtual osg::Drawable* operator() (const osg::Drawable* drawable) const { @@ -367,12 +422,6 @@ namespace MWRender { const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - float d = (viewPoint - pos).length(); - - CopyOp co = CopyOp(merge); - co.mDistance = d; - osg::ref_ptr node = osg::clone(cnode, co); - node->setUserDataContainer(nullptr); osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); @@ -381,9 +430,17 @@ namespace MWRender osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); osg::ref_ptr trans = new osg::MatrixTransform(matrix); - trans->addChild(node); trans->setDataVariance(osg::Object::STATIC); + CopyOp co = CopyOp(merge); + co.mNodePath.push_back(trans); + co.mSqrDistance = (viewPoint - pos).length2(); + co.mViewVector = (viewPoint - worldCenter); + osg::ref_ptr node = osg::clone(cnode, co); + node->setUserDataContainer(nullptr); + + trans->addChild(node); + if (merge) mergeGroup->addChild(trans); else diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index b57fa1cef8..fcacb442ac 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -23,6 +23,8 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); + mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); + osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); From 69514dfd46ede1f2b561e0ffcbe85432c3866016 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 06/31] ico redundency fix + stats counter Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 17 +++++++++++++---- apps/openmw/mwrender/objectpaging.hpp | 2 ++ components/resource/scenemanager.cpp | 7 ++++++- components/resource/stats.cpp | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 6393bfc76e..28644cecf3 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -463,14 +463,17 @@ namespace MWRender group->addChild(mergeGroup); auto ico = mSceneManager->getIncrementalCompileOperation(); - if (compile && ico) ico->add(mergeGroup); + if (compile && ico) + { + auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(mergeGroup); + ico->add(compileSet); + compileSet->_subgraphToCompile = group; // for ref counting in SceneManager::updateCache + } } group->getBound(); group->setNodeMask(Mask_Static); - osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); - udc->addUserObject(templateRefs); - udc->addUserObject(mergeGroup); // for ICO ref counting + group->getOrCreateUserDataContainer()->addUserObject(templateRefs); return group; } @@ -492,4 +495,10 @@ namespace MWRender OpenThreads::ScopedLock lock(mDisabledMutex); mDisabled.clear(); } + + void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const + { + stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize()); + } + } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 56f393c7b6..9fb3aa7541 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -36,6 +36,8 @@ namespace MWRender void enableObject(const ESM::RefNum & refnum, bool enabled); void clear(); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; + private: Resource::SceneManager* mSceneManager; float mMergeFactor; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index f4a605bca9..29b312670c 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -720,8 +720,13 @@ namespace Resource osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile(); for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();) { - if ((*it)->_subgraphToCompile->referenceCount() <= 2) + int refcount = (*it)->_subgraphToCompile->referenceCount(); + if ((*it)->_subgraphToCompile->asDrawable()) refcount -= 1; // ref by CompileList. + if (refcount <= 2) // ref by ObjectCache + ref by _subgraphToCompile. + { + // no other ref = not needed anymore. it = sets.erase(it); + } else ++it; } diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 0dd52ffb6a..2dda2c2dfa 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -292,6 +292,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "Nif", "Keyframe", "", + "Object Chunk", "Terrain Chunk", "Terrain Texture", "Land", From 38c21163eab45050096ed7067ddabddd27a3a444 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 4 May 2020 13:37:00 +0000 Subject: [PATCH 07/31] + meshsizecache for reduce i&o stalling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 21 +++++++++++++++++++-- apps/openmw/mwrender/objectpaging.hpp | 4 ++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 28644cecf3..4121233093 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -374,6 +374,17 @@ namespace MWRender continue; } + float d = (viewPoint - pos).length(); + { + OpenThreads::ScopedLock lock(mSizeCacheMutex); + SizeCache::iterator found = mSizeCache.find(pair.first); + if (found != mSizeCache.end()) + { + if (found->second < d*mMinSize) + continue; + } + } + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player @@ -389,9 +400,15 @@ namespace MWRender */ osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); - float d = (viewPoint - pos).length(); - if (cnode->getBound().radius() * ref.mScale < d*mMinSize) + float radius = cnode->getBound().radius() * ref.mScale; + if (radius < d*mMinSize) + { + OpenThreads::ScopedLock lock(mSizeCacheMutex); + { + mSizeCache[pair.first] = radius; + } continue; + } auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 9fb3aa7541..71a0db9962 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -45,6 +45,10 @@ namespace MWRender OpenThreads::Mutex mDisabledMutex; std::set mDisabled; + + OpenThreads::Mutex mSizeCacheMutex; + typedef std::map SizeCache; + SizeCache mSizeCache; }; } From 0b4226f3e27488d4f6dd3293871ffb4b27eba94a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 08/31] ico effieciency Signed-off-by: Bret Curtis --- apps/openmw/mwgui/loadingscreen.cpp | 32 ++++++++++++++------ apps/openmw/mwgui/loadingscreen.hpp | 10 ++++--- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 36 +++++++++++++++++------ apps/openmw/mwrender/renderingmanager.cpp | 1 - 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index dcfe723f77..436e9c2bc9 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" @@ -29,9 +30,9 @@ namespace MWGui { - LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) + LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer) : WindowBase("openmw_loading_screen.layout") - , mVFS(vfs) + , mResourceSystem(resourceSystem) , mViewer(viewer) , mTargetFrameRate(120.0) , mLastWallpaperChangeTime(0.0) @@ -64,9 +65,9 @@ namespace MWGui void LoadingScreen::findSplashScreens() { - const std::map& index = mVFS->getIndex(); + const std::map& index = mResourceSystem->getVFS()->getIndex(); std::string pattern = "Splash/"; - mVFS->normalizeFilename(pattern); + mResourceSystem->getVFS()->normalizeFilename(pattern); /* priority given to the left */ const std::array supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; @@ -171,6 +172,11 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); + if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) { + mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame(); + mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame(); + } + mVisible = visible; mLoadingBox->setVisible(mVisible); setVisible(true); @@ -215,6 +221,12 @@ namespace MWGui //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; setVisible(false); + if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) + { + ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(mOldIcoMin); + ico->setMaximumNumOfObjectsToCompilePerFrame(mOldIcoMax); + } + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); } @@ -336,7 +348,13 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(0, true, true); - //osg::Timer timer; + mResourceSystem->reportStats(mViewer->getFrameStamp()->getFrameNumber(), mViewer->getViewerStats()); + if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) + { + ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1.f/getTargetFrameRate()); + ico->setMaximumNumOfObjectsToCompilePerFrame(1000); + } + // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() @@ -344,10 +362,6 @@ namespace MWGui mViewer->updateTraversal(); mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); - //std::cout << "frame took " << timer.time_m() << std::endl; - - //if (mViewer->getIncrementalCompileOperation()) - //std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl; mLastRenderTime = mTimer.time_m(); } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index c054f3bbd6..1be1b3a0ce 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -20,9 +20,9 @@ namespace osg class Texture2D; } -namespace VFS +namespace Resource { - class Manager; + class ResourceSystem; } namespace MWGui @@ -32,7 +32,7 @@ namespace MWGui class LoadingScreen : public WindowBase, public Loading::Listener { public: - LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer); + LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer); virtual ~LoadingScreen(); /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details @@ -53,7 +53,7 @@ namespace MWGui void setupCopyFramebufferToTextureCallback(); - const VFS::Manager* mVFS; + Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mViewer; double mTargetFrameRate; @@ -70,6 +70,8 @@ namespace MWGui size_t mProgress; bool mShowWallpaper; + float mOldIcoMin = 0.f; + unsigned int mOldIcoMax = 0; MyGUI::Widget* mLoadingBox; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ab7c3334ce..e1f7a4fbf3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -230,7 +230,7 @@ namespace MWGui mKeyboardNavigation->setEnabled(keyboardNav); Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav); - mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); + mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer); mWindows.push_back(mLoadingScreen); //set up the hardware cursor manager diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 4121233093..0d547184c1 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -353,6 +353,7 @@ namespace MWRender { std::vector mInstances; AnalyzeVisitor::Result mAnalyzeResult; + bool mNeedCompile = false; }; typedef std::map, InstanceList> NodeMap; NodeMap nodes; @@ -398,7 +399,7 @@ namespace MWRender if (useAnim) model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); */ - osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); float radius = cnode->getBound().radius() * ref.mScale; if (radius < d*mMinSize) @@ -415,6 +416,7 @@ namespace MWRender { const_cast(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); + emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; } emplaced.first->second.mInstances.push_back(&ref); } @@ -422,19 +424,29 @@ namespace MWRender osg::ref_ptr group = new osg::Group; osg::ref_ptr mergeGroup = new osg::Group; osg::ref_ptr templateRefs = new TemplateRef; + osgUtil::StateToCompile stateToCompile(0, nullptr); for (const auto& pair : nodes) { const osg::Node* cnode = pair.first; - // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache - templateRefs->mObjects.push_back(cnode); - const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; float mergeCost = analyzeResult.mNumVerts * size; float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; bool merge = mergeBenefit > mergeCost; + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + if (pair.second.mNeedCompile) + { + int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; + if (!merge) + mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + stateToCompile._mode = mode; + const_cast(cnode)->accept(stateToCompile); + } + for (auto cref : pair.second.mInstances) { const ESM::CellRef& ref = *cref; @@ -479,15 +491,21 @@ namespace MWRender group->addChild(mergeGroup); - auto ico = mSceneManager->getIncrementalCompileOperation(); - if (compile && ico) + if (compile) { - auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(mergeGroup); - ico->add(compileSet); - compileSet->_subgraphToCompile = group; // for ref counting in SceneManager::updateCache + stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + mergeGroup->accept(stateToCompile); } } + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (!stateToCompile.empty() && ico) + { + auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group); + compileSet->buildCompileMap(ico->getContextSet(), stateToCompile); + ico->add(compileSet, false); + } + group->getBound(); group->setNodeMask(Mask_Static); group->getOrCreateUserDataContainer()->addUserObject(templateRefs); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 41cfc56a4d..a545e0674b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -260,7 +260,6 @@ namespace MWRender { mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); - mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); } mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); From 8a624e5a71aa39b81e3f69586eacd336363ff198 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 09/31] minsize based on mergedecision solves partial culling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 50 +++++++++++++++++++-------- apps/openmw/mwrender/objectpaging.hpp | 2 ++ files/settings-default.cfg | 6 ++++ 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 0d547184c1..5438347a1d 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -285,6 +285,8 @@ namespace MWRender { mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); + mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain"); + mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain"); } osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) @@ -358,7 +360,9 @@ namespace MWRender typedef std::map, InstanceList> NodeMap; NodeMap nodes; AnalyzeVisitor analyzeVisitor; - + float minSize = mMinSize; + if (mMinSizeMergeFactor) + minSize *= mMinSizeMergeFactor; for (const auto& pair : refs) { const ESM::CellRef& ref = pair.second; @@ -381,7 +385,7 @@ namespace MWRender SizeCache::iterator found = mSizeCache.find(pair.first); if (found != mSizeCache.end()) { - if (found->second < d*mMinSize) + if (found->second < d*minSize) continue; } } @@ -402,7 +406,7 @@ namespace MWRender osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); float radius = cnode->getBound().radius() * ref.mScale; - if (radius < d*mMinSize) + if (radius < d*minSize) { OpenThreads::ScopedLock lock(mSizeCacheMutex); { @@ -435,23 +439,26 @@ namespace MWRender float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; bool merge = mergeBenefit > mergeCost; - // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache - templateRefs->mObjects.push_back(cnode); - - if (pair.second.mNeedCompile) - { - int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; - if (!merge) - mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; - stateToCompile._mode = mode; - const_cast(cnode)->accept(stateToCompile); - } + float minSizeMerged = mMinSize; + float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1; + float minSizeMergeFactor2 = (1-factor2) * mMinSizeMergeFactor + factor2; + if (minSizeMergeFactor2 > 0) + minSizeMerged *= minSizeMergeFactor2; + unsigned int numinstances = 0; for (auto cref : pair.second.mInstances) { const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); + if (minSizeMerged != minSize) + { + float d = (viewPoint - pos).length(); + float radius = cnode->getBound().radius() * cref->mScale; + if (radius < d*minSizeMerged) + continue; + } + osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * @@ -474,6 +481,21 @@ namespace MWRender mergeGroup->addChild(trans); else group->addChild(trans); + ++numinstances; + } + if (numinstances > 0) + { + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + if (pair.second.mNeedCompile) + { + int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; + if (!merge) + mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + stateToCompile._mode = mode; + const_cast(cnode)->accept(stateToCompile); + } } } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 71a0db9962..c85fe3705b 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -42,6 +42,8 @@ namespace MWRender Resource::SceneManager* mSceneManager; float mMergeFactor; float mMinSize; + float mMinSizeMergeFactor; + float mMinSizeCostMultiplier; OpenThreads::Mutex mDisabledMutex; std::set mDisabled; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ec5e870168..bb21df73e3 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -115,6 +115,12 @@ object paging merge factor = 1500 # Cull objects smaller than this size divided by distance object paging min size = 0.01 +# Adjusts 'min size' based on merging decision. Allows inexpensive objects to be rendered from a greater distance. +object paging min size merge factor = 0.6 + +# Controls how inexpensive an object needs to be to utilize 'min size merge factor'. +object paging min size cost multiplier = 4 + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From 00e56ae8624e8b75102945efb6a2f13695f78d4a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 5 May 2020 13:37:00 +0000 Subject: [PATCH 10/31] batch debug colours Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 29 +++++++++++++++++++++++++++ apps/openmw/mwrender/objectpaging.hpp | 1 + files/settings-default.cfg | 5 ++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 5438347a1d..dc84ccdd2d 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include "apps/openmw/mwworld/esmstore.hpp" #include "apps/openmw/mwbase/environment.hpp" @@ -279,10 +281,31 @@ namespace MWRender StateSetCounter mGlobalStateSetCounter; }; + class DebugVisitor : public osg::NodeVisitor + { + public: + DebugVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {} + virtual void apply(osg::Drawable& node) + { + osg::ref_ptr m (new osg::Material); + osg::Vec4f color(Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f); + color.normalize(); + m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); + m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); + m->setColorMode(osg::Material::OFF); + m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color)); + osg::ref_ptr stateset = node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet; + stateset->setAttribute(m); + stateset->addUniform(new osg::Uniform("colorMode", 0)); + node.setStateSet(stateset); + } + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { + mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain"); @@ -518,6 +541,12 @@ namespace MWRender stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; mergeGroup->accept(stateToCompile); } + + if (mDebugBatches) + { + DebugVisitor dv; + mergeGroup->accept(dv); + } } auto ico = mSceneManager->getIncrementalCompileOperation(); diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index c85fe3705b..57121ae76c 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -40,6 +40,7 @@ namespace MWRender private: Resource::SceneManager* mSceneManager; + bool mDebugBatches; float mMergeFactor; float mMinSize; float mMinSizeMergeFactor; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index bb21df73e3..4a8eb889ee 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -109,7 +109,7 @@ max composite geometry size = 4.0 # Load far objects on terrain object paging = true -# Affects the likelyhood of objects being merged. A higher value means merging is more likely and improves FPS at the cost of memory. +# Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 1500 # Cull objects smaller than this size divided by distance @@ -121,6 +121,9 @@ object paging min size merge factor = 0.6 # Controls how inexpensive an object needs to be to utilize 'min size merge factor'. object paging min size cost multiplier = 4 +# Assign a random color to merged batches. +object paging debug batches = false + [Fog] # If true, use extended fog parameters for distant terrain not controlled by From 77b92aee9cd4b6282f5b3600b075994becc9f968 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 11/31] fix shadowsglitch by bounds overflow Signed-off-by: Bret Curtis --- components/terrain/quadtreenode.cpp | 7 ------- components/terrain/quadtreenode.hpp | 2 -- components/terrain/quadtreeworld.cpp | 2 ++ files/settings-default.cfg | 4 ++-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index a28554be94..ddb2c611bb 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -171,8 +171,6 @@ void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; mValidBounds = boundingBox.valid(); - dirtyBound(); - getBound(); } const osg::BoundingBox &QuadTreeNode::getBoundingBox() const @@ -180,11 +178,6 @@ const osg::BoundingBox &QuadTreeNode::getBoundingBox() const return mBoundingBox; } -osg::BoundingSphere QuadTreeNode::computeBound() const -{ - return osg::BoundingSphere(mBoundingBox); -} - float QuadTreeNode::getSize() const { return mSize; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 4adbc60250..0d4cf78077 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -91,8 +91,6 @@ namespace Terrain const osg::BoundingBox& getBoundingBox() const; bool hasValidBounds() const { return mValidBounds; } - virtual osg::BoundingSphere computeBound() const; - /// size in cell coordinates float getSize() const; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 69291f6d8c..d13a3989d1 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -140,6 +140,8 @@ public: addChildren(mRootNode); mRootNode->initNeighbours(); + float cellWorldSize = mStorage->getCellWorldSize(); + mRootNode->setInitialBound(osg::BoundingSphere(osg::BoundingBox(osg::Vec3(mMinX*cellWorldSize, mMinY*cellWorldSize, 0), osg::Vec3(mMaxX*cellWorldSize, mMaxY*cellWorldSize, 0)))); } void addChildren(QuadTreeNode* parent) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 4a8eb889ee..f250b68aa2 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -826,8 +826,8 @@ enable debug hud = false # Enable the debug overlay to see where each shadow map affects. enable debug overlay = false -# Attempt to better use the shadow map by making them cover a smaller area. May have a minor to major performance impact. -compute tight scene bounds = true +# Attempt to better use the shadow map by making them cover a smaller area. May have a major performance impact. +compute tight scene bounds = false # How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations. shadow map resolution = 1024 From b7b31926a844d0608915826c5d9c4c9421704be2 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 12/31] fix map glitch + cleanup Signed-off-by: Bret Curtis --- apps/openmw/mwrender/localmap.cpp | 8 +------ components/terrain/quadtreenode.cpp | 22 ----------------- components/terrain/quadtreenode.hpp | 3 --- components/terrain/quadtreeworld.cpp | 36 +++------------------------- components/terrain/quadtreeworld.hpp | 2 +- 5 files changed, 5 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 416a753ebc..12401f45ea 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -168,11 +168,10 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) { osg::ref_ptr camera (new osg::Camera); - camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector); - camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -360,11 +359,6 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) osg::ref_ptr camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize, osg::Vec3d(0,1,0), zmin, zmax); - camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod"); - std::ostringstream stream; - stream << x << " " << y; - camera->getOrCreateUserDataContainer()->addDescription(stream.str()); - setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index ddb2c611bb..1f5a3e4b3c 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -128,28 +128,6 @@ void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodC } } -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 - { - for (unsigned int i=0; itraverseTo(vd, size, center); - } -} - void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) { if (!hasValidBounds()) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 0d4cf78077..74abea362f 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -100,9 +100,6 @@ namespace Terrain /// Traverse nodes according to LOD selection. void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); - /// Traverse to a specific node and add only that node. - void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); - /// Adds all leaf nodes which intersect the line from start to end void intersect(ViewData* vd, TerrainLineIntersector& intersector); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index d13a3989d1..40d2c823b8 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -70,7 +70,7 @@ public: float halfSize = node->getSize()/2; const osg::Vec2f& center = node->getCenter(); osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); - bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) <= std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) <= std::min(nodeBounds.w(), mActiveGrid.w())); + bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) < std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) < std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border if (intersects) return false; @@ -430,21 +430,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (isCullVisitor) { osgUtil::CullVisitor* cv = static_cast(&nv); - - osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); - if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") - { - std::istringstream stream(udc->getDescriptions()[1]); - int x,y; - stream >> x; - stream >> y; - mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); - } - else - { - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); - mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); - } + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); + mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); } else { @@ -517,23 +504,6 @@ void QuadTreeWorld::enable(bool enabled) mRootNode->setNodeMask(enabled ? ~0 : 0); } -void QuadTreeWorld::cacheCell(View *view, int x, int y) -{ - ensureQuadTreeBuilt(); - osg::Vec4i grid (x,y,x+1,y+1); - ViewData* vd = static_cast(view); - vd->setActiveGrid(grid); - mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5f,y+0.5f)); - - const float cellWorldSize = mStorage->getCellWorldSize(); - - for (unsigned int i=0; igetNumEntries(); ++i) - { - ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); - } -} - View* QuadTreeWorld::createView() { return new ViewData; diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 2010548523..15ef0634aa 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -31,7 +31,7 @@ namespace Terrain virtual void setViewDistance(float distance) { mViewDistance = distance; } - void cacheCell(View *view, int x, int y); + void cacheCell(View *view, int x, int y) {} /// @note Not thread safe. virtual void loadCell(int x, int y); /// @note Not thread safe. From 4dccabeb83231557a353b22588043e32366ec062 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Wed, 6 May 2020 13:37:00 +0000 Subject: [PATCH 13/31] fix analyzation not taking instancecount in account + settings calibration Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 7 +++++++ files/settings-default.cfg | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index dc84ccdd2d..d30bd7a639 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -264,6 +264,11 @@ namespace MWRender mCurrentStateSet = nullptr; return result; } + void addInstance(const Result& result) + { + for (auto pair : result.mStateSetCounter) + mGlobalStateSetCounter[pair.first] += pair.second; + } float getMergeBenefit(const Result& result) { if (result.mStateSetCounter.empty()) return 1; @@ -445,6 +450,8 @@ namespace MWRender emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; } + else + analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index f250b68aa2..b8c151e56b 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -110,16 +110,16 @@ max composite geometry size = 4.0 object paging = true # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. -object paging merge factor = 1500 +object paging merge factor = 250 # Cull objects smaller than this size divided by distance object paging min size = 0.01 # Adjusts 'min size' based on merging decision. Allows inexpensive objects to be rendered from a greater distance. -object paging min size merge factor = 0.6 +object paging min size merge factor = 0.3 # Controls how inexpensive an object needs to be to utilize 'min size merge factor'. -object paging min size cost multiplier = 4 +object paging min size cost multiplier = 25 # Assign a random color to merged batches. object paging debug batches = false From da92ad329b23c91f4baa7d36b00e7b59a374d25b Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 7 May 2020 13:37:00 +0000 Subject: [PATCH 14/31] move renderbin Signed-off-by: Bret Curtis --- components/terrain/chunkmanager.cpp | 9 ++++++++- components/terrain/chunkmanager.hpp | 2 ++ components/terrain/material.cpp | 25 +++++++++++++------------ components/terrain/terraindrawable.cpp | 6 ++++++ components/terrain/world.cpp | 7 ------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 5f80b9d367..3c3bd0f9d5 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -31,7 +32,11 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T , mCompositeMapLevel(1.f) , mMaxCompGeometrySize(1.f) { - + mMultiPassRoot = new osg::StateSet; + mMultiPassRoot->setRenderingHint(osg::StateSet::OPAQUE_BIN); + osg::ref_ptr material (new osg::Material); + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON); } osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) @@ -196,6 +201,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->createClusterCullingCallback(); + geometry->setStateSet(mMultiPassRoot); + if (useCompositeMap) { osg::ref_ptr compositeMap = new CompositeMap; diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 87770fafb4..11e5769deb 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -65,6 +65,8 @@ namespace Terrain CompositeMapRenderer* mCompositeMapRenderer; BufferCache mBufferCache; + osg::ref_ptr mMultiPassRoot; + unsigned int mNodeMask; unsigned int mCompositeMapSize; diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index fd385e793c..e662f4439f 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -183,17 +183,20 @@ namespace Terrain osg::ref_ptr stateset (new osg::StateSet); - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - - if (!firstLayer) + if (!blendmaps.empty()) { - stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); - } - else - { - stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setRenderBinDetails(passIndex++, "RenderBin"); + if (!firstLayer) + { + stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); + } + else + { + stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); + } } int texunit = 0; @@ -268,8 +271,6 @@ namespace Terrain } - stateset->setRenderBinDetails(passIndex++, "RenderBin"); - passes.push_back(stateset); } return passes; diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index 9593687cfd..0d82be4fff 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -102,6 +102,10 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv); + osg::StateSet* stateset = getStateSet(); + if (stateset) + cv->pushStateSet(stateset); + for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) { cv->pushStateSet(*it); @@ -109,6 +113,8 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) cv->popStateSet(); } + if (stateset) + cv->popStateSet(); if (pushedLight) cv->popStateSet(); } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index fcacb442ac..5b4807b387 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -1,7 +1,6 @@ #include "world.hpp" #include -#include #include #include @@ -23,12 +22,6 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); - mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); - - osg::ref_ptr material (new osg::Material); - material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); - mTerrainRoot->setName("Terrain Root"); osg::ref_ptr compositeCam = new osg::Camera; From ffbed7ee38dc7fe7afb1a85a98bef241f83d69a5 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 7 May 2020 13:37:00 +0000 Subject: [PATCH 15/31] loadingscreen Signed-off-by: Bret Curtis --- apps/openmw/mwworld/cellpreloader.cpp | 52 +++++++++++++++++++++++---- apps/openmw/mwworld/cellpreloader.hpp | 3 ++ apps/openmw/mwworld/scene.cpp | 28 +++++++++++++++ apps/openmw/mwworld/scene.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 3 +- components/terrain/quadtreeworld.cpp | 8 +++-- components/terrain/quadtreeworld.hpp | 2 +- components/terrain/world.hpp | 2 +- 8 files changed, 88 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 60b9a3220e..8f61673b34 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -174,6 +174,8 @@ namespace MWWorld public: TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) + , mProgress(views.size()) + , mProgressRange(0) , mTerrainViews(views) , mWorld(world) , mPreloadPositions(preloadPositions) @@ -191,7 +193,7 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange); } } @@ -200,8 +202,13 @@ namespace MWWorld mAbort = true; } + int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; } + int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; } + private: std::atomic mAbort; + std::vector> mProgress; + int mProgressRange; std::vector > mTerrainViews; Terrain::World* mWorld; std::vector mPreloadPositions; @@ -328,9 +335,6 @@ namespace MWWorld } mPreloadCells.erase(found); - - if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone()) - mTerrainPreloadItem->storeViews(0.0); } } @@ -415,6 +419,38 @@ namespace MWWorld mUnrefQueue = unrefQueue; } + bool CellPreloader::getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp) + { + if (!mTerrainPreloadItem) + return false; + else if (mTerrainPreloadItem->isDone()) + { + mTerrainPreloadItem->storeViews(timestamp); + mTerrainPreloadItem = nullptr; + return false; + } + else + { + progress = mTerrainPreloadItem->getProgress(); + progressRange = mTerrainPreloadItem->getProgressRange(); + return !progress || progress < progressRange; + } + } + + void CellPreloader::abortTerrainPreloadExcept(const osg::Vec3f& exceptPos) + { + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + { + const float resetThreshold = ESM::Land::REAL_SIZE; + for (auto pos : mTerrainPreloadPositions) + if ((pos.first-exceptPos).length2() < resetThreshold*resetThreshold) + return; + mTerrainPreloadItem->abort(); + mTerrainPreloadItem->waitTillDone(); + mTerrainPreloadItem = nullptr; + } + } + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) @@ -436,8 +472,12 @@ namespace MWWorld } mTerrainPreloadPositions = positions; - mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); - mWorkQueue->addWorkItem(mTerrainPreloadItem); + + if (!positions.empty()) + { + mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); + mWorkQueue->addWorkItem(mTerrainPreloadItem); + } } } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index ef28170190..386fee2932 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -72,6 +72,9 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); + bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); + void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos); + private: Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1dac18eaa9..f8c16ca370 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -849,15 +849,42 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); + preloadTerrain(position.asVec3()); + changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); + checkTerrainLoaded(); + if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } + void Scene::checkTerrainLoaded() + { + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int progress = 0, initialProgress = -1, progressRange = 0; + while (mPreloader->getTerrainPreloadInProgress(progress, progressRange, mRendering.getReferenceTime())) + { + if (initialProgress == -1) + { + loadingListener->setLabel("#{sLoadingMessage4}"); + initialProgress = progress; + } + if (progress) + { + loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); + loadingListener->setProgress(progress-initialProgress); + } + else + loadingListener->setProgress(0); + OpenThreads::Thread::microSleep(5000); + } + } + CellStore* Scene::getCurrentCell () { return mCurrentCell; @@ -1102,6 +1129,7 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { + mPreloader->abortTerrainPreloadExcept(pos); std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); mPreloader->setTerrainPreloadPositions(vec); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 9b8403c389..349dce4239 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -101,6 +101,8 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void checkTerrainLoaded(); + osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0817a42263..5f4f9d1cd1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -243,7 +243,6 @@ namespace MWWorld if (bypass && !mStartCell.empty()) { ESM::Position pos; - if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos, true); @@ -384,9 +383,9 @@ namespace MWWorld mPlayer->readRecord(reader, type); if (getPlayerPtr().isInCell()) { - mWorldScene->preloadCell(getPlayerPtr().getCell(), true); if (getPlayerPtr().getCell()->isExterior()) mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3()); + mWorldScene->preloadCell(getPlayerPtr().getCell(), true); } break; default: diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 40d2c823b8..cdb9e61910 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -509,7 +509,7 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) { ensureQuadTreeBuilt(); @@ -519,12 +519,16 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); - const float cellWorldSize = mStorage->getCellWorldSize(); + if (!progressTotal) + for (unsigned int i=0; igetNumEntries(); ++i) + progressTotal += vd->getEntry(i).mNode->getSize(); + const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); + progress += entry.mNode->getSize(); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 15ef0634aa..97fcd004d8 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,7 +38,7 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort); + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index e559321693..dba79994ad 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -147,7 +147,7 @@ namespace Terrain /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. From c1ebd9474bdafb7c252428c83927802eac09b269 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 16/31] stop navmesh updates when ai off Signed-off-by: Bret Curtis --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 8 ++++++++ components/detournavigator/navigator.hpp | 5 +++++ components/detournavigator/navigatorimpl.cpp | 8 ++++++++ components/detournavigator/navigatorimpl.hpp | 3 +++ components/detournavigator/navigatorstub.hpp | 2 ++ 5 files changed, 26 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5f5e7d13b0..6b175239eb 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include "../mwworld/esmstore.hpp" @@ -900,6 +902,12 @@ namespace MWMechanics bool MechanicsManager::toggleAI() { mAI = !mAI; + + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->getNavigator()->setUpdatesEnabled(mAI); + if (mAI) + world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3()); + return mAI; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index cfdf922324..fa2faf383a 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -155,6 +155,11 @@ namespace DetourNavigator */ virtual void update(const osg::Vec3f& playerPosition) = 0; + /** + * @brief disable navigator updates + */ + virtual void setUpdatesEnabled(bool enabled) = 0; + /** * @brief wait locks thread until all tiles are updated from last update call. */ diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index d7889629eb..c47cf97662 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -12,6 +12,7 @@ namespace DetourNavigator NavigatorImpl::NavigatorImpl(const Settings& settings) : mSettings(settings) , mNavMeshManager(mSettings) + , mUpdatesEnabled(true) { } @@ -138,11 +139,18 @@ namespace DetourNavigator void NavigatorImpl::update(const osg::Vec3f& playerPosition) { + if (!mUpdatesEnabled) + return; removeUnusedNavMeshes(); for (const auto& v : mAgents) mNavMeshManager.update(playerPosition, v.first); } + void NavigatorImpl::setUpdatesEnabled(bool enabled) + { + mUpdatesEnabled = enabled; + } + void NavigatorImpl::wait() { mNavMeshManager.wait(); diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 66a4d8bb34..f99aab05a9 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -46,6 +46,8 @@ namespace DetourNavigator void update(const osg::Vec3f& playerPosition) override; + void setUpdatesEnabled(bool enabled) override; + void wait() override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; @@ -61,6 +63,7 @@ namespace DetourNavigator private: Settings mSettings; NavMeshManager mNavMeshManager; + bool mUpdatesEnabled; std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 9c379338ff..9279e77e3a 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -66,6 +66,8 @@ namespace DetourNavigator void update(const osg::Vec3f& /*playerPosition*/) override {} + void setUpdatesEnabled(bool enabled) override {} + void wait() override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override From 17637c65759fd93008672dc1704dd0dd05783fea Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 17/31] pagerebuild on disable Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 47 ++++++++++++++++++++--- apps/openmw/mwrender/objectpaging.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +-- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 10 ++++- components/resource/objectcache.hpp | 4 +- components/terrain/quadtreeworld.cpp | 7 ++++ components/terrain/quadtreeworld.hpp | 1 + components/terrain/texturemanager.cpp | 2 +- components/terrain/viewdata.cpp | 20 ++++++++++ components/terrain/viewdata.hpp | 2 + components/terrain/world.hpp | 2 + 12 files changed, 91 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index d30bd7a639..eb71f0993c 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -576,17 +576,52 @@ namespace MWRender return Mask_Static; } - void ObjectPaging::enableObject(const ESM::RefNum & refnum, bool enabled) + struct ClearCacheFunctor { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (enabled) mDisabled.erase(refnum); - else mDisabled.insert(refnum); + void operator()(MWRender::ChunkId id, osg::Object* obj) + { + if (intersects(id, mPosition)) + mToClear.insert(id); + } + bool intersects(ChunkId id, osg::Vec3f pos) + { + pos /= ESM::Land::REAL_SIZE; + osg::Vec2f center = std::get<0>(id); + float halfSize = std::get<1>(id)/2; + return pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize; + } + osg::Vec3f mPosition; + std::set mToClear; + }; + + bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) + { + if (!typeFilter(type, false)) + return false; + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (enabled && !mDisabled.erase(refnum)) return false; + if (!enabled && !mDisabled.insert(refnum).second) return false; + } + + ClearCacheFunctor ccf; + ccf.mPosition = pos; + mCache->call(ccf); + for (auto chunk : ccf.mToClear) + mCache->removeFromObjectCache(chunk); + return true; } void ObjectPaging::clear() { - OpenThreads::ScopedLock lock(mDisabledMutex); - mDisabled.clear(); + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (mDisabled.empty()) + return; + mDisabled.clear(); + } + mCache->clear(); } void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 57121ae76c..93423f21e8 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -33,7 +33,9 @@ namespace MWRender virtual unsigned int getNodeMask() override; - void enableObject(const ESM::RefNum & refnum, bool enabled); + /// @return true if something changed + bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled); + void clear(); void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a545e0674b..9d435a6123 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1481,9 +1481,9 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } - void RenderingManager::pagingEnableObject(const ESM::RefNum & refnum, bool enabled) + void RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { - if (mObjectPaging) - mObjectPaging->enableObject(refnum, enabled); + if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) + mTerrain->clearCachedViews(ptr.getRefData().getPosition().asVec3()); } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 6bf122232f..36df09202d 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -241,7 +241,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); - void pagingEnableObject(const ESM::RefNum & refnum, bool enabled); + void pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5f4f9d1cd1..fdb54fb756 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -815,7 +815,10 @@ namespace MWWorld mWorldScene->addObjectToScene (reference); if (reference.getCellRef().getRefNum().hasContentFile()) - mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), true); + { + int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); + mRendering->pagingEnableObject(type, reference, true); + } } } @@ -853,7 +856,10 @@ namespace MWWorld reference.getRefData().disable(); if (reference.getCellRef().getRefNum().hasContentFile()) - mRendering->pagingEnableObject(reference.getCellRef().getRefNum(), false); + { + int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); + mRendering->pagingEnableObject(type, reference, false); + } if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->removeObjectFromScene (reference); diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index cfd41f19cf..24d7d04a97 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -161,13 +161,13 @@ class GenericObjectCache : public osg::Referenced } } - /** call operator()(osg::Object*) for each object in the cache. */ + /** call operator()(KeyType, osg::Object*) for each object in the cache. */ template void call(Functor& f) { OpenThreads::ScopedLock lock(_objectCacheMutex); for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) - f(it->second.first.get()); + f(it->first, it->second.first.get()); } /** Get the number of objects in the cache. */ diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index cdb9e61910..92bd09bd0f 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -574,4 +574,11 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); } +void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos) +{ +//mViewDataMap->clear(); + osg::Vec3 pos_ = pos / mStorage->getCellWorldSize(); + mViewDataMap->clearCachedViews(pos_); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 97fcd004d8..7c01f5c27e 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -40,6 +40,7 @@ namespace Terrain View* createView(); void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); void storeView(const View* view, double referenceTime); + void clearCachedViews(const osg::Vec3f& pos) override; void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/texturemanager.cpp b/components/terrain/texturemanager.cpp index b901fa1590..c28b13f1dc 100644 --- a/components/terrain/texturemanager.cpp +++ b/components/terrain/texturemanager.cpp @@ -25,7 +25,7 @@ struct UpdateTextureFilteringFunctor } Resource::SceneManager* mSceneManager; - void operator()(osg::Object* obj) + void operator()(std::string, osg::Object* obj) { mSceneManager->applyFilterSettings(static_cast(obj)); } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 6399425a40..e5e856cadf 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -1,5 +1,7 @@ #include "viewdata.hpp" +#include "quadtreenode.hpp" + namespace Terrain { @@ -99,6 +101,18 @@ bool ViewData::contains(QuadTreeNode *node) return false; } +bool intersects(const osg::Vec2f& center, float halfSize, osg::Vec3f pos) +{ + return (pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize); +} + +void ViewData::clearCache(const osg::Vec3f &cellPos) +{ + for (Entry& entry : mEntries) + if (entry.mNode && intersects(entry.mNode->getCenter(), entry.mNode->getSize()/2.f, cellPos)) + entry.mRenderingNode = nullptr; +} + ViewData::Entry::Entry() : mNode(nullptr) , mLodFlags(0) @@ -188,6 +202,12 @@ void ViewDataMap::clearUnusedViews(double referenceTime) } } +void ViewDataMap::clearCachedViews(const osg::Vec3f &cellPos) +{ + for (auto pair : mViews) + pair.second->clearCache(cellPos); +} + void ViewDataMap::clear() { mViews.clear(); diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 87b45f57b1..f21f56ae11 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -24,6 +24,7 @@ namespace Terrain void reset(); void clear(); + void clearCache(const osg::Vec3f &cellPos); bool contains(QuadTreeNode* node); @@ -84,6 +85,7 @@ namespace Terrain ViewData* createOrReuseView(); void clearUnusedViews(double referenceTime); + void clearCachedViews(const osg::Vec3f &cellPos); void clear(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index dba79994ad..4ad4e5f0ac 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -153,6 +153,8 @@ namespace Terrain /// @note Not thread safe. virtual void storeView(const View* view, double referenceTime) {} + virtual void clearCachedViews(const osg::Vec3f& pos) {} + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} virtual void setViewDistance(float distance) {} From b4af2ac6725430b9547006c87ee0daed2be73b93 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 8 May 2020 13:37:00 +0000 Subject: [PATCH 18/31] avoid blocking on pagerebuild Signed-off-by: Bret Curtis --- apps/openmw/mwrender/renderingmanager.cpp | 10 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/cellpreloader.cpp | 44 ++++++-- apps/openmw/mwworld/cellpreloader.hpp | 1 + apps/openmw/mwworld/scene.cpp | 5 + apps/openmw/mwworld/scene.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 6 +- components/terrain/quadtreenode.hpp | 1 - components/terrain/quadtreeworld.cpp | 20 ++-- components/terrain/quadtreeworld.hpp | 4 +- components/terrain/viewdata.cpp | 117 ++++++++++++---------- components/terrain/viewdata.hpp | 24 +++-- components/terrain/world.hpp | 4 +- 13 files changed, 150 insertions(+), 89 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9d435a6123..569f40483c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1481,9 +1481,15 @@ namespace MWRender { mTerrain->setActiveGrid(grid); } - void RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) + bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { + if (!ptr.isInCell() || !ptr.getCell()->isExterior()) + return false; if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) - mTerrain->clearCachedViews(ptr.getRefData().getPosition().asVec3()); + { + mTerrain->rebuildViews(); + return true; + } + return false; } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 36df09202d..b45e3c8dc8 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -241,7 +241,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); - void pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 8f61673b34..4280e1e681 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -182,10 +182,12 @@ namespace MWWorld { } - void storeViews(double referenceTime) + bool storeViews(double referenceTime) { for (unsigned int i=0; istoreView(mTerrainViews[i], referenceTime); + if (!mWorld->storeView(mTerrainViews[i], referenceTime)) + return false; + return true; } virtual void doWork() @@ -244,6 +246,7 @@ namespace MWWorld , mMaxCacheSize(0) , mPreloadInstances(true) , mLastResourceCacheUpdate(0.0) + , mStoreViewsFailCount(0) { } @@ -379,7 +382,17 @@ namespace MWWorld if (mTerrainPreloadItem && mTerrainPreloadItem->isDone()) { - mTerrainPreloadItem->storeViews(timestamp); + if (!mTerrainPreloadItem->storeViews(timestamp)) + { + if (++mStoreViewsFailCount > 100) + { + OSG_ALWAYS << "paging views are rebuilt every frame, please check for faulty enable/disable scripts." << std::endl; + mStoreViewsFailCount = 0; + } + setTerrainPreloadPositions(std::vector()); + } + else + mStoreViewsFailCount = 0; mTerrainPreloadItem = nullptr; } } @@ -451,11 +464,31 @@ namespace MWWorld } } + bool contains(const std::vector& container, const std::vector& contained) + { + for (auto pos : contained) + { + bool found = false; + for (auto pos2 : container) + { + if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second) + { + found = true; + break; + } + } + if (!found) return false; + } + return true; + } + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { - if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + if (positions.empty()) + mTerrainPreloadPositions.clear(); + else if (contains(mTerrainPreloadPositions, positions)) return; - else if (positions == mTerrainPreloadPositions) + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) return; else { @@ -472,7 +505,6 @@ namespace MWWorld } mTerrainPreloadPositions = positions; - if (!positions.empty()) { mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 386fee2932..c170787357 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -88,6 +88,7 @@ namespace MWWorld bool mPreloadInstances; double mLastResourceCacheUpdate; + int mStoreViewsFailCount; struct PreloadEntry { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index f8c16ca370..36f877bee2 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1135,6 +1135,11 @@ namespace MWWorld mPreloader->setTerrainPreloadPositions(vec); } + void Scene::reloadTerrain() + { + mPreloader->setTerrainPreloadPositions(std::vector()); + } + struct ListFastTravelDestinationsVisitor { ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 349dce4239..5727f13919 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -115,6 +115,7 @@ namespace MWWorld void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); void preloadTerrain(const osg::Vec3f& pos); + void reloadTerrain(); void unloadCell (CellStoreCollection::iterator iter, bool test = false); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fdb54fb756..8392a827cf 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -817,7 +817,8 @@ namespace MWWorld if (reference.getCellRef().getRefNum().hasContentFile()) { int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); - mRendering->pagingEnableObject(type, reference, true); + if (mRendering->pagingEnableObject(type, reference, true)) + mWorldScene->reloadTerrain(); } } } @@ -858,7 +859,8 @@ namespace MWWorld if (reference.getCellRef().getRefNum().hasContentFile()) { int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); - mRendering->pagingEnableObject(type, reference, false); + if (mRendering->pagingEnableObject(type, reference, false)) + mWorldScene->reloadTerrain(); } if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 74abea362f..147ca8c8a8 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -53,7 +53,6 @@ namespace Terrain virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; - class ViewDataMap; class ViewData; class QuadTreeNode : public osg::Group diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 92bd09bd0f..c7544dbe29 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -253,7 +253,6 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour QuadTreeWorld::~QuadTreeWorld() { - mViewDataMap->clear(); } /// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set. @@ -279,7 +278,7 @@ unsigned int getVertexLod(QuadTreeNode* node, int vertexLodMod) } /// get the flags to use for stitching in the index buffer so that chunks of different LOD connect seamlessly -unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewData* vd) +unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, const ViewData* vd) { unsigned int lodFlags = 0; for (unsigned int i=0; i<4; ++i) @@ -506,7 +505,7 @@ void QuadTreeWorld::enable(bool enabled) View* QuadTreeWorld::createView() { - return new ViewData; + return mViewDataMap->createIndependentView(); } void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) @@ -533,14 +532,9 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: vd->markUnchanged(); } -void QuadTreeWorld::storeView(const View* view, double referenceTime) +bool QuadTreeWorld::storeView(const View* view, double referenceTime) { - osg::ref_ptr dummy = new osg::DummyObject; - const ViewData* vd = static_cast(view); - bool needsUpdate = false; - ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), vd->getActiveGrid(), needsUpdate); - stored->copyFrom(*vd); - stored->setLastUsageTimeStamp(referenceTime); + return mViewDataMap->storeView(static_cast(view), referenceTime); } void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) @@ -574,11 +568,9 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m) mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); } -void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos) +void QuadTreeWorld::rebuildViews() { -//mViewDataMap->clear(); - osg::Vec3 pos_ = pos / mStorage->getCellWorldSize(); - mViewDataMap->clearCachedViews(pos_); + mViewDataMap->rebuildViews(); } } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7c01f5c27e..0cd2526dec 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -39,8 +39,8 @@ namespace Terrain View* createView(); void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); - void storeView(const View* view, double referenceTime); - void clearCachedViews(const osg::Vec3f& pos) override; + bool storeView(const View* view, double referenceTime); + void rebuildViews() override; void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index e5e856cadf..8a87690005 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -10,6 +10,7 @@ ViewData::ViewData() , mLastUsageTimeStamp(0.0) , mChanged(false) , mHasViewPoint(false) + , mWorldUpdateRevision(0) { } @@ -27,6 +28,7 @@ void ViewData::copyFrom(const ViewData& other) mHasViewPoint = other.mHasViewPoint; mViewPoint = other.mViewPoint; mActiveGrid = other.mActiveGrid; + mWorldUpdateRevision = other.mWorldUpdateRevision; } void ViewData::add(QuadTreeNode *node) @@ -93,7 +95,12 @@ void ViewData::clear() mHasViewPoint = false; } -bool ViewData::contains(QuadTreeNode *node) +bool ViewData::suitableToUse(const osg::Vec4i &activeGrid) const +{ + return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries(); +} + +bool ViewData::contains(QuadTreeNode *node) const { for (unsigned int i=0; i= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize); -} - -void ViewData::clearCache(const osg::Vec3f &cellPos) -{ - for (Entry& entry : mEntries) - if (entry.mNode && intersects(entry.mNode->getCenter(), entry.mNode->getSize()/2.f, cellPos)) - entry.mRenderingNode = nullptr; -} - ViewData::Entry::Entry() : mNode(nullptr) , mLodFlags(0) @@ -133,86 +128,106 @@ bool ViewData::Entry::set(QuadTreeNode *node) } } -bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist, const osg::Vec4i& activeGrid) -{ - return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist && vd->getActiveGrid() == activeGrid; -} - ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate) { - Map::const_iterator found = mViews.find(viewer); + ViewerMap::const_iterator found = mViewers.find(viewer); ViewData* vd = nullptr; - if (found == mViews.end()) + if (found == mViewers.end()) { vd = createOrReuseView(); - mViews[viewer] = vd; + mViewers[viewer] = vd; } else vd = found->second; + needsUpdate = false; - if (!suitable(vd, viewPoint, mReuseDistance, activeGrid)) + if (!(vd->suitableToUse(activeGrid) && (vd->getViewPoint()-viewPoint).length2() < mReuseDistance*mReuseDistance && vd->getWorldUpdateRevision() >= mWorldUpdateRevision)) { - for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) + float shortestDist = std::numeric_limits::max(); + const ViewData* mostSuitableView = nullptr; + for (const ViewData& other : mViewVector) { - if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries()) + if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision) { - vd->copyFrom(*other->second); - needsUpdate = false; - return vd; + float dist = (viewPoint-other.getViewPoint()).length2(); + if (dist < shortestDist) + { + shortestDist = dist; + mostSuitableView = &other; + } } } + if (mostSuitableView && mostSuitableView != vd) + { + vd->copyFrom(*mostSuitableView); + return vd; + } + } + if (!vd->suitableToUse(activeGrid)) + { vd->setViewPoint(viewPoint); vd->setActiveGrid(activeGrid); needsUpdate = true; } - else - needsUpdate = false; - return vd; } +bool ViewDataMap::storeView(const ViewData* view, double referenceTime) +{ + if (view->getWorldUpdateRevision() < mWorldUpdateRevision) + return false; + ViewData* store = createOrReuseView(); + store->copyFrom(*view); + store->setLastUsageTimeStamp(referenceTime); + return true; +} + ViewData *ViewDataMap::createOrReuseView() { + ViewData* vd = nullptr; if (mUnusedViews.size()) { - ViewData* vd = mUnusedViews.front(); + vd = mUnusedViews.front(); mUnusedViews.pop_front(); - return vd; } else { mViewVector.push_back(ViewData()); - return &mViewVector.back(); + vd = &mViewVector.back(); } + vd->setWorldUpdateRevision(mWorldUpdateRevision); + return vd; +} + +ViewData *ViewDataMap::createIndependentView() const +{ + ViewData* vd = new ViewData; + vd->setWorldUpdateRevision(mWorldUpdateRevision); + return vd; } void ViewDataMap::clearUnusedViews(double referenceTime) { - for (Map::iterator it = mViews.begin(); it != mViews.end(); ) + for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end(); ) { - ViewData* vd = it->second; - if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) - { - vd->clear(); - mUnusedViews.push_back(vd); - mViews.erase(it++); - } + if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + mViewers.erase(it++); else ++it; } + for (ViewData& vd : mViewVector) + { + if (vd.getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + { + vd.clear(); + mUnusedViews.push_back(&vd); + } + } } -void ViewDataMap::clearCachedViews(const osg::Vec3f &cellPos) -{ - for (auto pair : mViews) - pair.second->clearCache(cellPos); -} - -void ViewDataMap::clear() +void ViewDataMap::rebuildViews() { - mViews.clear(); - mUnusedViews.clear(); - mViewVector.clear(); + ++mWorldUpdateRevision; } } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index f21f56ae11..ba4fba3b2a 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -23,10 +23,11 @@ namespace Terrain void reset(); + bool suitableToUse(const osg::Vec4i& activeGrid) const; + void clear(); - void clearCache(const osg::Vec3f &cellPos); - bool contains(QuadTreeNode* node); + bool contains(QuadTreeNode* node) const; void copyFrom(const ViewData& other); @@ -61,6 +62,9 @@ namespace Terrain void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} } const osg::Vec4i &getActiveGrid() const { return mActiveGrid;} + unsigned int getWorldUpdateRevision() const { return mWorldUpdateRevision; } + void setWorldUpdateRevision(int updateRevision) { mWorldUpdateRevision = updateRevision; } + private: std::vector mEntries; unsigned int mNumEntries; @@ -69,35 +73,39 @@ namespace Terrain osg::Vec3f mViewPoint; bool mHasViewPoint; osg::Vec4i mActiveGrid; + unsigned int mWorldUpdateRevision; }; class ViewDataMap : public osg::Referenced { public: ViewDataMap() - : mReuseDistance(300) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. + : mReuseDistance(150) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. // this value also serves as a threshold for when a newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the LODs won't keep loading and unloading all the time. , mExpiryDelay(1.f) + , mWorldUpdateRevision(0) {} ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate); ViewData* createOrReuseView(); + ViewData* createIndependentView() const; void clearUnusedViews(double referenceTime); - void clearCachedViews(const osg::Vec3f &cellPos); - - void clear(); + void rebuildViews(); + bool storeView(const ViewData* view, double referenceTime); private: std::list mViewVector; - typedef std::map, ViewData*> Map; - Map mViews; + typedef std::map, ViewData*> ViewerMap; + ViewerMap mViewers; float mReuseDistance; float mExpiryDelay; // time in seconds for unused view to be removed + unsigned int mWorldUpdateRevision; + std::deque mUnusedViews; }; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 4ad4e5f0ac..9f08454c8c 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -151,9 +151,9 @@ namespace Terrain /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe. - virtual void storeView(const View* view, double referenceTime) {} + virtual bool storeView(const View* view, double referenceTime) {return true;} - virtual void clearCachedViews(const osg::Vec3f& pos) {} + virtual void rebuildViews() {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} From c7fda6d28004fa9be927c0f48d4cede21773d991 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sun, 10 May 2020 13:37:00 +0000 Subject: [PATCH 19/31] activegrid paging = 2xfps Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 86 +++++++++++++++++++---- apps/openmw/mwrender/objectpaging.hpp | 9 ++- apps/openmw/mwrender/renderingmanager.cpp | 5 ++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellpreloader.cpp | 4 +- apps/openmw/mwworld/cellpreloader.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 48 ++++++++----- apps/openmw/mwworld/scene.hpp | 2 + components/terrain/quadtreenode.cpp | 18 ++--- components/terrain/quadtreenode.hpp | 10 ++- components/terrain/quadtreeworld.cpp | 35 +++++---- components/terrain/quadtreeworld.hpp | 1 - files/settings-default.cfg | 5 +- 13 files changed, 158 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index eb71f0993c..9b3165d87a 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -32,16 +33,19 @@ namespace MWRender { - bool typeFilter(int type, bool far) + bool typeFilter(int type, bool far, bool activeGrid) { switch (type) { case ESM::REC_STAT: - case ESM::REC_ACTI: - case ESM::REC_DOOR: - return true; + return true; + + case ESM::REC_ACTI: // TODO enable when intersectionvisitor supported + case ESM::REC_DOOR: + return !activeGrid; case ESM::REC_CONT: - return far ? false : true; + return far ? false : !activeGrid; + default: return false; } @@ -64,17 +68,19 @@ namespace MWRender } } - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { - if (!far)return nullptr; - ChunkId id = std::make_tuple(center, size); + if (activeGrid && !mActiveGrid) + return nullptr; + + ChunkId id = std::make_tuple(center, size, activeGrid); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); if (obj) return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, viewPoint, compile); + osg::ref_ptr node = createChunk(size, center, activeGrid, viewPoint, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -231,6 +237,15 @@ namespace MWRender std::vector> mObjects; }; + class RefnumSet : public osg::Object + { + public: + RefnumSet(){} + RefnumSet(const RefnumSet& copy, const osg::CopyOp&) : mRefnums(copy.mRefnums) {} + META_Object(MWRender, RefnumSet) + std::set mRefnums; + }; + class AnalyzeVisitor : public osg::NodeVisitor { public: @@ -310,6 +325,7 @@ namespace MWRender : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { + mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain"); mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); @@ -317,7 +333,7 @@ namespace MWRender mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain"); } - osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); @@ -349,7 +365,7 @@ namespace MWRender { if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2)) continue; + if (!typeFilter(type,size>=2,activeGrid)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; } @@ -365,7 +381,7 @@ namespace MWRender bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2)) continue; + if (!typeFilter(type,size>=2,activeGrid)) continue; refs[ref.mRefNum] = ref; } } @@ -387,6 +403,7 @@ namespace MWRender }; typedef std::map, InstanceList> NodeMap; NodeMap nodes; + osg::ref_ptr refnumSet = activeGrid ? new RefnumSet : nullptr; AnalyzeVisitor analyzeVisitor; float minSize = mMinSize; if (mMinSizeMergeFactor) @@ -443,6 +460,9 @@ namespace MWRender continue; } + if (activeGrid && cnode->getNumChildrenRequiringUpdateTraversal() > 0) + continue; + auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { @@ -453,6 +473,9 @@ namespace MWRender else analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); + + if (activeGrid) + refnumSet->mRefnums.insert(pair.first); } osg::ref_ptr group = new osg::Group; @@ -566,7 +589,13 @@ namespace MWRender group->getBound(); group->setNodeMask(Mask_Static); - group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); + if (activeGrid) + { + udc->addUserObject(refnumSet); + group->addCullCallback(new SceneUtil::LightListCallback); + } + udc->addUserObject(templateRefs); return group; } @@ -596,7 +625,7 @@ namespace MWRender bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) { - if (!typeFilter(type, false)) + if (!typeFilter(type, false, false)) return false; { @@ -624,6 +653,35 @@ namespace MWRender mCache->clear(); } + struct GetRefnumsFunctor + { + GetRefnumsFunctor(std::set& output) : mOutput(output) {} + void operator()(MWRender::ChunkId chunkId, osg::Object* obj) + { + if (!std::get<2>(chunkId)) return; + const osg::Vec2f& center = std::get<0>(chunkId); + bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w()); + if (!activeGrid) return; + + osg::UserDataContainer* udc = obj->getUserDataContainer(); + if (udc && udc->getNumUserObjects()) + { + RefnumSet* refnums = dynamic_cast(udc->getUserObject(0)); + if (!refnums) return; + mOutput.insert(refnums->mRefnums.begin(), refnums->mRefnums.end()); + } + } + osg::Vec4i mActiveGrid; + std::set& mOutput; + }; + + void ObjectPaging::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) + { + GetRefnumsFunctor grf(out); + grf.mActiveGrid = activeGrid; + mCache->call(grf); + } + void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize()); diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 93423f21e8..79591dcc41 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -19,7 +19,7 @@ namespace MWWorld namespace MWRender { - typedef std::tuple ChunkId; // Center, Size + typedef std::tuple ChunkId; // Center, Size, ActiveGrid class ObjectPaging : public Resource::GenericResourceManager, public Terrain::QuadTreeWorld::ChunkManager { @@ -27,9 +27,9 @@ namespace MWRender ObjectPaging(Resource::SceneManager* sceneManager); ~ObjectPaging() = default; - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile); virtual unsigned int getNodeMask() override; @@ -40,8 +40,11 @@ namespace MWRender void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; + void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); + private: Resource::SceneManager* mSceneManager; + bool mActiveGrid; bool mDebugBatches; float mMergeFactor; float mMinSize; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 569f40483c..5523a69369 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1492,4 +1492,9 @@ namespace MWRender } return false; } + void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) + { + if (mObjectPaging) + mObjectPaging->getPagedRefnums(activeGrid, out); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b45e3c8dc8..3082866c43 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -242,6 +242,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: void updateProjectionMatrix(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 4280e1e681..eb48424c51 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -450,13 +450,13 @@ namespace MWWorld } } - void CellPreloader::abortTerrainPreloadExcept(const osg::Vec3f& exceptPos) + void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid& exceptPos) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) { const float resetThreshold = ESM::Land::REAL_SIZE; for (auto pos : mTerrainPreloadPositions) - if ((pos.first-exceptPos).length2() < resetThreshold*resetThreshold) + if ((pos.first-exceptPos.first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos.second) return; mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index c170787357..98e173f175 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -73,7 +73,7 @@ namespace MWWorld void setTerrainPreloadPositions(const std::vector& positions); bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); - void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos); + void abortTerrainPreloadExcept(const PositionCellGrid &exceptPos); private: Resource::ResourceSystem* mResourceSystem; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 36f877bee2..bd9a92daee 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,7 @@ namespace } void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering) + MWRender::RenderingManager& rendering, std::set& pagedRefs) { if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) { @@ -104,7 +105,11 @@ namespace if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - ptr.getClass().insertObjectRendering(ptr, model, rendering); + const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); + if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) + ptr.getClass().insertObjectRendering(ptr, model, rendering); + else + ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode setNodeRotation(ptr, rendering, RotationOrder::direct); ptr.getClass().insertObject (ptr, model, physics); @@ -498,18 +503,14 @@ namespace MWWorld osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); if (newCell != mCurrentGridCenter) + { + preloadTerrain(pos); changeCellGrid(newCell.x(), newCell.y()); + } } void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) { - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(loadingListener); - - int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); - std::string loadingExteriorText = "#{sLoadingMessage3}"; - loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); - CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) { @@ -526,6 +527,14 @@ namespace MWWorld unloadCell (active++); } + mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); + osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); + mRendering.setActiveGrid(newGrid); + + mPagedRefs.clear(); + checkTerrainLoaded(); + mRendering.getPagedRefnums(newGrid, mPagedRefs); + std::size_t refsToLoad = 0; std::vector> cellsPositionsToLoad; // get the number of refs to load @@ -554,6 +563,11 @@ namespace MWWorld } } + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); + std::string loadingExteriorText = "#{sLoadingMessage3}"; + loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); loadingListener->setProgressRange(refsToLoad); const auto getDistanceToPlayerCell = [&] (const std::pair& cellPosition) @@ -601,9 +615,6 @@ namespace MWWorld CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY); MWBase::Environment::get().getWindowManager()->changeCell(current); - mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); - mRendering.setActiveGrid(gridCenterToBounds(mCurrentGridCenter)); - if (changeEvent) mCellChanged = true; } @@ -820,6 +831,7 @@ namespace MWWorld loadingListener->setProgressRange(cell->count()); // Load cell. + mPagedRefs.clear(); loadCell (cell, loadingListener, changeEvent); changePlayerCell(cell, position, adjustPlayerPos); @@ -850,14 +862,12 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); preloadTerrain(position.asVec3()); - + checkTerrainLoaded(); changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); - checkTerrainLoaded(); - if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } @@ -899,7 +909,7 @@ namespace MWWorld { InsertVisitor insertVisitor (cell, *loadingListener, test); cell.forEach (insertVisitor); - insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering); }); + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order @@ -911,7 +921,7 @@ namespace MWWorld { try { - addObject(ptr, *mPhysics, mRendering); + addObject(ptr, *mPhysics, mRendering, mPagedRefs); addObject(ptr, *mPhysics, mNavigator); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); @@ -943,6 +953,8 @@ namespace MWWorld mRendering.removeObject (ptr); if (ptr.getClass().isActor()) mRendering.removeWaterRippleEmitter(ptr); + ptr.getRefData().setBaseNode(nullptr); + mPagedRefs.erase(ptr.getCellRef().getRefNum()); } bool Scene::isCellActive(const CellStore &cell) @@ -1129,9 +1141,9 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { - mPreloader->abortTerrainPreloadExcept(pos); std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); + mPreloader->abortTerrainPreloadExcept(vec[0]); mPreloader->setTerrainPreloadPositions(vec); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 5727f13919..b7aeafbfec 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -88,6 +88,8 @@ namespace MWWorld osg::Vec3f mLastPlayerPos; + std::set mPagedRefs; + void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); osg::Vec2i mCurrentGridCenter; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 1f5a3e4b3c..8f47986df7 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -108,24 +108,20 @@ void QuadTreeNode::initNeighbours() getChild(i)->initNeighbours(); } -void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist) +void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback) { if (!hasValidBounds()) return; - - float dist = distance(viewPoint); - if (dist > maxDist) + LodCallback::ReturnValue lodResult = lodCallback->isSufficientDetail(this, distance(viewPoint)); + if (lodResult == LodCallback::StopTraversal) return; - - bool stopTraversal = (lodCallback->isSufficientDetail(this, dist)) || !getNumChildren(); - - if (stopTraversal) - vd->add(this); - else + else if (lodResult == LodCallback::Deeper && getNumChildren()) { for (unsigned int i=0; itraverseNodes(vd, viewPoint, lodCallback, maxDist); + getChild(i)->traverseNodes(vd, viewPoint, lodCallback); } + else + vd->add(this); } void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 147ca8c8a8..183b5e07ae 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -50,7 +50,13 @@ namespace Terrain public: virtual ~LodCallback() {} - virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; + enum ReturnValue + { + Deeper, + StopTraversal, + StopTraversalAndUse + }; + virtual ReturnValue isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; class ViewData; @@ -97,7 +103,7 @@ namespace Terrain const osg::Vec2f& getCenter() const; /// Traverse nodes according to LOD selection. - void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback, float maxDist); + void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback); /// Adds all leaf nodes which intersect the line from start to end void intersect(ViewData* vd, TerrainLineIntersector& intersector); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index c7544dbe29..78d8835b58 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -53,34 +53,40 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - DefaultLodCallback(float factor, float minSize, const osg::Vec4i& grid) + DefaultLodCallback(float factor, float minSize, float viewDistance, const osg::Vec4i& grid) : mFactor(factor) , mMinSize(minSize) + , mViewDistance(viewDistance) , mActiveGrid(grid) { } - virtual bool isSufficientDetail(QuadTreeNode* node, float dist) + virtual ReturnValue isSufficientDetail(QuadTreeNode* node, float dist) { - int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); - int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); - + const osg::Vec2f& center = node->getCenter(); + bool activeGrid = (center.x() > mActiveGrid.x() && center.y() > mActiveGrid.y() && center.x() < mActiveGrid.z() && center.y() < mActiveGrid.w()); + if (dist > mViewDistance && !activeGrid) // for Scene<->ObjectPaging sync the activegrid must remain loaded + return StopTraversal; if (node->getSize()>1) { float halfSize = node->getSize()/2; - const osg::Vec2f& center = node->getCenter(); osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) < std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) < std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border if (intersects) - return false; + return Deeper; } - return nativeLodLevel <= lodLevel; + + int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); + int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); + + return nativeLodLevel <= lodLevel ? StopTraversalAndUse : Deeper; } private: float mFactor; float mMinSize; + float mViewDistance; osg::Vec4i mActiveGrid; }; @@ -330,11 +336,11 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f pat->setPosition(osg::Vec3f(entry.mNode->getCenter().x()*cellWorldSize, entry.mNode->getCenter().y()*cellWorldSize, 0.f)); const osg::Vec2f& center = entry.mNode->getCenter(); - bool far = (center.x() <= gridbounds.x() || center.y() <= gridbounds.y() || center.x() >= gridbounds.z() || center.y() >= gridbounds.w()); + bool activeGrid = (center.x() > gridbounds.x() && center.y() > gridbounds.y() && center.x() < gridbounds.z() && center.y() < gridbounds.w()); for (QuadTreeWorld::ChunkManager* m : chunkManagers) { - osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile); + osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, activeGrid, vd->getViewPoint(), compile); if (n) pat->addChild(n); } entry.mRenderingNode = pat; @@ -428,9 +434,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) vd->reset(); if (isCullVisitor) { - osgUtil::CullVisitor* cv = static_cast(&nv); - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); - mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); + mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); } else { @@ -515,8 +520,8 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); vd->setActiveGrid(grid); - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); - mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, grid); + mRootNode->traverseNodes(vd, viewPoint, &lodCallback); if (!progressTotal) for (unsigned int i=0; igetNumEntries(); ++i) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0cd2526dec..7b395bff62 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -15,7 +15,6 @@ 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) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b8c151e56b..686d65adc8 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -106,9 +106,12 @@ composite map resolution = 512 # Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks. max composite geometry size = 4.0 -# Load far objects on terrain +# Use object paging for non active cells object paging = true +# Use object paging for active cells grid +object paging active grid = true + # Affects the likelyhood of objects being merged. A higher value means merging is more likely and may improve FPS at the cost of memory. object paging merge factor = 250 From 340d626589c661724d3c03b12117d3bd0a1825bf Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 20/31] static moving support Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 45 +++++++++++-- apps/openmw/mwrender/objectpaging.hpp | 6 +- apps/openmw/mwrender/renderingmanager.cpp | 13 +++- apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 6 ++ apps/openmw/mwworld/scene.cpp | 82 +++++++++++++---------- apps/openmw/mwworld/scene.hpp | 3 + apps/openmw/mwworld/worldimp.cpp | 29 +++++--- 8 files changed, 132 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 9b3165d87a..4c8706caf8 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -389,8 +389,9 @@ namespace MWRender { OpenThreads::ScopedLock lock(mDisabledMutex); - for (auto disabled : mDisabled) - refs.erase(disabled); + if (activeGrid) + for (auto ref : mBlacklist) + refs.erase(ref); } osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); @@ -424,7 +425,9 @@ namespace MWRender continue; } + float d = (viewPoint - pos).length(); + if (!activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); @@ -450,6 +453,15 @@ namespace MWRender */ osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + if (activeGrid) + refnumSet->mRefnums.insert(pair.first); + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (mDisabled.count(pair.first)) + continue; + } + float radius = cnode->getBound().radius() * ref.mScale; if (radius < d*minSize) { @@ -473,9 +485,6 @@ namespace MWRender else analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); - - if (activeGrid) - refnumSet->mRefnums.insert(pair.first); } osg::ref_ptr group = new osg::Group; @@ -614,6 +623,7 @@ namespace MWRender } bool intersects(ChunkId id, osg::Vec3f pos) { + if (mActiveGridOnly && !std::get<2>(id)) return false; pos /= ESM::Land::REAL_SIZE; osg::Vec2f center = std::get<0>(id); float halfSize = std::get<1>(id)/2; @@ -621,6 +631,7 @@ namespace MWRender } osg::Vec3f mPosition; std::set mToClear; + bool mActiveGridOnly = false; }; bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) @@ -642,13 +653,33 @@ namespace MWRender return true; } + bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos) + { + if (!typeFilter(type, false, true)) + return false; + + { + OpenThreads::ScopedLock lock(mDisabledMutex); + if (!mBlacklist.insert(refnum).second) return false; + } + + ClearCacheFunctor ccf; + ccf.mPosition = pos; + ccf.mActiveGridOnly = true; + mCache->call(ccf); + if (ccf.mToClear.empty()) return false; + for (auto chunk : ccf.mToClear) + mCache->removeFromObjectCache(chunk); + return true; + } + + void ObjectPaging::clear() { { OpenThreads::ScopedLock lock(mDisabledMutex); - if (mDisabled.empty()) - return; mDisabled.clear(); + mBlacklist.clear(); } mCache->clear(); } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 79591dcc41..4419cd86a9 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -33,9 +33,12 @@ namespace MWRender virtual unsigned int getNodeMask() override; - /// @return true if something changed + /// @return true if view needs rebuild bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled); + /// @return true if view needs rebuild + bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos); + void clear(); void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; @@ -53,6 +56,7 @@ namespace MWRender OpenThreads::Mutex mDisabledMutex; std::set mDisabled; + std::set mBlacklist; OpenThreads::Mutex mSizeCacheMutex; typedef std::map SizeCache; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5523a69369..590c59302e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1483,15 +1483,24 @@ namespace MWRender } bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { - if (!ptr.isInCell() || !ptr.getCell()->isExterior()) + if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) return false; - if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) + if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), enabled)) { mTerrain->rebuildViews(); return true; } return false; } + void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr) + { + if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) + return; + const ESM::RefNum & refnum = ptr.getCellRef().getRefNum(); + if (!refnum.hasContentFile()) return; + if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3())) + mTerrain->rebuildViews(); + } void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) { if (mObjectPaging) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 3082866c43..c46c2f3434 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -242,6 +242,7 @@ namespace MWRender void setActiveGrid(const osg::Vec4i &grid); bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); + void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 6b737f2029..36f568a5f7 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -141,7 +141,13 @@ namespace if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID) { // overwrite existing reference + float oldscale = iter->mRef.getScale(); iter->load (state); + const ESM::Position & oldpos = iter->mRef.getPosition(); + const ESM::Position & newpos = iter->mData.getPosition(); + const MWWorld::Ptr ptr(&*iter, cellstore); + if ((oldscale != iter->mRef.getScale() || oldpos.asVec3() != newpos.asVec3() || oldpos.rot[0] != newpos.rot[0] || oldpos.rot[1] != newpos.rot[1] || oldpos.rot[2] != newpos.rot[2]) && !ptr.getClass().isActor()) + MWBase::Environment::get().getWorld()->moveObject(ptr, newpos.pos[0], newpos.pos[1], newpos.pos[2]); if (!iter->mData.isEnabled()) { iter->mData.enable(); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bd9a92daee..a469866167 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -87,6 +87,19 @@ namespace ); } + std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs) + { + bool useAnim = ptr.getClass().useAnim(); + std::string model = ptr.getClass().getModel(ptr); + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, vfs); + + const std::string &id = ptr.getCellRef().getRefId(); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + return model; + } + void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering, std::set& pagedRefs) { @@ -97,13 +110,7 @@ namespace } bool useAnim = ptr.getClass().useAnim(); - std::string model = ptr.getClass().getModel(ptr); - if (useAnim) - model = Misc::ResourceHelpers::correctActorModelPath(model, rendering.getResourceSystem()->getVFS()); - - std::string id = ptr.getCellRef().getRefId(); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS()); const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) @@ -188,27 +195,6 @@ namespace } } - void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering, RotationOrder order) - { - setNodeRotation(ptr, rendering, order); - physics.updateRotation(ptr); - } - - void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering) - { - if (ptr.getRefData().getBaseNode() != nullptr) - { - float scale = ptr.getCellRef().getScale(); - osg::Vec3f scaleVec (scale, scale, scale); - ptr.getClass().adjustScale(ptr, scaleVec, true); - rendering.scaleObject(ptr, scaleVec); - - physics.updateScale(ptr); - } - } - struct InsertVisitor { MWWorld::CellStore& mCell; @@ -281,23 +267,49 @@ namespace namespace MWWorld { - void Scene::updateObjectRotation(const Ptr& ptr, RotationOrder order) + void Scene::removeFromPagedRefs(const Ptr &ptr) + { + const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); + if (refnum.hasContentFile() && mPagedRefs.erase(refnum)) + { + if (!ptr.getRefData().getBaseNode()) return; + ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering); + setNodeRotation(ptr, mRendering, RotationOrder::direct); + reloadTerrain(); + } + } + + void Scene::updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics) + { + mRendering.moveObject(ptr, pos); + if (movePhysics) + { + mPhysics->updatePosition(ptr); + } + } + + void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order) { - ::updateObjectRotation(ptr, *mPhysics, mRendering, order); + setNodeRotation(ptr, mRendering, order); + mPhysics->updateRotation(ptr); } void Scene::updateObjectScale(const Ptr &ptr) { - ::updateObjectScale(ptr, *mPhysics, mRendering); + float scale = ptr.getCellRef().getScale(); + osg::Vec3f scaleVec (scale, scale, scale); + ptr.getClass().adjustScale(ptr, scaleVec, true); + mRendering.scaleObject(ptr, scaleVec); + mPhysics->updateScale(ptr); } void Scene::update (float duration, bool paused) { - mPreloadTimer += duration; - if (mPreloadTimer > 0.1f) + mPreloadTimer -= duration; + if (mPreloadTimer <= 0.f) { preloadCells(0.1f); - mPreloadTimer = 0.f; + mPreloadTimer = 0.1f; } mRendering.update (duration, paused); @@ -954,7 +966,6 @@ namespace MWWorld if (ptr.getClass().isActor()) mRendering.removeWaterRippleEmitter(ptr); ptr.getRefData().setBaseNode(nullptr); - mPagedRefs.erase(ptr.getCellRef().getRefNum()); } bool Scene::isCellActive(const CellStore &cell) @@ -1149,6 +1160,7 @@ namespace MWWorld void Scene::reloadTerrain() { + mPreloadTimer = 0; mPreloader->setTerrainPreloadPositions(std::vector()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index b7aeafbfec..2fbbccf68f 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -155,8 +155,11 @@ namespace MWWorld void removeObjectFromScene (const Ptr& ptr); ///< Remove an object from the scene, but not from the world model. + void removeFromPagedRefs(const Ptr &ptr); + void updateObjectRotation(const Ptr& ptr, RotationOrder order); void updateObjectScale(const Ptr& ptr); + void updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics); bool isCellActive(const CellStore &cell); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8392a827cf..2975240e78 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1204,20 +1204,22 @@ namespace MWWorld } if (haveToMove && newPtr.getRefData().getBaseNode()) { - mRendering->moveObject(newPtr, vec); + mWorldScene->updateObjectPosition(newPtr, vec, movePhysics); if (movePhysics) { - mPhysics->updatePosition(newPtr); - mPhysics->updatePtr(ptr, newPtr); - - if (const auto object = mPhysics->getObject(newPtr)) + if (const auto object = mPhysics->getObject(ptr)) updateNavigatorObject(object); } } + if (isPlayer) - { mWorldScene->playerMoved(vec); + else + { + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(newPtr); } + return newPtr; } @@ -1246,9 +1248,15 @@ namespace MWWorld if (mPhysics->getActor(ptr)) mNavigator->removeAgent(getPathfindingHalfExtents(ptr)); - ptr.getCellRef().setScale(scale); + if (scale != ptr.getCellRef().getScale()) + { + ptr.getCellRef().setScale(scale); + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); + } - mWorldScene->updateObjectScale(ptr); + if(ptr.getRefData().getBaseNode() != 0) + mWorldScene->updateObjectScale(ptr); if (mPhysics->getActor(ptr)) mNavigator->addAgent(getPathfindingHalfExtents(ptr)); @@ -1292,6 +1300,9 @@ namespace MWWorld ptr.getRefData().setPosition(pos); + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); + if(ptr.getRefData().getBaseNode() != 0) { const auto order = flags & MWBase::RotationFlag_inverseOrder @@ -3565,6 +3576,8 @@ namespace MWWorld std::string World::exportSceneGraph(const Ptr &ptr) { std::string file = mUserDataPath + "/openmw.osgt"; + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); mRendering->exportSceneGraph(ptr, file, "Ascii"); return file; } From 65cd2c77aa40f1e184b6231d8273651bbe43e7dd Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 21/31] static intersections Signed-off-by: Bret Curtis --- components/terrain/quadtreenode.cpp | 17 ------------- components/terrain/quadtreenode.hpp | 30 ---------------------- components/terrain/quadtreeworld.cpp | 37 +++------------------------- 3 files changed, 4 insertions(+), 80 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 8f47986df7..7baea45c82 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -124,23 +124,6 @@ void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodC vd->add(this); } -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); - } -} - void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 183b5e07ae..a309d91fc8 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -2,39 +2,12 @@ #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; - } - - TerrainLineIntersector(osgUtil::LineSegmentIntersector* intersector) : - osgUtil::LineSegmentIntersector(intersector->getStart(), intersector->getEnd()) - { - 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, @@ -105,9 +78,6 @@ namespace Terrain /// Traverse nodes according to LOD selection. void traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback); - /// Adds all leaf nodes which intersect the line from start to end - void intersect(ViewData* vd, TerrainLineIntersector& intersector); - private: QuadTreeNode* mParent; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 78d8835b58..e094330610 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -418,44 +418,15 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) return; } + osg::Object * viewer = isCullVisitor ? static_cast(&nv)->getCurrentCamera() : nullptr; bool needsUpdate = true; - ViewData* vd = nullptr; - if (isCullVisitor) - vd = mViewDataMap->getViewData(static_cast(&nv)->getCurrentCamera(), nv.getViewPoint(), mActiveGrid, needsUpdate); - else - { - static ViewData sIntersectionViewData; - vd = &sIntersectionViewData; - vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. - } + ViewData *vd = mViewDataMap->getViewData(viewer, nv.getViewPoint(), mActiveGrid, needsUpdate); if (needsUpdate) { vd->reset(); - if (isCullVisitor) - { - DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); - mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); - } - else - { - 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"); - - if (lineIntersector->getCoordinateFrame() == osgUtil::Intersector::CoordinateFrame::MODEL && iv->getModelMatrix() == 0) - { - TerrainLineIntersector terrainIntersector(lineIntersector); - mRootNode->intersect(vd, terrainIntersector); - } - else - { - osg::Matrix matrix(lineIntersector->getTransformation(*iv, lineIntersector->getCoordinateFrame())); - TerrainLineIntersector terrainIntersector(lineIntersector, matrix); - mRootNode->intersect(vd, terrainIntersector); - } - } + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mViewDistance, mActiveGrid); + mRootNode->traverseNodes(vd, nv.getViewPoint(), &lodCallback); } const float cellWorldSize = mStorage->getCellWorldSize(); From 9f0398c0214b5a52aa13ee21117572d99d4a831d Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 22/31] intersection by refnum tag + enable paging for acti,door,cont Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 75 ++++++++++++++++++----- apps/openmw/mwrender/objectpaging.hpp | 10 +++ apps/openmw/mwrender/renderingmanager.cpp | 16 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 40 ++---------- apps/openmw/mwworld/worldimp.cpp | 8 +++ components/sceneutil/optimizer.cpp | 4 ++ 7 files changed, 104 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 4c8706caf8..365e8ec91b 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -33,18 +35,16 @@ namespace MWRender { - bool typeFilter(int type, bool far, bool activeGrid) + bool typeFilter(int type, bool far) { switch (type) { case ESM::REC_STAT: - return true; - - case ESM::REC_ACTI: // TODO enable when intersectionvisitor supported + case ESM::REC_ACTI: case ESM::REC_DOOR: - return !activeGrid; + return true; case ESM::REC_CONT: - return far ? false : !activeGrid; + return !far; default: return false; @@ -321,6 +321,21 @@ namespace MWRender } }; + class AddRefnumMarkerVisitor : public osg::NodeVisitor + { + public: + AddRefnumMarkerVisitor(const ESM::RefNum &refnum) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mRefnum(refnum) {} + ESM::RefNum mRefnum; + virtual void apply(osg::Geometry &node) + { + osg::ref_ptr marker (new RefnumMarker); + marker->mRefnum = mRefnum; + if (osg::Array* array = node.getVertexArray()) + marker->mNumVertices = array->getNumElements(); + node.getOrCreateUserDataContainer()->addUserObject(marker); + } + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) @@ -365,7 +380,7 @@ namespace MWRender { if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2,activeGrid)) continue; + if (!typeFilter(type,size>=2)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; } @@ -381,7 +396,7 @@ namespace MWRender bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); - if (!typeFilter(type,size>=2,activeGrid)) continue; + if (!typeFilter(type,size>=2)) continue; refs[ref.mRefNum] = ref; } } @@ -446,15 +461,32 @@ namespace MWRender std::string model = getModel(type, id, store); if (model.empty()) continue; model = "meshes/" + model; -/* + bool useAnim = type != ESM::REC_STAT; if (useAnim) + { model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); -*/ + if (activeGrid) + { + std::string kfname = Misc::StringUtils::lowerCase(model); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + { + kfname.replace(kfname.size()-4, 4, ".kf"); + if (mSceneManager->getVFS()->exists(kfname)) + continue; + } + } + } + osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); if (activeGrid) - refnumSet->mRefnums.insert(pair.first); + { + if (cnode->getNumChildrenRequiringUpdateTraversal() > 0 || SceneUtil::hasUserDescription(cnode, Constants::NightDayLabel) || SceneUtil::hasUserDescription(cnode, Constants::HerbalismLabel)) + continue; + else + refnumSet->mRefnums.insert(pair.first); + } { OpenThreads::ScopedLock lock(mDisabledMutex); @@ -472,9 +504,6 @@ namespace MWRender continue; } - if (activeGrid && cnode->getNumChildrenRequiringUpdateTraversal() > 0) - continue; - auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { @@ -537,6 +566,20 @@ namespace MWRender osg::ref_ptr node = osg::clone(cnode, co); node->setUserDataContainer(nullptr); + if (activeGrid) + { + if (merge) + { + AddRefnumMarkerVisitor visitor(ref.mRefNum); + node->accept(visitor); + } + else + { + osg::ref_ptr marker = new RefnumMarker; marker->mRefnum = ref.mRefNum; + node->getOrCreateUserDataContainer()->addUserObject(marker); + } + } + trans->addChild(node); if (merge) @@ -636,7 +679,7 @@ namespace MWRender bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) { - if (!typeFilter(type, false, false)) + if (!typeFilter(type, false)) return false; { @@ -655,7 +698,7 @@ namespace MWRender bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos) { - if (!typeFilter(type, false, true)) + if (!typeFilter(type, false)) return false; { diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 4419cd86a9..03d9ccfad0 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -63,6 +63,16 @@ namespace MWRender SizeCache mSizeCache; }; + class RefnumMarker : public osg::Object + { + public: + RefnumMarker() : mNumVertices(0) {} + RefnumMarker(const RefnumMarker ©, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {} + META_Object(MWRender, RefnumMarker) + + ESM::RefNum mRefnum; + unsigned int mNumVertices; + }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 590c59302e..bb7528a20a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1016,6 +1016,7 @@ namespace MWRender { RenderingManager::RayResult result; result.mHit = false; + result.mHitRefnum.mContentFile = -1; result.mRatio = 0; if (intersector->containsIntersections()) { @@ -1027,6 +1028,7 @@ namespace MWRender result.mRatio = intersection.ratio; PtrHolder* ptrHolder = nullptr; + std::vector refnumMarkers; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); @@ -1036,11 +1038,25 @@ namespace MWRender { if (PtrHolder* p = dynamic_cast(userDataContainer->getUserObject(i))) ptrHolder = p; + if (RefnumMarker* r = dynamic_cast(userDataContainer->getUserObject(i))) + refnumMarkers.push_back(r); } } if (ptrHolder) result.mHitObject = ptrHolder->mPtr; + + unsigned int vertexCounter = 0; + for (unsigned int i=0; imNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices)) + { + result.mHitRefnum = refnumMarkers[i]->mRefnum; + break; + } + vertexCounter += refnumMarkers[i]->mNumVertices; + } } return result; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index c46c2f3434..28376d1d67 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -157,6 +157,7 @@ namespace MWRender osg::Vec3f mHitNormalWorld; osg::Vec3f mHitPointWorld; MWWorld::Ptr mHitObject; + ESM::RefNum mHitRefnum; float mRatio; }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 36f568a5f7..2cda83e17d 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -165,28 +165,6 @@ namespace ref.load (state); collection.mList.push_back (ref); } - - struct SearchByRefNumVisitor - { - MWWorld::LiveCellRefBase* mFound; - ESM::RefNum mRefNumToFind; - - SearchByRefNumVisitor(const ESM::RefNum& toFind) - : mFound(nullptr) - , mRefNumToFind(toFind) - { - } - - bool operator()(const MWWorld::Ptr& ptr) - { - if (ptr.getCellRef().getRefNum() == mRefNumToFind) - { - mFound = ptr.getBase(); - return false; - } - return true; - } - }; } namespace MWWorld @@ -263,9 +241,7 @@ namespace MWWorld throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); // Ensure that the object actually exists in the cell - SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); - forEach(searchVisitor); - if (!searchVisitor.mFound) + if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty()) throw std::runtime_error("moveTo: object is not in this cell"); @@ -942,26 +918,22 @@ namespace MWWorld movedTo.load(reader); // Search for the reference. It might no longer exist if its content file was removed. - SearchByRefNumVisitor visitor(refnum); - forEachInternal(visitor); - - if (!visitor.mFound) + Ptr movedRef = searchViaRefNum(refnum); + if (movedRef.isEmpty()) { Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)"; continue; } - MWWorld::LiveCellRefBase* movedRef = visitor.mFound; - CellStore* otherCell = callback->getCellStore(movedTo); if (otherCell == nullptr) { - Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() + Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: - movedRef->mData.setPosition(movedRef->mRef.getPosition()); + movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition()); continue; } @@ -972,7 +944,7 @@ namespace MWWorld continue; } - moveTo(MWWorld::Ptr(movedRef, this), otherCell); + moveTo(movedRef, otherCell); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2975240e78..5f1c8aaa25 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1965,6 +1965,14 @@ namespace MWWorld rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer); facedObject = rayToObject.mHitObject; + if (facedObject.isEmpty() && rayToObject.mHitRefnum.hasContentFile()) + { + for (CellStore* cellstore : mWorldScene->getActiveCells()) + { + facedObject = cellstore->searchViaRefNum(rayToObject.mHitRefnum); + if (!facedObject.isEmpty()) break; + } + } if (rayToObject.mHit) mDistanceToFacedObject = (rayToObject.mRatio * maxDistance) - camDist; else diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 985fd9ee25..f0da9e7e24 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -1824,6 +1824,10 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom lhs.dirtyBound(); lhs.dirtyDisplayList(); + if (osg::UserDataContainer* rhsUserData = rhs.getUserDataContainer()) + for (unsigned int i=0; igetNumUserObjects(); ++i) + lhs.getOrCreateUserDataContainer()->addUserObject(rhsUserData->getUserObject(i)); + return true; } From 89ec6cfea2d9aadd5996cff755ee457aaf6a2437 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Mon, 11 May 2020 13:37:00 +0000 Subject: [PATCH 23/31] tooltips labels Signed-off-by: Bret Curtis --- apps/openmw/mwphysics/physicssystem.cpp | 9 +++++++++ apps/openmw/mwphysics/physicssystem.hpp | 4 ++++ apps/openmw/mwrender/renderingmanager.cpp | 12 +++--------- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 11 +++++++++-- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 7cc0f77706..bbb2ae8d3a 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -397,6 +397,15 @@ namespace MWPhysics return osg::Vec3f(); } + osg::BoundingBox PhysicsSystem::getBoundingBox(const MWWorld::ConstPtr &object) const + { + const Object * physobject = getObject(object); + if (!physobject) return osg::BoundingBox(); + btVector3 min, max; + physobject->getCollisionObject()->getCollisionShape()->getAabb(physobject->getCollisionObject()->getWorldTransform(), min, max); + return osg::BoundingBox(Misc::Convert::toOsg(min), Misc::Convert::toOsg(max)); + } + osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 8b09722afb..32d460b1d8 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "../mwworld/ptr.hpp" @@ -144,6 +145,9 @@ namespace MWPhysics /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const; + /// Get bounding box in world space of the given object. + osg::BoundingBox getBoundingBox(const MWWorld::ConstPtr &object) const; + /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bb7528a20a..30e63e56e0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -977,20 +977,14 @@ namespace MWRender renderCameraToImage(rttCamera.get(),image,w,h); } - osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) + osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb) { - if (!ptr.getRefData().getBaseNode()) - return osg::Vec4f(); - - osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect)); - ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor); - + if (!worldbb.valid()) return osg::Vec4f(); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f; for (int i=0; i<8; ++i) { - osg::Vec3f corner = computeBoundsVisitor.getBoundingBox().corner(i); + osg::Vec3f corner = worldbb.corner(i); corner = corner * viewProj; float x = (corner.x() + 1.f) * 0.5f; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 28376d1d67..b18d2caf19 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -168,7 +168,7 @@ namespace MWRender RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false); /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner. - osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr); + osg::Vec4f getScreenBounds(const osg::BoundingBox &worldbb); void setSkyEnabled(bool enabled); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5f1c8aaa25..12521f0591 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1934,8 +1934,15 @@ namespace MWWorld // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) { - osg::Vec4f screenBounds = mRendering->getScreenBounds(object); - + osg::BoundingBox bb = mPhysics->getBoundingBox(object); + if (!bb.valid() && object.getRefData().getBaseNode()) + { + osg::ComputeBoundsVisitor computeBoundsVisitor; + computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect)); + object.getRefData().getBaseNode()->accept(computeBoundsVisitor); + bb = computeBoundsVisitor.getBoundingBox(); + } + osg::Vec4f screenBounds = mRendering->getScreenBounds(bb); MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords( screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w()); } From 6fa12a6eb854476c98a9546658d4ce6f837e930c Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 24/31] preload tweak Signed-off-by: Bret Curtis --- apps/openmw/mwworld/cellpreloader.cpp | 51 ++++++++++----------------- apps/openmw/mwworld/scene.cpp | 12 ++----- apps/openmw/mwworld/scene.hpp | 1 - 3 files changed, 22 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index eb48424c51..872a29e899 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -65,23 +66,7 @@ namespace MWWorld mTerrainView = mTerrain->createView(); ListModelsVisitor visitor (mMeshes); - if (cell->getState() == MWWorld::CellStore::State_Loaded) - { - cell->forEach(visitor); - } - else - { - const std::vector& objectIds = cell->getPreloadedIds(); - - // could possibly build the model list in the worker thread if we manage to make the Store thread safe - for (const std::string& id : objectIds) - { - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id); - std::string model = ref.getPtr().getClass().getModel(ref.getPtr()); - if (!model.empty()) - mMeshes.push_back(model); - } - } + cell->forEach(visitor); } virtual void abort() @@ -97,7 +82,7 @@ namespace MWWorld try { mTerrain->cacheCell(mTerrainView.get(), mX, mY); - mPreloadedObjects.push_back(mLandManager->getLand(mX, mY)); + mPreloadedObjects.insert(mLandManager->getLand(mX, mY)); } catch(std::exception& e) { @@ -113,17 +98,7 @@ namespace MWWorld { mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); - if (mPreloadInstances) - { - mPreloadedObjects.push_back(mSceneManager->cacheInstance(mesh)); - mPreloadedObjects.push_back(mBulletShapeManager->cacheInstance(mesh)); - } - else - { - mPreloadedObjects.push_back(mSceneManager->getTemplate(mesh)); - mPreloadedObjects.push_back(mBulletShapeManager->getShape(mesh)); - } - + bool animated = false; size_t slashpos = mesh.find_last_of("/\\"); if (slashpos != std::string::npos && slashpos != mesh.size()-1) { @@ -134,11 +109,23 @@ namespace MWWorld if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) { kfname.replace(kfname.size()-4, 4, ".kf"); - mPreloadedObjects.push_back(mKeyframeManager->get(kfname)); + if (mSceneManager->getVFS()->exists(kfname)) + { + mPreloadedObjects.insert(mKeyframeManager->get(kfname)); + animated = true; + } } - } } + if (mPreloadInstances && animated) + mPreloadedObjects.insert(mSceneManager->cacheInstance(mesh)); + else + mPreloadedObjects.insert(mSceneManager->getTemplate(mesh)); + if (mPreloadInstances) + mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh)); + else + mPreloadedObjects.insert(mBulletShapeManager->getShape(mesh)); + } catch (std::exception& e) { @@ -166,7 +153,7 @@ namespace MWWorld osg::ref_ptr mTerrainView; // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state - std::vector > mPreloadedObjects; + std::set > mPreloadedObjects; }; class TerrainPreloadItem : public SceneUtil::WorkItem diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a469866167..d2b31a8552 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -305,12 +305,7 @@ namespace MWWorld void Scene::update (float duration, bool paused) { - mPreloadTimer -= duration; - if (mPreloadTimer <= 0.f) - { - preloadCells(0.1f); - mPreloadTimer = 0.1f; - } + preloadCells(duration); mRendering.update (duration, paused); @@ -760,13 +755,12 @@ namespace MWWorld MWBase::Environment::get().getWorld()->adjustSky(); - mLastPlayerPos = pos.asVec3(); + mLastPlayerPos = player.getRefData().getPosition().asVec3(); } Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, DetourNavigator::Navigator& navigator) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator) - , mPreloadTimer(0.f) , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) , mCellLoadingThreshold(1024.f) , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) @@ -1025,6 +1019,7 @@ namespace MWWorld void Scene::preloadCells(float dt) { + if (dt<=1e-06) return; std::vector exteriorPositions; const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -1160,7 +1155,6 @@ namespace MWWorld void Scene::reloadTerrain() { - mPreloadTimer = 0; mPreloader->setTerrainPreloadPositions(std::vector()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 2fbbccf68f..227bb7b313 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -75,7 +75,6 @@ namespace MWWorld MWRender::RenderingManager& mRendering; DetourNavigator::Navigator& mNavigator; std::unique_ptr mPreloader; - float mPreloadTimer; int mHalfGridSize; float mCellLoadingThreshold; float mPreloadDistance; From b27b76e32582601412da380eef273619a62cfe4b Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 25/31] avoid pagerebuild when reloading a same save Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 41 ++++++++++----- apps/openmw/mwrender/objectpaging.hpp | 20 +++++-- apps/openmw/mwrender/renderingmanager.cpp | 9 ++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/cellpreloader.cpp | 32 +++++++----- apps/openmw/mwworld/cellpreloader.hpp | 4 +- apps/openmw/mwworld/scene.cpp | 64 +++++++++++------------ apps/openmw/mwworld/scene.hpp | 6 +-- 8 files changed, 110 insertions(+), 67 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 365e8ec91b..c3a90da801 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -339,6 +339,7 @@ namespace MWRender ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) + , mRefTrackerLocked(false) { mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain"); mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); @@ -403,9 +404,9 @@ namespace MWRender } { - OpenThreads::ScopedLock lock(mDisabledMutex); + OpenThreads::ScopedLock lock(mRefTrackerMutex); if (activeGrid) - for (auto ref : mBlacklist) + for (auto ref : getRefTracker().mBlacklist) refs.erase(ref); } @@ -489,8 +490,8 @@ namespace MWRender } { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (mDisabled.count(pair.first)) + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (getRefTracker().mDisabled.count(pair.first)) continue; } @@ -683,14 +684,16 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (enabled && !mDisabled.erase(refnum)) return false; - if (!enabled && !mDisabled.insert(refnum).second) return false; + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false; + if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false; + if (mRefTrackerLocked) return false; } ClearCacheFunctor ccf; ccf.mPosition = pos; mCache->call(ccf); + if (ccf.mToClear.empty()) return false; for (auto chunk : ccf.mToClear) mCache->removeFromObjectCache(chunk); return true; @@ -702,8 +705,9 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - if (!mBlacklist.insert(refnum).second) return false; + OpenThreads::ScopedLock lock(mRefTrackerMutex); + if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false; + if (mRefTrackerLocked) return false; } ClearCacheFunctor ccf; @@ -719,12 +723,25 @@ namespace MWRender void ObjectPaging::clear() { + OpenThreads::ScopedLock lock(mRefTrackerMutex); + mRefTrackerNew.mDisabled.clear(); + mRefTrackerNew.mBlacklist.clear(); + mRefTrackerLocked = true; + } + + bool ObjectPaging::unlockCache() + { + if (!mRefTrackerLocked) return false; { - OpenThreads::ScopedLock lock(mDisabledMutex); - mDisabled.clear(); - mBlacklist.clear(); + OpenThreads::ScopedLock lock(mRefTrackerMutex); + mRefTrackerLocked = false; + if (mRefTracker == mRefTrackerNew) + return false; + else + mRefTracker = mRefTrackerNew; } mCache->clear(); + return true; } struct GetRefnumsFunctor diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 03d9ccfad0..c79dd6e066 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -41,6 +41,10 @@ namespace MWRender void clear(); + /// Must be called after clear() before rendering starts. + /// @return true if view needs rebuild + bool unlockCache(); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); @@ -54,9 +58,19 @@ namespace MWRender float mMinSizeMergeFactor; float mMinSizeCostMultiplier; - OpenThreads::Mutex mDisabledMutex; - std::set mDisabled; - std::set mBlacklist; + OpenThreads::Mutex mRefTrackerMutex; + struct RefTracker + { + std::set mDisabled; + std::set mBlacklist; + bool operator==(const RefTracker&other) { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; } + }; + RefTracker mRefTracker; + RefTracker mRefTrackerNew; + bool mRefTrackerLocked; + + const RefTracker& getRefTracker() const { return mRefTracker; } + RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; } OpenThreads::Mutex mSizeCacheMutex; typedef std::map SizeCache; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 30e63e56e0..99f3e25acc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1511,6 +1511,15 @@ namespace MWRender if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3())) mTerrain->rebuildViews(); } + bool RenderingManager::pagingUnlockCache() + { + if (mObjectPaging && mObjectPaging->unlockCache()) + { + mTerrain->rebuildViews(); + return true; + } + return false; + } void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out) { if (mObjectPaging) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b18d2caf19..db4788970e 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -244,6 +244,7 @@ namespace MWRender bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); + bool pagingUnlockCache(); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); private: diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 872a29e899..bee6a957dc 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -419,36 +419,44 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - bool CellPreloader::getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp) + bool CellPreloader::syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp) { if (!mTerrainPreloadItem) - return false; + return true; else if (mTerrainPreloadItem->isDone()) { - mTerrainPreloadItem->storeViews(timestamp); - mTerrainPreloadItem = nullptr; - return false; + if (mTerrainPreloadItem->storeViews(timestamp)) + { + mTerrainPreloadItem = nullptr; + return true; + } + else + { + setTerrainPreloadPositions(std::vector()); + setTerrainPreloadPositions(positions); + return false; + } } else { progress = mTerrainPreloadItem->getProgress(); progressRange = mTerrainPreloadItem->getProgressRange(); - return !progress || progress < progressRange; + return false; } } - void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid& exceptPos) + void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos) { + const float resetThreshold = ESM::Land::REAL_SIZE; + for (auto pos : mTerrainPreloadPositions) + if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second) + return; if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) { - const float resetThreshold = ESM::Land::REAL_SIZE; - for (auto pos : mTerrainPreloadPositions) - if ((pos.first-exceptPos.first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos.second) - return; mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); - mTerrainPreloadItem = nullptr; } + setTerrainPreloadPositions(std::vector()); } bool contains(const std::vector& container, const std::vector& contained) diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 98e173f175..e719f2e606 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -72,8 +72,8 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); - bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); - void abortTerrainPreloadExcept(const PositionCellGrid &exceptPos); + bool syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp); + void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos); private: Resource::ResourceSystem* mResourceSystem; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index d2b31a8552..53ddaf08ca 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -510,13 +510,10 @@ namespace MWWorld osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); if (newCell != mCurrentGridCenter) - { - preloadTerrain(pos); - changeCellGrid(newCell.x(), newCell.y()); - } + changeCellGrid(pos, newCell.x(), newCell.y()); } - void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) + void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent) { CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) @@ -538,8 +535,8 @@ namespace MWWorld osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); mRendering.setActiveGrid(newGrid); + preloadTerrain(pos, true); mPagedRefs.clear(); - checkTerrainLoaded(); mRendering.getPagedRefnums(newGrid, mPagedRefs); std::size_t refsToLoad = 0; @@ -867,9 +864,7 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); - preloadTerrain(position.asVec3()); - checkTerrainLoaded(); - changeCellGrid(x, y, changeEvent); + changeCellGrid(position.asVec3(), x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); @@ -878,29 +873,6 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } - void Scene::checkTerrainLoaded() - { - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(loadingListener); - int progress = 0, initialProgress = -1, progressRange = 0; - while (mPreloader->getTerrainPreloadInProgress(progress, progressRange, mRendering.getReferenceTime())) - { - if (initialProgress == -1) - { - loadingListener->setLabel("#{sLoadingMessage4}"); - initialProgress = progress; - } - if (progress) - { - loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); - loadingListener->setProgress(progress-initialProgress); - } - else - loadingListener->setProgress(0); - OpenThreads::Thread::microSleep(5000); - } - } - CellStore* Scene::getCurrentCell () { return mCurrentCell; @@ -1145,12 +1117,36 @@ namespace MWWorld mPreloader->preload(cell, mRendering.getReferenceTime()); } - void Scene::preloadTerrain(const osg::Vec3f &pos) + void Scene::preloadTerrain(const osg::Vec3f &pos, bool sync) { std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); - mPreloader->abortTerrainPreloadExcept(vec[0]); + if (sync && mRendering.pagingUnlockCache()) + mPreloader->abortTerrainPreloadExcept(nullptr); + else + mPreloader->abortTerrainPreloadExcept(&vec[0]); mPreloader->setTerrainPreloadPositions(vec); + if (!sync) return; + + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int progress = 0, initialProgress = -1, progressRange = 0; + while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime())) + { + if (initialProgress == -1) + { + loadingListener->setLabel("#{sLoadingMessage4}"); + initialProgress = progress; + } + if (progress) + { + loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); + loadingListener->setProgress(progress-initialProgress); + } + else + loadingListener->setProgress(0); + OpenThreads::Thread::microSleep(5000); + } } void Scene::reloadTerrain() diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 227bb7b313..b9a39779de 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -93,7 +93,7 @@ namespace MWWorld osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center - void changeCellGrid (int playerCellX, int playerCellY, bool changeEvent = true); + void changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent = true); typedef std::pair PositionCellGrid; @@ -102,8 +102,6 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); - void checkTerrainLoaded(); - osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; @@ -115,7 +113,7 @@ namespace MWWorld ~Scene(); void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); - void preloadTerrain(const osg::Vec3f& pos); + void preloadTerrain(const osg::Vec3f& pos, bool sync=false); void reloadTerrain(); void unloadCell (CellStoreCollection::iterator iter, bool test = false); From 4238fbccdf450c89454bfdb76fc7b1a7c1b19945 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 26/31] view fix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwworld/scene.cpp | 3 +-- components/terrain/viewdata.cpp | 20 ++++++++++++-------- components/terrain/viewdata.hpp | 1 + 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 99f3e25acc..d214828942 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1063,6 +1063,7 @@ namespace MWRender mIntersectionVisitor = new osgUtil::IntersectionVisitor; mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); + mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp()); mIntersectionVisitor->setIntersector(intersector); int mask = ~0; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 53ddaf08ca..b311107f47 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -305,11 +305,10 @@ namespace MWWorld void Scene::update (float duration, bool paused) { + mPreloader->updateCache(mRendering.getReferenceTime()); preloadCells(duration); mRendering.update (duration, paused); - - mPreloader->updateCache(mRendering.getReferenceTime()); } void Scene::unloadCell (CellStoreCollection::iterator iter, bool test) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 8a87690005..c24252b7d5 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -145,15 +145,15 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo { float shortestDist = std::numeric_limits::max(); const ViewData* mostSuitableView = nullptr; - for (const ViewData& other : mViewVector) + for (const ViewData* other : mUsedViews) { - if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision) + if (other->suitableToUse(activeGrid) && other->getWorldUpdateRevision() >= mWorldUpdateRevision) { - float dist = (viewPoint-other.getViewPoint()).length2(); + float dist = (viewPoint-other->getViewPoint()).length2(); if (dist < shortestDist) { shortestDist = dist; - mostSuitableView = &other; + mostSuitableView = other; } } } @@ -195,6 +195,7 @@ ViewData *ViewDataMap::createOrReuseView() mViewVector.push_back(ViewData()); vd = &mViewVector.back(); } + mUsedViews.push_back(vd); vd->setWorldUpdateRevision(mWorldUpdateRevision); return vd; } @@ -215,13 +216,16 @@ void ViewDataMap::clearUnusedViews(double referenceTime) else ++it; } - for (ViewData& vd : mViewVector) + for (std::deque::iterator it = mUsedViews.begin(); it != mUsedViews.end(); ) { - if (vd.getLastUsageTimeStamp() + mExpiryDelay < referenceTime) + if ((*it)->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) { - vd.clear(); - mUnusedViews.push_back(&vd); + (*it)->clear(); + mUnusedViews.push_back(*it); + it = mUsedViews.erase(it); } + else + ++it; } } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index ba4fba3b2a..400d9fbbea 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -106,6 +106,7 @@ namespace Terrain unsigned int mWorldUpdateRevision; + std::deque mUsedViews; std::deque mUnusedViews; }; From f12879a04c4db10e8ee8f80f217c9b9e8d11c121 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 27/31] allow statesetupdater as cullcallback = faster + works in paging Signed-off-by: Bret Curtis --- apps/openmw/mwrender/animation.cpp | 22 +++++----------------- apps/openmw/mwrender/animation.hpp | 3 --- apps/openmw/mwrender/npcanimation.cpp | 8 +++----- apps/openmw/mwrender/npcanimation.hpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 11 +++++++++++ components/nifosg/controller.cpp | 16 ++++++++-------- components/nifosg/controller.hpp | 8 +++++--- components/nifosg/nifloader.cpp | 18 +++++++++++++----- components/sceneutil/statesetupdater.cpp | 22 +++++++++++++++------- components/sceneutil/statesetupdater.hpp | 2 +- 10 files changed, 62 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8f8e8c2336..48c58f2475 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -513,6 +513,9 @@ namespace MWRender if (mShadowUniform) stateset->addUniform(mShadowUniform); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + // FIXME: overriding diffuse/ambient/emissive colors osg::Material* material = new osg::Material; material->setColorMode(osg::Material::OFF); @@ -1741,31 +1744,16 @@ namespace MWRender if (mTransparencyUpdater == nullptr) { mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); - mObjectRoot->addUpdateCallback(mTransparencyUpdater); + mObjectRoot->addCullCallback(mTransparencyUpdater); } else mTransparencyUpdater->setAlpha(alpha); } else { - mObjectRoot->removeUpdateCallback(mTransparencyUpdater); + mObjectRoot->removeCullCallback(mTransparencyUpdater); mTransparencyUpdater = nullptr; - mObjectRoot->setStateSet(nullptr); - } - - setRenderBin(); - } - - void Animation::setRenderBin() - { - if (mAlpha != 1.f) - { - osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); - stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } - else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) - stateset->setRenderBinToInherit(); } void Animation::setLightEffect(float effect) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2890e7be41..c53cf98a95 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -336,9 +336,6 @@ protected: */ virtual void addControllers(); - /// Set the render bin for this animation's object root. May be customized by subclasses. - virtual void setRenderBin(); - public: Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ec825ca2f7..6e7669976e 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -435,12 +435,10 @@ void NpcAnimation::setRenderBin() osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); prototypeAdded = true; } - - osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); - stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } - else - Animation::setRenderBin(); + else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) + stateset->setRenderBinToInherit(); } void NpcAnimation::rebuild() diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index e102f5097e..7f8d5434bf 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -88,7 +88,7 @@ private: void addPartGroup(int group, int priority, const std::vector &parts, bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); - virtual void setRenderBin(); + void setRenderBin(); osg::ref_ptr mFirstPersonNeckController; diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index c3a90da801..b138a64126 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -162,6 +162,17 @@ namespace MWRender { if (callback->className() == std::string("BillboardCallback")) handleBillboard(cloned); + else + { + if (node->getCullCallback()->getNestedCallback()) + { + osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); + clonedCallback->setNestedCallback(nullptr); + cloned->addCullCallback(clonedCallback); + } + else + cloned->addCullCallback(const_cast(callback)); + } callback = callback->getNestedCallback(); } } diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index a088ead4c1..b5fd374e36 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -352,8 +352,9 @@ void RollController::operator() (osg::Node* node, osg::NodeVisitor* nv) } } -AlphaController::AlphaController(const Nif::NiFloatData *data) +AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial) : mData(data->mKeyList, 1.f) + , mBaseMaterial(baseMaterial) { } @@ -365,14 +366,13 @@ AlphaController::AlphaController() AlphaController::AlphaController(const AlphaController ©, const osg::CopyOp ©op) : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) + , mBaseMaterial(copy.mBaseMaterial) { } void AlphaController::setDefaults(osg::StateSet *stateset) { - // need to create a deep copy of StateAttributes we will modify - osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) @@ -387,9 +387,10 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) } } -MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color) +MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial) : mData(data->mKeyList, osg::Vec3f(1,1,1)) , mTargetColor(color) + , mBaseMaterial(baseMaterial) { } @@ -401,14 +402,13 @@ MaterialColorController::MaterialColorController(const MaterialColorController & : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) , mTargetColor(copy.mTargetColor) + , mBaseMaterial(copy.mBaseMaterial) { } void MaterialColorController::setDefaults(osg::StateSet *stateset) { - // need to create a deep copy of StateAttributes we will modify - osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 3f66013a22..c81f97a714 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -24,6 +24,7 @@ namespace osg { class Node; class StateSet; + class Material; } namespace osgParticle @@ -268,9 +269,9 @@ namespace NifOsg { private: FloatInterpolator mData; - + osg::ref_ptr mBaseMaterial; public: - AlphaController(const Nif::NiFloatData *data); + AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial); AlphaController(); AlphaController(const AlphaController& copy, const osg::CopyOp& copyop); @@ -291,7 +292,7 @@ namespace NifOsg Specular = 2, Emissive = 3 }; - MaterialColorController(const Nif::NiPosData *data, TargetColor color); + MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial); MaterialColorController(); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); @@ -304,6 +305,7 @@ namespace NifOsg private: Vec3Interpolator mData; TargetColor mTargetColor = Ambient; + osg::ref_ptr mBaseMaterial; }; class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index bff4147076..88e89b4005 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -639,7 +639,15 @@ namespace NifOsg handleParticleSystem(nifNode, node, composite, animflags, rootNode); if (composite->getNumControllers() > 0) - node->addUpdateCallback(composite); + { + osg::Callback *cb = composite; + if (composite->getNumControllers() == 1) + cb = composite->getController(0); + if (animflags & Nif::NiNode::AnimFlag_AutoPlay) + node->addCullCallback(cb); + else + node->addUpdateCallback(cb); // have to remain as UpdateCallback so AssignControllerSourcesVisitor can find it. + } bool isAnimated = false; handleNodeControllers(nifNode, node, animflags, isAnimated); @@ -778,7 +786,7 @@ namespace NifOsg } } - void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags) + void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial) { for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -789,7 +797,7 @@ namespace NifOsg const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); if (alphactrl->data.empty()) continue; - osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr())); + osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr(), baseMaterial)); setupController(alphactrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -799,7 +807,7 @@ namespace NifOsg if (matctrl->data.empty()) continue; auto targetColor = static_cast(matctrl->targetColor); - osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor)); + osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial)); setupController(matctrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -1767,7 +1775,7 @@ namespace NifOsg if (!matprop->controller.empty()) { hasMatCtrl = true; - handleMaterialControllers(matprop, composite, animflags); + handleMaterialControllers(matprop, composite, animflags, mat); } break; diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 121cdaca67..08418f3312 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -2,30 +2,38 @@ #include #include +#include namespace SceneUtil { void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv) { + bool isCullVisitor = nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR; if (!mStateSets[0]) { - // first time setup - osg::StateSet* src = node->getOrCreateStateSet(); - for (int i=0; i<2; ++i) // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first - // This can be done conveniently in user implementations of the setDefaults() method + for (int i=0; i<2; ++i) { - mStateSets[i] = new osg::StateSet(*src, osg::CopyOp::SHALLOW_COPY); + if (!isCullVisitor) + mStateSets[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults + else + mStateSets[i] = new osg::StateSet; setDefaults(mStateSets[i]); } } osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2]; - node->setStateSet(stateset); - apply(stateset, nv); + if (!isCullVisitor) + node->setStateSet(stateset); + else + static_cast(nv)->pushStateSet(stateset); + traverse(node, nv); + + if (isCullVisitor) + static_cast(nv)->popStateSet(); } void StateSetUpdater::reset() diff --git a/components/sceneutil/statesetupdater.hpp b/components/sceneutil/statesetupdater.hpp index 51398844cf..d12316fb23 100644 --- a/components/sceneutil/statesetupdater.hpp +++ b/components/sceneutil/statesetupdater.hpp @@ -13,7 +13,7 @@ namespace SceneUtil /// traversals run in parallel can yield up to 200% framerates. /// @par Race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns, /// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame. - /// @par Must be set as UpdateCallback on a Node. + /// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet, otherwise it operates on a clone of the node's existing StateSet. /// @note Do not add the same StateSetUpdater to multiple nodes. /// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater. class StateSetUpdater : public osg::NodeCallback From 66c9469a804d772d56f60e0329b46d48d69432e4 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Tue, 12 May 2020 13:37:00 +0000 Subject: [PATCH 28/31] fix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index b138a64126..8cff46fa89 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -444,15 +444,12 @@ namespace MWRender if (size < 1.f) { osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; - cellPos.x() = std::max(cellPos.x(), std::floor(minBound.x())); - cellPos.x() = std::min(cellPos.x(), std::ceil(maxBound.x())); - cellPos.y() = std::max(cellPos.y(), std::floor(minBound.y())); - cellPos.y() = std::min(cellPos.y(), std::ceil(maxBound.y())); - if (cellPos.x() < minBound.x() || cellPos.x() > maxBound.x() || cellPos.y() < minBound.y() || cellPos.y() > maxBound.y()) + if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())) + continue; + if ((maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) continue; } - float d = (viewPoint - pos).length(); if (!activeGrid) { @@ -619,7 +616,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()) + if ((relativeViewPoint - mergeGroup->getBound().center()).length() > mergeGroup->getBound().radius()*2) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); @@ -630,17 +627,16 @@ namespace MWRender group->addChild(mergeGroup); - if (compile) - { - stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; - mergeGroup->accept(stateToCompile); - } - if (mDebugBatches) { DebugVisitor dv; mergeGroup->accept(dv); } + if (compile) + { + stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; + mergeGroup->accept(stateToCompile); + } } auto ico = mSceneManager->getIncrementalCompileOperation(); From 4e2efb3cdb6d4ab3cbf4b120bfc6d3a70062de1a Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Fri, 15 May 2020 13:37:00 +0000 Subject: [PATCH 29/31] avoid sqrt Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 8cff46fa89..dbe99b0e17 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -450,14 +450,14 @@ namespace MWRender continue; } - float d = (viewPoint - pos).length(); + float dSqr = (viewPoint - pos).length2(); if (!activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); if (found != mSizeCache.end()) { - if (found->second < d*minSize) + if (found->second < dSqr*minSize*minSize) continue; } } @@ -503,12 +503,12 @@ namespace MWRender continue; } - float radius = cnode->getBound().radius() * ref.mScale; - if (radius < d*minSize) + float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; + if (radius2 < dSqr*minSize*minSize) { OpenThreads::ScopedLock lock(mSizeCacheMutex); { - mSizeCache[pair.first] = radius; + mSizeCache[pair.first] = radius2; } continue; } @@ -551,13 +551,8 @@ namespace MWRender const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - if (minSizeMerged != minSize) - { - float d = (viewPoint - pos).length(); - float radius = cnode->getBound().radius() * cref->mScale; - if (radius < d*minSizeMerged) - continue; - } + if (minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) + continue; osg::Matrixf matrix; matrix.preMultTranslate(pos - worldCenter); @@ -616,7 +611,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length() > mergeGroup->getBound().radius()*2) + if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()*2*2) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); From daa2761c2d01120d2fad597b25668a2563cb712f Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sat, 16 May 2020 13:37:00 +0000 Subject: [PATCH 30/31] alphablending & billboardfix Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 68 ++++++++++------------ components/sceneutil/mwshadowtechnique.cpp | 5 +- components/sceneutil/optimizer.cpp | 7 +++ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index dbe99b0e17..d65fffde88 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -95,21 +95,15 @@ namespace MWRender } virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const { - return true; + return (node->getDataVariance() != osg::Object::DYNAMIC); } }; class CopyOp : public osg::CopyOp { public: - CopyOp(bool deep) : mSqrDistance(0.f) { - unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; - if (deep) - flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; - setCopyFlags(flags); - } - - float mSqrDistance; + bool mOptimizeBillboards = true; + float mSqrDistance = 0.f; osg::Vec3f mViewVector; mutable std::vector mNodePath; @@ -157,23 +151,27 @@ namespace MWRender } void handleCallbacks(const osg::Node* node, osg::Node *cloned) const { - const osg::Callback* callback = node->getCullCallback(); - while (callback) + for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; callback = callback->getNestedCallback()) { if (callback->className() == std::string("BillboardCallback")) - handleBillboard(cloned); - else { - if (node->getCullCallback()->getNestedCallback()) + if (mOptimizeBillboards) { - osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); - clonedCallback->setNestedCallback(nullptr); - cloned->addCullCallback(clonedCallback); + handleBillboard(cloned); + continue; } else - cloned->addCullCallback(const_cast(callback)); + cloned->setDataVariance(osg::Object::DYNAMIC); } - callback = callback->getNestedCallback(); + + if (node->getCullCallback()->getNestedCallback()) + { + osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); + clonedCallback->setNestedCallback(nullptr); + cloned->addCullCallback(clonedCallback); + } + else + cloned->addCullCallback(const_cast(callback)); } } void handleBillboard(osg::Node* node) const @@ -414,11 +412,11 @@ namespace MWRender } } + if (activeGrid) { OpenThreads::ScopedLock lock(mRefTrackerMutex); - if (activeGrid) - for (auto ref : getRefTracker().mBlacklist) - refs.erase(ref); + for (auto ref : getRefTracker().mBlacklist) + refs.erase(ref); } osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); @@ -444,9 +442,8 @@ namespace MWRender if (size < 1.f) { osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; - if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())) - continue; - if ((maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) + if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y()) + || (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) continue; } @@ -455,11 +452,8 @@ namespace MWRender { OpenThreads::ScopedLock lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); - if (found != mSizeCache.end()) - { - if (found->second < dSqr*minSize*minSize) - continue; - } + if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize) + continue; } std::string id = Misc::StringUtils::lowerCase(ref.mRefID); @@ -504,12 +498,10 @@ namespace MWRender } float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; - if (radius2 < dSqr*minSize*minSize) + if (radius2 < dSqr*minSize*minSize && !activeGrid) { OpenThreads::ScopedLock lock(mSizeCacheMutex); - { - mSizeCache[pair.first] = radius2; - } + mSizeCache[pair.first] = radius2; continue; } @@ -551,7 +543,7 @@ namespace MWRender const ESM::CellRef& ref = *cref; osg::Vec3f pos = ref.mPos.asVec3(); - if (minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) + if (!activeGrid && minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) continue; osg::Matrixf matrix; @@ -563,7 +555,9 @@ namespace MWRender osg::ref_ptr trans = new osg::MatrixTransform(matrix); trans->setDataVariance(osg::Object::STATIC); - CopyOp co = CopyOp(merge); + CopyOp co; + co.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); + co.mOptimizeBillboards = (size > 1/4.f); co.mNodePath.push_back(trans); co.mSqrDistance = (viewPoint - pos).length2(); co.mViewVector = (viewPoint - worldCenter); @@ -611,7 +605,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()*2*2) + if (size > 1/8.f) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index cb3a1b278b..98771bbb42 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -1580,7 +1581,9 @@ void MWShadowTechnique::createShaders() _shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON); _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); _shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform); - + osg::ref_ptr depth = new osg::Depth; + depth->setWriteMask(true); + _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); _shadowCastingStateSet->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "RenderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index f0da9e7e24..71cb8a2e52 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1543,6 +1544,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) geom->setUseVertexBufferObjects(true); geom->setUseDisplayList(false); } + if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet()) + { + osg::Depth* d = new osg::Depth; + d->setWriteMask(0); + geom->getOrCreateStateSet()->setAttribute(d); + } } } From 26ab1763893e19b5f4d0b9eded3b86956829fa2f Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Sat, 16 May 2020 13:37:00 +0000 Subject: [PATCH 31/31] profiling Signed-off-by: Bret Curtis --- apps/openmw/mwrender/animation.cpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 80 ++++++++++++++------------ apps/openmw/mwworld/store.cpp | 18 ++---- components/esm/cellref.cpp | 16 ------ components/esm/cellref.hpp | 16 +++++- components/nifosg/controller.cpp | 4 +- components/nifosg/particle.cpp | 2 +- components/resource/scenemanager.cpp | 2 +- components/sceneutil/clone.cpp | 19 ++---- components/sceneutil/clone.hpp | 2 - components/sceneutil/morphgeometry.cpp | 2 +- components/sceneutil/optimizer.cpp | 4 +- components/sceneutil/riggeometry.cpp | 6 +- 13 files changed, 78 insertions(+), 95 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 48c58f2475..594713a1cf 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1372,7 +1372,7 @@ namespace MWRender osg::Group* sheathParent = findVisitor.mFoundNode; if (sheathParent) { - osg::Node* copy = osg::clone(nodePair.first, osg::CopyOp::DEEP_COPY_NODES); + osg::Node* copy = static_cast(nodePair.first->clone(osg::CopyOp::DEEP_COPY_NODES)); sheathParent->addChild(copy); } } diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index d65fffde88..71268a52c8 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -107,8 +107,23 @@ namespace MWRender osg::Vec3f mViewVector; mutable std::vector mNodePath; + void copy(const osg::Node* toCopy, osg::Group* attachTo) + { + const osg::Group* groupToCopy = toCopy->asGroup(); + if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy) + attachTo->addChild(operator()(toCopy)); + else + { + for (unsigned int i=0; igetNumChildren(); ++i) + attachTo->addChild(operator()(groupToCopy->getChild(i))); + } + } + virtual osg::Node* operator() (const osg::Node* node) const { + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + if (dynamic_cast(node)) return nullptr; if (dynamic_cast(node)) @@ -133,12 +148,9 @@ namespace MWRender return n; } - if (const osg::Drawable* d = node->asDrawable()) - return operator()(d); - mNodePath.push_back(node); - osg::Node* cloned = osg::clone(node, *this); + osg::Node* cloned = static_cast(node->clone(*this)); cloned->setDataVariance(osg::Object::STATIC); cloned->setUserDataContainer(nullptr); cloned->setName(""); @@ -222,14 +234,14 @@ namespace MWRender if (getCopyFlags() & DEEP_COPY_DRAWABLES) { - osg::Drawable* d = osg::clone(drawable, *this); + osg::Drawable* d = static_cast(drawable->clone(*this)); d->setDataVariance(osg::Object::STATIC); d->setUserDataContainer(nullptr); d->setName(""); return d; } else - return osg::CopyOp::operator()(drawable); + return const_cast(drawable); } virtual osg::Callback* operator() (const osg::Callback* callback) const { @@ -388,8 +400,9 @@ namespace MWRender bool deleted = false; while(cell->getNextRef(esm[index], ref, deleted)) { + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; - int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } refs[ref.mRefNum] = ref; @@ -403,9 +416,10 @@ namespace MWRender for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) { ESM::CellRef ref = it->first; + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } - int type = store.findStatic(Misc::StringUtils::lowerCase(ref.mRefID)); + int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; refs[ref.mRefNum] = ref; } @@ -456,28 +470,23 @@ namespace MWRender continue; } - std::string id = Misc::StringUtils::lowerCase(ref.mRefID); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + if (ref.mRefID == "prisonmarker" || ref.mRefID == "divinemarker" || ref.mRefID == "templemarker" || ref.mRefID == "northmarker") continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - int type = store.findStatic(id); - std::string model = getModel(type, id, store); + int type = store.findStatic(ref.mRefID); + std::string model = getModel(type, ref.mRefID, store); if (model.empty()) continue; model = "meshes/" + model; - bool useAnim = type != ESM::REC_STAT; - if (useAnim) + if (activeGrid && type != ESM::REC_STAT) { model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); - if (activeGrid) + std::string kfname = Misc::StringUtils::lowerCase(model); + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) { - std::string kfname = Misc::StringUtils::lowerCase(model); - if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) - { - kfname.replace(kfname.size()-4, 4, ".kf"); - if (mSceneManager->getVFS()->exists(kfname)) - continue; - } + kfname.replace(kfname.size()-4, 4, ".kf"); + if (mSceneManager->getVFS()->exists(kfname)) + continue; } } @@ -521,6 +530,7 @@ namespace MWRender osg::ref_ptr mergeGroup = new osg::Group; osg::ref_ptr templateRefs = new TemplateRef; osgUtil::StateToCompile stateToCompile(0, nullptr); + CopyOp copyop; for (const auto& pair : nodes) { const osg::Node* cnode = pair.first; @@ -555,35 +565,29 @@ namespace MWRender osg::ref_ptr trans = new osg::MatrixTransform(matrix); trans->setDataVariance(osg::Object::STATIC); - CopyOp co; - co.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); - co.mOptimizeBillboards = (size > 1/4.f); - co.mNodePath.push_back(trans); - co.mSqrDistance = (viewPoint - pos).length2(); - co.mViewVector = (viewPoint - worldCenter); - osg::ref_ptr node = osg::clone(cnode, co); - node->setUserDataContainer(nullptr); + copyop.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); + copyop.mOptimizeBillboards = (size > 1/4.f); + copyop.mNodePath.push_back(trans); + copyop.mSqrDistance = (viewPoint - pos).length2(); + copyop.mViewVector = (viewPoint - worldCenter); + copyop.copy(cnode, trans); if (activeGrid) { if (merge) { AddRefnumMarkerVisitor visitor(ref.mRefNum); - node->accept(visitor); + trans->accept(visitor); } else { osg::ref_ptr marker = new RefnumMarker; marker->mRefnum = ref.mRefNum; - node->getOrCreateUserDataContainer()->addUserObject(marker); + trans->getOrCreateUserDataContainer()->addUserObject(marker); } } - trans->addChild(node); - - if (merge) - mergeGroup->addChild(trans); - else - group->addChild(trans); + osg::Group* attachTo = merge ? mergeGroup : group; + attachTo->addChild(trans); ++numinstances; } if (numinstances > 0) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 93f11d4fd9..4c44175879 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -139,15 +139,12 @@ namespace MWWorld std::string idLower = Misc::StringUtils::lowerCase(id); typename Dynamic::const_iterator dit = mDynamic.find(idLower); - if (dit != mDynamic.end()) { + if (dit != mDynamic.end()) return &dit->second; - } typename std::map::const_iterator it = mStatic.find(idLower); - - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) return &(it->second); - } return 0; } @@ -156,10 +153,8 @@ namespace MWWorld { std::string idLower = Misc::StringUtils::lowerCase(id); typename std::map::const_iterator it = mStatic.find(idLower); - - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) return &(it->second); - } return 0; } @@ -289,7 +284,7 @@ namespace MWWorld typename std::map::iterator it = mStatic.find(idLower); - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) { // delete from the static part of mShared typename std::vector::iterator sharedIter = mShared.begin(); typename std::vector::iterator end = sharedIter + mStatic.size(); @@ -566,7 +561,7 @@ namespace MWWorld std::map::const_iterator it = mInt.find(cell.mName); - if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) { + if (it != mInt.end()) { return &(it->second); } @@ -1129,9 +1124,8 @@ namespace MWWorld { auto it = mStatic.find(Misc::StringUtils::lowerCase(id)); - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + if (it != mStatic.end()) mStatic.erase(it); - } return true; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index ab6ba27547..4b9852d656 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -232,19 +232,3 @@ void ESM::CellRef::blank() mPos.rot[i] = 0; } } - -bool ESM::operator== (const RefNum& left, const RefNum& right) -{ - return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; -} - -bool ESM::operator< (const RefNum& left, const RefNum& right) -{ - if (left.mIndexright.mIndex) - return false; - - return left.mContentFileright.mIndex) + return false; + return left.mContentFilesetAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(static_cast(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON); } void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) @@ -408,7 +408,7 @@ MaterialColorController::MaterialColorController(const MaterialColorController & void MaterialColorController::setDefaults(osg::StateSet *stateset) { - stateset->setAttribute(osg::clone(mBaseMaterial.get(), osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); + stateset->setAttribute(static_cast(mBaseMaterial->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::StateAttribute::ON); } void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 1b1e469bc2..804a8f8ab8 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -277,7 +277,7 @@ Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op) , mPlacer(copy.mPlacer) , mShooter(copy.mShooter) // need a deep copy because the remainder is stored in the object - , mCounter(osg::clone(copy.mCounter.get(), osg::CopyOp::DEEP_COPY_ALL)) + , mCounter(static_cast(copy.mCounter->clone(osg::CopyOp::DEEP_COPY_ALL))) { } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 29b312670c..d06a072734 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -577,7 +577,7 @@ namespace Resource osg::ref_ptr SceneManager::createInstance(const osg::Node *base) { - osg::ref_ptr cloned = osg::clone(base, SceneUtil::CopyOp()); + osg::ref_ptr cloned = static_cast(base->clone(SceneUtil::CopyOp())); // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache cloned->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(base)); diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 0df0f4a5b2..c3261515d6 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -22,20 +22,11 @@ namespace SceneUtil | osg::CopyOp::DEEP_COPY_USERDATA); } - osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const - { - if (!stateset) - return nullptr; - if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) - return osg::clone(stateset, *this); - return const_cast(stateset); - } - osg::Object* CopyOp::operator ()(const osg::Object* node) const { // We should copy node transformations when we copy node - if (const NifOsg::NodeUserData* data = dynamic_cast(node)) - return osg::clone(data, *this); + if (dynamic_cast(node)) + return static_cast(node->clone(*this)); return osg::CopyOp::operator()(node); } @@ -60,7 +51,7 @@ namespace SceneUtil if (dynamic_cast(drawable) || dynamic_cast(drawable)) { - return osg::clone(drawable, *this); + return static_cast(drawable->clone(*this)); } return osg::CopyOp::operator()(drawable); @@ -68,7 +59,7 @@ namespace SceneUtil osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const { - osgParticle::ParticleProcessor* cloned = osg::clone(processor, osg::CopyOp::DEEP_COPY_CALLBACKS); + osgParticle::ParticleProcessor* cloned = static_cast(processor->clone(osg::CopyOp::DEEP_COPY_CALLBACKS)); for (const auto& oldPsNewPsPair : mOldPsToNewPs) { if (processor->getParticleSystem() == oldPsNewPsPair.first) @@ -84,7 +75,7 @@ namespace SceneUtil osgParticle::ParticleSystem* CopyOp::operator ()(const osgParticle::ParticleSystem* partsys) const { - osgParticle::ParticleSystem* cloned = osg::clone(partsys, *this); + osgParticle::ParticleSystem* cloned = static_cast(partsys->clone(*this)); for (const auto& processorPsPair : mProcessorToOldPs) { diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp index 20788799fa..cf6d79e683 100644 --- a/components/sceneutil/clone.hpp +++ b/components/sceneutil/clone.hpp @@ -17,7 +17,6 @@ namespace SceneUtil /// @par Defines the cloning behaviour we need: /// * Assigns updated ParticleSystem pointers on cloned emitters and programs. - /// * Creates deep copy of StateSets if they have a DYNAMIC data variance. /// * Deep copies RigGeometry and MorphGeometry so they can animate without affecting clones. /// @warning Do not use an object of this class for more than one copy operation. class CopyOp : public osg::CopyOp @@ -31,7 +30,6 @@ namespace SceneUtil virtual osg::Node* operator() (const osg::Node* node) const; virtual osg::Drawable* operator() (const osg::Drawable* drawable) const; - virtual osg::StateSet* operator() (const osg::StateSet* stateset) const; virtual osg::Object* operator ()(const osg::Object* node) const; private: diff --git a/components/sceneutil/morphgeometry.cpp b/components/sceneutil/morphgeometry.cpp index 01bb35c455..04fd6fb365 100644 --- a/components/sceneutil/morphgeometry.cpp +++ b/components/sceneutil/morphgeometry.cpp @@ -44,7 +44,7 @@ void MorphGeometry::setSourceGeometry(osg::ref_ptr sourceGeom) osg::ref_ptr vbo (new osg::VertexBufferObject); vbo->setUsage(GL_DYNAMIC_DRAW_ARB); - osg::ref_ptr vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr vertexArray = static_cast(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); if (vertexArray) { vertexArray->setVertexBufferObject(vbo); diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 71cb8a2e52..e8a9451143 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -600,7 +600,7 @@ bool needvbo(const osg::Geometry* geom) osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom) { - array = osg::clone(array, osg::CopyOp::DEEP_COPY_ALL); + array = static_cast(array->clone(osg::CopyOp::DEEP_COPY_ALL)); if (!vbo && needvbo(geom)) vbo = new osg::VertexBufferObject; if (vbo) @@ -1135,7 +1135,7 @@ osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObjec { if (ps->referenceCount() <= 1) return ps; - ps = osg::clone(ps, osg::CopyOp::DEEP_COPY_ALL); + ps = static_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::DrawElements* drawElements = ps->getDrawElements(); if (!drawElements) return ps; diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 627353b996..b9201fdf66 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -77,7 +77,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) osg::ref_ptr vbo (new osg::VertexBufferObject); vbo->setUsage(GL_DYNAMIC_DRAW_ARB); - osg::ref_ptr vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr vertexArray = static_cast(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); if (vertexArray) { vertexArray->setVertexBufferObject(vbo); @@ -86,7 +86,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) if (const osg::Array* normals = from.getNormalArray()) { - osg::ref_ptr normalArray = osg::clone(normals, osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr normalArray = static_cast(normals->clone(osg::CopyOp::DEEP_COPY_ALL)); if (normalArray) { normalArray->setVertexBufferObject(vbo); @@ -97,7 +97,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) if (const osg::Vec4Array* tangents = dynamic_cast(from.getTexCoordArray(7))) { mSourceTangents = tangents; - osg::ref_ptr tangentArray = osg::clone(tangents, osg::CopyOp::DEEP_COPY_ALL); + osg::ref_ptr tangentArray = static_cast(tangents->clone(osg::CopyOp::DEEP_COPY_ALL)); tangentArray->setVertexBufferObject(vbo); to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX); }