mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 08:45:33 +00:00
activegrid paging = 2xfps
Signed-off-by: Bret Curtis <psi29a@gmail.com>
This commit is contained in:
parent
b4af2ac672
commit
c7fda6d280
13 changed files with 158 additions and 68 deletions
|
@ -18,6 +18,7 @@
|
|||
#include <osgParticle/ParticleProcessor>
|
||||
#include <osgParticle/ParticleSystemUpdater>
|
||||
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
@ -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<osg::Node> 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<osg::Node> 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<osg::Object> obj = mCache->getRefFromObjectCache(id);
|
||||
if (obj)
|
||||
return obj->asNode();
|
||||
else
|
||||
{
|
||||
osg::ref_ptr<osg::Node> node = createChunk(size, center, viewPoint, compile);
|
||||
osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile);
|
||||
mCache->addEntryToObjectCache(id, node.get());
|
||||
return node;
|
||||
}
|
||||
|
@ -231,6 +237,15 @@ namespace MWRender
|
|||
std::vector<osg::ref_ptr<const Object>> mObjects;
|
||||
};
|
||||
|
||||
class RefnumSet : public osg::Object
|
||||
{
|
||||
public:
|
||||
RefnumSet(){}
|
||||
RefnumSet(const RefnumSet& copy, const osg::CopyOp&) : mRefnums(copy.mRefnums) {}
|
||||
META_Object(MWRender, RefnumSet)
|
||||
std::set<ESM::RefNum> mRefnums;
|
||||
};
|
||||
|
||||
class AnalyzeVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
@ -310,6 +325,7 @@ namespace MWRender
|
|||
: GenericResourceManager<ChunkId>(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<osg::Node> ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile)
|
||||
osg::ref_ptr<osg::Node> 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<osg::ref_ptr<const osg::Node>, InstanceList> NodeMap;
|
||||
NodeMap nodes;
|
||||
osg::ref_ptr<RefnumSet> 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<osg::Group> 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<ESM::RefNum>& 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<RefnumSet*>(udc->getUserObject(0));
|
||||
if (!refnums) return;
|
||||
mOutput.insert(refnums->mRefnums.begin(), refnums->mRefnums.end());
|
||||
}
|
||||
}
|
||||
osg::Vec4i mActiveGrid;
|
||||
std::set<ESM::RefNum>& mOutput;
|
||||
};
|
||||
|
||||
void ObjectPaging::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &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());
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace MWWorld
|
|||
namespace MWRender
|
||||
{
|
||||
|
||||
typedef std::tuple<osg::Vec2f, float> ChunkId; // Center, Size
|
||||
typedef std::tuple<osg::Vec2f, float, bool> ChunkId; // Center, Size, ActiveGrid
|
||||
|
||||
class ObjectPaging : public Resource::GenericResourceManager<ChunkId>, public Terrain::QuadTreeWorld::ChunkManager
|
||||
{
|
||||
|
@ -27,9 +27,9 @@ namespace MWRender
|
|||
ObjectPaging(Resource::SceneManager* sceneManager);
|
||||
~ObjectPaging() = default;
|
||||
|
||||
osg::ref_ptr<osg::Node> 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<osg::Node> 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<osg::Node> createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile);
|
||||
osg::ref_ptr<osg::Node> 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<ESM::RefNum> &out);
|
||||
|
||||
private:
|
||||
Resource::SceneManager* mSceneManager;
|
||||
bool mActiveGrid;
|
||||
bool mDebugBatches;
|
||||
float mMergeFactor;
|
||||
float mMinSize;
|
||||
|
|
|
@ -1492,4 +1492,9 @@ namespace MWRender
|
|||
}
|
||||
return false;
|
||||
}
|
||||
void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out)
|
||||
{
|
||||
if (mObjectPaging)
|
||||
mObjectPaging->getPagedRefnums(activeGrid, out);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ESM::RefNum> &out);
|
||||
|
||||
private:
|
||||
void updateProjectionMatrix();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace MWWorld
|
|||
void setTerrainPreloadPositions(const std::vector<PositionCellGrid>& positions);
|
||||
|
||||
bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp);
|
||||
void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos);
|
||||
void abortTerrainPreloadExcept(const PositionCellGrid &exceptPos);
|
||||
|
||||
private:
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/bulletshape.hpp>
|
||||
#include <components/sceneutil/unrefqueue.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
#include <components/detournavigator/debug.hpp>
|
||||
#include <components/misc/convert.hpp>
|
||||
|
@ -87,7 +88,7 @@ namespace
|
|||
}
|
||||
|
||||
void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
|
||||
MWRender::RenderingManager& rendering)
|
||||
MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& 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<std::pair<int, int>> 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<int, int>& 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<PositionCellGrid> vec;
|
||||
vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
|
||||
mPreloader->abortTerrainPreloadExcept(vec[0]);
|
||||
mPreloader->setTerrainPreloadPositions(vec);
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,8 @@ namespace MWWorld
|
|||
|
||||
osg::Vec3f mLastPlayerPos;
|
||||
|
||||
std::set<ESM::RefNum> mPagedRefs;
|
||||
|
||||
void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false);
|
||||
osg::Vec2i mCurrentGridCenter;
|
||||
|
||||
|
|
|
@ -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; i<getNumChildren(); ++i)
|
||||
getChild(i)->traverseNodes(vd, viewPoint, lodCallback, maxDist);
|
||||
getChild(i)->traverseNodes(vd, viewPoint, lodCallback);
|
||||
}
|
||||
else
|
||||
vd->add(this);
|
||||
}
|
||||
|
||||
void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<unsigned int>(node->getSize()/mMinSize));
|
||||
int lodLevel = Log2(static_cast<unsigned int>(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<int>(center.x() - halfSize), static_cast<int>(center.y() - halfSize), static_cast<int>(center.x() + halfSize), static_cast<int>(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<unsigned int>(node->getSize()/mMinSize));
|
||||
int lodLevel = Log2(static_cast<unsigned int>(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<osg::Node> n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile);
|
||||
osg::ref_ptr<osg::Node> 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<osgUtil::CullVisitor*>(&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<ViewData*>(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; i<vd->getNumEntries(); ++i)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue