crashfix + optimiziation

Signed-off-by: Bret Curtis <psi29a@gmail.com>
pull/2911/head
bzzt lost a hitlab login 5 years ago committed by Bret Curtis
parent c0f128bcb3
commit ce505a9bb3

@ -1,5 +1,7 @@
#include "objectpaging.hpp"
#include <unordered_map>
#include <osg/Version>
#include <osg/LOD>
#include <osg/Switch>
@ -14,7 +16,6 @@
#include <osgParticle/ParticleProcessor>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/Emitter>
#include <components/sceneutil/morphgeometry.hpp>
#include <components/sceneutil/riggeometry.hpp>
@ -60,7 +61,7 @@ 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)
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)
{
if (!far)return nullptr;
ChunkId id = std::make_tuple(center, size);
@ -70,7 +71,7 @@ namespace MWRender
return obj->asNode();
else
{
osg::ref_ptr<osg::Node> node = createChunk(size, center, viewPoint);
osg::ref_ptr<osg::Node> 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<const SceneUtil::RigGeometry*>(drawable))
return osg::CopyOp::operator()(rig->getSourceGeometry());
return operator()(rig->getSourceGeometry());
if (const SceneUtil::MorphGeometry* morph = dynamic_cast<const SceneUtil::MorphGeometry*>(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<osg::ref_ptr<const Object>> mObjects;
};
class AnalyzeVisitor : public osg::NodeVisitor
{
public:
AnalyzeVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mCurrentStateSet(nullptr) {}
typedef std::unordered_map<osg::StateSet*, unsigned int> 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<ChunkId>(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<osg::Node> ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint)
osg::ref_ptr<osg::Node> 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<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;
@ -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<const ESM::CellRef*> mInstances;
AnalyzeVisitor::Result mAnalyzeResult;
};
typedef std::map<osg::ref_ptr<const osg::Node>, 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<const osg::Node> 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<const osg::Node> 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<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);
auto emplaced = nodes.emplace(cnode, InstanceList());
if (emplaced.second)
{
const_cast<osg::Node*>(cnode.get())->accept(analyzeVisitor);
emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult();
}
emplaced.first->second.mInstances.push_back(&ref);
}
if (mMergeGeometry)
osg::ref_ptr<osg::Group> group = new osg::Group;
osg::ref_ptr<osg::Group> mergeGroup = new osg::Group;
osg::ref_ptr<TemplateRef> 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<osg::Node> 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<osg::MatrixTransform> 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;
}

@ -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) override;
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> createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint);
osg::ref_ptr<osg::Node> 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;

@ -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;

@ -18,6 +18,7 @@
#include "optimizer.hpp"
#include <osg/Version>
#include <osg/Transform>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
@ -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<osg::Array*>(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<osg::Array*>(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<osg::PrimitiveSet*>(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; i<geom->getNumPrimitiveSets(); ++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." <<std::endl;
@ -1602,6 +1644,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom
if (lhs.getNormalArray() && rhs.getNormalArray() && lhs.getNormalArray()->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." <<std::endl;
@ -1615,6 +1659,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom
if (lhs.getColorArray() && rhs.getColorArray() && lhs.getColorArray()->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." <<std::endl;
@ -1627,6 +1673,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom
if (lhs.getSecondaryColorArray() && rhs.getSecondaryColorArray() && lhs.getSecondaryColorArray()->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." <<std::endl;
@ -1639,6 +1687,8 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom
if (lhs.getFogCoordArray() && rhs.getFogCoordArray() && lhs.getFogCoordArray()->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." <<std::endl;
@ -1653,6 +1703,9 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom
unsigned int unit;
for(unit=0;unit<lhs.getNumTexCoordArrays();++unit)
{
if (!lhs.getTexCoordArray(unit)) continue;
if (lhs.getTexCoordArray(unit)->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." <<std::endl;
@ -1661,6 +1714,9 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom
for(unit=0;unit<lhs.getNumVertexAttribArrays();++unit)
{
if (!lhs.getVertexAttribArray(unit)) continue;
if (lhs.getVertexAttribArray(unit)->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." <<std::endl;
@ -1668,6 +1724,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom
}
// shift the indices of the incoming primitives to account for the pre existing geometry.
osg::ElementBufferObject* ebo = nullptr;
osg::Geometry::PrimitiveSetList::iterator primItr;
for(primItr=rhs.getPrimitiveSetList().begin(); primItr!=rhs.getPrimitiveSetList().end(); ++primItr)
{
@ -1689,6 +1746,11 @@ 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(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;
}

@ -34,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T
}
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f &center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint)
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f &center, 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<osg::Object> obj = mCache->getRefFromObjectCache(id);
@ -42,7 +42,7 @@ osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f &cen
return obj->asNode();
else
{
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags);
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile);
mCache->addEntryToObjectCache(id, node.get());
return node;
}
@ -160,7 +160,7 @@ std::vector<osg::ref_ptr<osg::StateSet> > ChunkManager::createPasses(float chunk
return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale);
}
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags)
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile)
{
osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);
osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);
@ -221,7 +221,7 @@ osg::ref_ptr<osg::Node> 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);
}

@ -35,7 +35,7 @@ namespace Terrain
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, bool far, const osg::Vec3f& viewPoint);
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);
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<osg::Node> createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags);
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile);
osg::ref_ptr<osg::Texture2D> createCompositeMapRTT();

@ -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<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)
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
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<QuadTreeWorld::ChunkManager*>& chunkManagers)
void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector<QuadTreeWorld::ChunkManager*>& 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<osg::Node> 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; i<vd->getNumEntries(); ++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<osgUtil::CullVisitor*>(&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; i<vd->getNumEntries(); ++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<ViewData*>(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; i<vd->getNumEntries() && !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();
}

@ -47,7 +47,7 @@ namespace Terrain
{
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 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) = 0;
virtual unsigned int getNodeMask() { return 0; }
};
void addChunkManager(ChunkManager*);
@ -58,7 +58,6 @@ namespace Terrain
osg::ref_ptr<RootNode> mRootNode;
osg::ref_ptr<ViewDataMap> mViewDataMap;
osg::ref_ptr<LodCallback> mLodCallback;
std::vector<ChunkManager*> mChunkManagers;

@ -58,7 +58,7 @@ 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, false, osg::Vec3f());
osg::ref_ptr<osg::Node> node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true);
if (!node)
return nullptr;

@ -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

Loading…
Cancel
Save