mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-28 06:39:40 +00:00
terrainbased objectpaging
Signed-off-by: Bret Curtis <psi29a@gmail.com>
This commit is contained in:
parent
87dba1fcfc
commit
d684f1a78f
29 changed files with 648 additions and 142 deletions
|
@ -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
|
||||
|
|
302
apps/openmw/mwrender/objectpaging.cpp
Normal file
302
apps/openmw/mwrender/objectpaging.cpp
Normal file
|
@ -0,0 +1,302 @@
|
|||
#include "objectpaging.hpp"
|
||||
|
||||
#include <osg/Version>
|
||||
#include <osg/LOD>
|
||||
#include <osg/Switch>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osgUtil/IncrementalCompileOperation>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/sceneutil/optimizer.hpp>
|
||||
#include <components/sceneutil/clone.hpp>
|
||||
|
||||
#include <osgParticle/ParticleProcessor>
|
||||
#include <osgParticle/ParticleSystemUpdater>
|
||||
#include <osgParticle/Emitter>
|
||||
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#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<ESM::Static>().searchStatic(id)->mModel;
|
||||
case ESM::REC_ACTI:
|
||||
return store.get<ESM::Activator>().searchStatic(id)->mModel;
|
||||
case ESM::REC_DOOR:
|
||||
return store.get<ESM::Door>().searchStatic(id)->mModel;
|
||||
case ESM::REC_CONT:
|
||||
return store.get<ESM::Container>().searchStatic(id)->mModel;
|
||||
default: throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!far)return nullptr;
|
||||
ChunkId id = std::make_tuple(center, size);
|
||||
|
||||
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);
|
||||
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<const osgParticle::ParticleProcessor*>(node))
|
||||
return nullptr;
|
||||
if (dynamic_cast<const osgParticle::ParticleSystemUpdater*>(node))
|
||||
return nullptr;
|
||||
|
||||
if (const osg::Switch* sw = node->asSwitch())
|
||||
{
|
||||
osg::Group* n = new osg::Group;
|
||||
for (unsigned int i=0; i<sw->getNumChildren(); ++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<const osg::LOD*>(node))
|
||||
{
|
||||
osg::Group* n = new osg::Group;
|
||||
for (unsigned int i=0; i<lod->getNumChildren(); ++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<const osgParticle::ParticleSystem*>(drawable))
|
||||
return nullptr;
|
||||
|
||||
if (const SceneUtil::RigGeometry* rig = dynamic_cast<const SceneUtil::RigGeometry*>(drawable))
|
||||
return osg::CopyOp::operator()(rig->getSourceGeometry());
|
||||
if (const SceneUtil::MorphGeometry* morph = dynamic_cast<const SceneUtil::MorphGeometry*>(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<ChunkId>(nullptr)
|
||||
, mSceneManager(sceneManager)
|
||||
{
|
||||
mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain");
|
||||
mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain");
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> 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<osg::Group> 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<ESM::CellRef> refs;
|
||||
std::vector<ESM::ESMReader> 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<ESM::Cell>().searchStatic(cellX, cellY);
|
||||
if (!cell) continue;
|
||||
for (size_t i=0; i<cell->mContextList.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<const osg::Node> 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<osg::Node> 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<osg::MatrixTransform> 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;
|
||||
}
|
||||
|
||||
}
|
44
apps/openmw/mwrender/objectpaging.hpp
Normal file
44
apps/openmw/mwrender/objectpaging.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H
|
||||
#define OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H
|
||||
|
||||
#include <components/terrain/quadtreeworld.hpp>
|
||||
#include <components/resource/resourcemanager.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class SceneManager;
|
||||
}
|
||||
namespace MWWorld
|
||||
{
|
||||
class ESMStore;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
typedef std::tuple<osg::Vec2f, float> ChunkId; // Center, Size
|
||||
|
||||
class ObjectPaging : public Resource::GenericResourceManager<ChunkId>, public Terrain::QuadTreeWorld::ChunkManager
|
||||
{
|
||||
public:
|
||||
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) override;
|
||||
|
||||
osg::ref_ptr<osg::Node> 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
|
|
@ -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<Terrain::QuadTreeWorld*>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Water> mWater;
|
||||
std::unique_ptr<Terrain::World> mTerrain;
|
||||
TerrainStorage* mTerrainStorage;
|
||||
std::unique_ptr<ObjectPaging> mObjectPaging;
|
||||
std::unique_ptr<SkyManager> mSky;
|
||||
std::unique_ptr<FogManager> mFog;
|
||||
std::unique_ptr<EffectManager> mEffectManager;
|
||||
|
|
|
@ -172,7 +172,7 @@ namespace MWWorld
|
|||
class TerrainPreloadItem : public SceneUtil::WorkItem
|
||||
{
|
||||
public:
|
||||
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<osg::Vec3f>& preloadPositions)
|
||||
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<CellPreloader::PositionCellGrid>& preloadPositions)
|
||||
: mAbort(false)
|
||||
, mTerrainViews(views)
|
||||
, mWorld(world)
|
||||
|
@ -191,7 +191,7 @@ namespace MWWorld
|
|||
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
||||
{
|
||||
mTerrainViews[i]->reset();
|
||||
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<bool> mAbort;
|
||||
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
||||
Terrain::World* mWorld;
|
||||
std::vector<osg::Vec3f> mPreloadPositions;
|
||||
std::vector<CellPreloader::PositionCellGrid> 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<osg::Vec3f> &positions)
|
||||
void CellPreloader::setTerrainPreloadPositions(const std::vector<CellPreloader::PositionCellGrid> &positions)
|
||||
{
|
||||
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
|
||||
return;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <map>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/Vec4i>
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
|
||||
namespace Resource
|
||||
|
@ -68,7 +69,8 @@ namespace MWWorld
|
|||
|
||||
void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue);
|
||||
|
||||
void setTerrainPreloadPositions(const std::vector<osg::Vec3f>& positions);
|
||||
typedef std::pair<osg::Vec3f, osg::Vec4i> PositionCellGrid;
|
||||
void setTerrainPreloadPositions(const std::vector<PositionCellGrid>& positions);
|
||||
|
||||
private:
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
|
@ -105,7 +107,7 @@ namespace MWWorld
|
|||
PreloadMap mPreloadCells;
|
||||
|
||||
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
||||
std::vector<osg::Vec3f> mTerrainPreloadPositions;
|
||||
std::vector<PositionCellGrid> mTerrainPreloadPositions;
|
||||
osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem;
|
||||
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
|
||||
};
|
||||
|
|
|
@ -136,6 +136,10 @@ void ESMStore::setUp(bool validateRecords)
|
|||
mIds[*record] = storeIt->first;
|
||||
}
|
||||
}
|
||||
|
||||
if (mStaticIds.empty())
|
||||
mStaticIds = mIds;
|
||||
|
||||
mSkills.setUp();
|
||||
mMagicEffects.setUp();
|
||||
mAttributes.setUp();
|
||||
|
|
|
@ -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<std::string, int> mIds;
|
||||
std::map<std::string, int> mStaticIds;
|
||||
|
||||
std::map<int, StoreBase *> mStores;
|
||||
|
||||
ESM::NPC mPlayerTemplate;
|
||||
|
@ -99,6 +101,14 @@ namespace MWWorld
|
|||
}
|
||||
return it->second;
|
||||
}
|
||||
int findStatic(const std::string &id) const
|
||||
{
|
||||
std::map<std::string, int>::const_iterator it = mStaticIds.find(id);
|
||||
if (it == mStaticIds.end()) {
|
||||
return 0;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ESMStore()
|
||||
: mDynamicCount(0)
|
||||
|
|
|
@ -286,28 +286,6 @@ namespace MWWorld
|
|||
::updateObjectScale(ptr, *mPhysics, mRendering);
|
||||
}
|
||||
|
||||
void Scene::getGridCenter(int &cellX, int &cellY)
|
||||
{
|
||||
int maxX = std::numeric_limits<int>::min();
|
||||
int maxY = std::numeric_limits<int>::min();
|
||||
int minX = std::numeric_limits<int>::max();
|
||||
int minY = std::numeric_limits<int>::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<osg::Vec3f> exteriorPositions;
|
||||
std::vector<PositionCellGrid> 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<osg::Vec3f>& exteriorPositions)
|
||||
void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions)
|
||||
{
|
||||
std::vector<MWWorld::ConstPtr> 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<osg::Vec3f> vec;
|
||||
vec.push_back(pos);
|
||||
std::vector<PositionCellGrid> vec;
|
||||
vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
|
||||
mPreloader->setTerrainPreloadPositions(vec);
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1137,7 @@ namespace MWWorld
|
|||
std::vector<ESM::Transport::Dest> mList;
|
||||
};
|
||||
|
||||
void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector<osg::Vec3f>& 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<PositionCellGrid>& 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef GAME_MWWORLD_SCENE_H
|
||||
#define GAME_MWWORLD_SCENE_H
|
||||
|
||||
#include <osg/Vec4i>
|
||||
#include <osg/Vec2i>
|
||||
|
||||
#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<osg::Vec3f, osg::Vec4i> PositionCellGrid;
|
||||
|
||||
void preloadCells(float dt);
|
||||
void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<osg::Vec3f>& exteriorPositions);
|
||||
void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions);
|
||||
void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos);
|
||||
void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<osg::Vec3f>& exteriorPositions);
|
||||
void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions);
|
||||
|
||||
osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const;
|
||||
osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const;
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -151,6 +151,19 @@ namespace MWWorld
|
|||
|
||||
return 0;
|
||||
}
|
||||
template<typename T>
|
||||
const T *Store<T>::searchStatic(const std::string &id) const
|
||||
{
|
||||
std::string idLower = Misc::StringUtils::lowerCase(id);
|
||||
typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower);
|
||||
|
||||
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
|
||||
return &(it->second);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Store<T>::isDynamic(const std::string &id) const
|
||||
{
|
||||
|
@ -582,6 +595,18 @@ namespace MWWorld
|
|||
|
||||
return 0;
|
||||
}
|
||||
const ESM::Cell *Store<ESM::Cell>::searchStatic(int x, int y) const
|
||||
{
|
||||
ESM::Cell cell;
|
||||
cell.mData.mX = x, cell.mData.mY = y;
|
||||
|
||||
std::pair<int, int> key(x, y);
|
||||
DynamicExt::const_iterator it = mExt.find(key);
|
||||
if (it != mExt.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const ESM::Cell *Store<ESM::Cell>::searchOrCreate(int x, int y)
|
||||
{
|
||||
std::pair<int, int> key(x, y);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -466,7 +466,7 @@ namespace Resource
|
|||
return options;
|
||||
}
|
||||
|
||||
osg::ref_ptr<const osg::Node> SceneManager::getTemplate(const std::string &name)
|
||||
osg::ref_ptr<const osg::Node> 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<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
|
||||
while (mIncrementalCompileOperation->getToCompile().size() > 1000)
|
||||
mIncrementalCompileOperation->getToCompile().pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::clearCache()
|
||||
|
|
|
@ -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<const osg::Node> getTemplate(const std::string& name);
|
||||
osg::ref_ptr<const osg::Node> 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.
|
||||
|
|
|
@ -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<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& 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<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& 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<osg::PrimitiveSet*>(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<osg::Geometry> 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<osg::PrimitiveSet*> 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;unit<geom.getNumTexCoordArrays();++unit)
|
||||
{
|
||||
osg::Array* tex = geom.getTexCoordArray(unit);
|
||||
if (tex && tex->referenceCount()>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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<const osg::Object*,unsigned int> 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<osg::StateSet*> _stateSetStack;
|
||||
bool _allowedToMerge;
|
||||
bool _alphaBlendingActive;
|
||||
bool _mergeAlphaBlending;
|
||||
osg::Vec3f _viewPoint;
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -106,7 +106,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeometry)
|
|||
}
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> RigGeometry::getSourceGeometry()
|
||||
osg::ref_ptr<osg::Geometry> RigGeometry::getSourceGeometry() const
|
||||
{
|
||||
return mSourceGeometry;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace SceneUtil
|
|||
/// @note The source geometry will not be modified.
|
||||
void setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom);
|
||||
|
||||
osg::ref_ptr<osg::Geometry> getSourceGeometry();
|
||||
osg::ref_ptr<osg::Geometry> getSourceGeometry() const;
|
||||
|
||||
virtual void accept(osg::NodeVisitor &nv);
|
||||
virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <components/resource/objectcache.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
#include "terraindrawable.hpp"
|
||||
|
@ -35,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T
|
|||
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags)
|
||||
osg::ref_ptr<osg::Node> 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<osg::Object> obj = mCache->getRefFromObjectCache(id);
|
||||
|
@ -163,10 +162,6 @@ std::vector<osg::ref_ptr<osg::StateSet> > ChunkManager::createPasses(float chunk
|
|||
|
||||
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags)
|
||||
{
|
||||
osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize();
|
||||
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> transform (new SceneUtil::PositionAttitudeTransform);
|
||||
transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f));
|
||||
|
||||
osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);
|
||||
osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);
|
||||
osg::ref_ptr<osg::Vec4ubArray> colors (new osg::Vec4ubArray);
|
||||
|
@ -224,16 +219,15 @@ osg::ref_ptr<osg::Node> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <components/resource/resourcemanager.hpp>
|
||||
|
||||
#include "buffercache.hpp"
|
||||
#include "quadtreeworld.hpp"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
|
@ -29,23 +30,28 @@ namespace Terrain
|
|||
typedef std::tuple<osg::Vec2f, unsigned char, unsigned int> ChunkId; // Center, Lod, Lod Flags
|
||||
|
||||
/// @brief Handles loading and caching of terrain chunks
|
||||
class ChunkManager : public Resource::GenericResourceManager<ChunkId>
|
||||
class ChunkManager : public Resource::GenericResourceManager<ChunkId>, public QuadTreeWorld::ChunkManager
|
||||
{
|
||||
public:
|
||||
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer);
|
||||
|
||||
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags);
|
||||
osg::ref_ptr<osg::Node> 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<osg::Node> 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;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/sceneutil/mwshadowtechnique.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
||||
#include "quadtreenode.hpp"
|
||||
#include "storage.hpp"
|
||||
|
@ -63,6 +64,12 @@ public:
|
|||
int nativeLodLevel = Log2(static_cast<unsigned int>(node->getSize()/mMinSize));
|
||||
int lodLevel = Log2(static_cast<unsigned int>(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<QuadTreeWorld::ChunkManager*>& 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<osgUtil::CullVisitor*>(&nv)->getCurrentCamera(), nv.getViewPoint(), needsUpdate);
|
||||
vd = mViewDataMap->getViewData(static_cast<osgUtil::CullVisitor*>(&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; i<vd->getNumEntries(); ++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<ViewData*>(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; i<vd->getNumEntries(); ++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<bool> &abort)
|
||||
void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic<bool> &abort)
|
||||
{
|
||||
ensureQuadTreeBuilt();
|
||||
|
||||
ViewData* vd = static_cast<ViewData*>(view);
|
||||
vd->setViewPoint(viewPoint);
|
||||
vd->setActiveGrid(grid);
|
||||
mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance);
|
||||
|
||||
const float cellWorldSize = mStorage->getCellWorldSize();
|
||||
|
||||
for (unsigned int i=0; i<vd->getNumEntries() && !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<osg::Object> dummy = new osg::DummyObject;
|
||||
const ViewData* vd = static_cast<const ViewData*>(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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<bool>& abort);
|
||||
void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic<bool>& 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<osg::Node> 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<ViewDataMap> mViewDataMap;
|
||||
osg::ref_ptr<LodCallback> mLodCallback;
|
||||
|
||||
std::vector<ChunkManager*> mChunkManagers;
|
||||
|
||||
OpenThreads::Mutex mQuadTreeMutex;
|
||||
bool mQuadTreeBuilt;
|
||||
float mLodFactor;
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
#include <osg/Group>
|
||||
#include <osg/ComputeBoundsVisitor>
|
||||
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include "chunkmanager.hpp"
|
||||
#include "compositemaprenderer.hpp"
|
||||
|
||||
#include "storage.hpp"
|
||||
namespace Terrain
|
||||
{
|
||||
|
||||
|
@ -57,12 +58,17 @@ osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chu
|
|||
}
|
||||
else
|
||||
{
|
||||
osg::ref_ptr<osg::Node> node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0);
|
||||
osg::ref_ptr<osg::Node> node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f());
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
const float cellWorldSize = mStorage->getCellWorldSize();
|
||||
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Entry> 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();
|
||||
|
||||
|
|
|
@ -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<osg::Material> 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());
|
||||
|
|
|
@ -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<bool>& abort) {}
|
||||
virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic<bool>& 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<std::pair<int,int>> mLoadedCells;
|
||||
osg::ref_ptr<HeightCullCallback> mHeightCullCallback;
|
||||
|
||||
osg::Vec4i mActiveGrid;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue