From ce505a9bb355233ff1dfbe4017aa4e66711a86e4 Mon Sep 17 00:00:00 2001 From: bzzt lost a hitlab login Date: Thu, 30 Apr 2020 13:37:00 +0000 Subject: [PATCH] crashfix + optimiziation Signed-off-by: Bret Curtis --- apps/openmw/mwrender/objectpaging.cpp | 233 +++++++++++++++++++------- apps/openmw/mwrender/objectpaging.hpp | 6 +- apps/openmw/mwworld/scene.cpp | 2 +- components/sceneutil/optimizer.cpp | 102 +++++++++-- components/terrain/chunkmanager.cpp | 8 +- components/terrain/chunkmanager.hpp | 4 +- components/terrain/quadtreeworld.cpp | 42 +++-- components/terrain/quadtreeworld.hpp | 3 +- components/terrain/terraingrid.cpp | 2 +- files/settings-default.cfg | 4 +- 10 files changed, 300 insertions(+), 106 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 8a80153728..b529336eb6 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -1,5 +1,7 @@ #include "objectpaging.hpp" +#include + #include #include #include @@ -14,7 +16,6 @@ #include #include -#include #include #include @@ -60,7 +61,7 @@ namespace MWRender } } - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { if (!far)return nullptr; ChunkId id = std::make_tuple(center, size); @@ -70,7 +71,7 @@ namespace MWRender return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, viewPoint); + osg::ref_ptr node = createChunk(size, center, viewPoint, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -92,12 +93,11 @@ namespace MWRender class CopyOp : public osg::CopyOp { public: - CopyOp() : mDistance(0.f) { - setCopyFlags(osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES - #if OSG_MIN_VERSION_REQUIRED(3,5,6) - |osg::CopyOp::DEEP_COPY_ARRAYS|osg::CopyOp::DEEP_COPY_PRIMITIVES // damned vbogarbage racing - #endif - ); + CopyOp(bool deep) : mDistance(0.f) { + unsigned int flags = osg::CopyOp::DEEP_COPY_NODES; + if (deep) + flags |= osg::CopyOp::DEEP_COPY_DRAWABLES; + setCopyFlags(flags); } float mDistance; @@ -128,12 +128,13 @@ namespace MWRender return n; } - osg::Node* n = osg::CopyOp::operator()(node); - if (n) { - n->setDataVariance(osg::Object::STATIC); - n->setUserDataContainer(nullptr); - n->setName(""); - } + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + + osg::Node* n = osg::clone(node, *this); + n->setDataVariance(osg::Object::STATIC); + n->setUserDataContainer(nullptr); + n->setName(""); return n; } virtual osg::Drawable* operator() (const osg::Drawable* drawable) const @@ -142,11 +143,20 @@ namespace MWRender return nullptr; if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) - return osg::CopyOp::operator()(rig->getSourceGeometry()); + return operator()(rig->getSourceGeometry()); if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) - return osg::CopyOp::operator()(morph->getSourceGeometry()); + return operator()(morph->getSourceGeometry()); - return osg::CopyOp::operator()(drawable); + if (getCopyFlags() & DEEP_COPY_DRAWABLES) + { + osg::Drawable* d = osg::clone(drawable, *this); + d->setDataVariance(osg::Object::STATIC); + d->setUserDataContainer(nullptr); + d->setName(""); + return d; + } + else + return osg::CopyOp::operator()(drawable); } virtual osg::Callback* operator() (const osg::Callback* callback) const { @@ -154,20 +164,77 @@ namespace MWRender } }; + class TemplateRef : public osg::Object + { + public: + TemplateRef() {} + TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObjects(copy.mObjects) {} + META_Object(MWRender, TemplateRef) + std::vector> mObjects; + }; + + class AnalyzeVisitor : public osg::NodeVisitor + { + public: + AnalyzeVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mCurrentStateSet(nullptr) {} + + typedef std::unordered_map StateSetCounter; + struct Result + { + StateSetCounter mStateSetCounter; + unsigned int mNumVerts = 0; + }; + + virtual void apply(osg::Node& node) + { + if (node.getStateSet()) + mCurrentStateSet = node.getStateSet(); + traverse(node); + } + virtual void apply(osg::Geometry& geom) + { + mResult.mNumVerts += geom.getVertexArray()->getNumElements(); + ++mResult.mStateSetCounter[mCurrentStateSet]; + ++mGlobalStateSetCounter[mCurrentStateSet]; + } + Result retrieveResult() + { + Result result = mResult; + mResult = Result(); + mCurrentStateSet = nullptr; + return result; + } + float getMergeBenefit(const Result& result) + { + if (result.mStateSetCounter.empty()) return 1; + float mergeBenefit = 0; + for (auto pair : result.mStateSetCounter) + { + mergeBenefit += mGlobalStateSetCounter[pair.first]; + } + mergeBenefit /= result.mStateSetCounter.size(); + return mergeBenefit; + } + + Result mResult; + osg::StateSet* mCurrentStateSet; + StateSetCounter mGlobalStateSetCounter; + }; + ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) { - mMergeGeometry = Settings::Manager::getBool("object paging merge geometry", "Terrain"); + mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); } - osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint) + osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile) { osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); - osg::ref_ptr group = new osg::Group; - osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; osg::Vec3f relativeViewPoint = viewPoint - worldCenter; @@ -226,21 +293,18 @@ namespace MWRender osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); + struct InstanceList + { + std::vector mInstances; + AnalyzeVisitor::Result mAnalyzeResult; + }; + typedef std::map, InstanceList> NodeMap; + NodeMap nodes; + AnalyzeVisitor analyzeVisitor; + for (const auto& pair : refs) { const ESM::CellRef& ref = pair.second; - std::string id = Misc::StringUtils::lowerCase(ref.mRefID); - if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") - continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - - int type = store.findStatic(id); - std::string model = "meshes/" + getModel(type, id, store); -/* - bool useAnim = type != ESM::REC_STAT; - if (useAnim) - model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); -*/ - if (model.empty()) continue; osg::Vec3f pos = ref.mPos.asVec3(); if (size < 1.f) @@ -254,50 +318,101 @@ namespace MWRender continue; } - osg::ref_ptr cnode = mSceneManager->getTemplate(model, false); + std::string id = Misc::StringUtils::lowerCase(ref.mRefID); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - float d = (viewPoint - pos).length(); + int type = store.findStatic(id); + std::string model = getModel(type, id, store); + if (model.empty()) continue; + model = "meshes/" + model; +/* + bool useAnim = type != ESM::REC_STAT; + if (useAnim) + model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); +*/ + osg::ref_ptr cnode = mSceneManager->getTemplate(model, compile); + float d = (viewPoint - pos).length(); if (cnode->getBound().radius() * ref.mScale < d*mMinSize) continue; - CopyOp co = CopyOp(); - co.mDistance = d; - osg::ref_ptr node = osg::clone(cnode.get(), co); - node->setUserDataContainer(nullptr); - - osg::Matrixf matrix; - matrix.preMultTranslate(pos - worldCenter); - matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * - osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * - osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); - matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); - osg::ref_ptr trans = new osg::MatrixTransform(matrix); - trans->addChild(node); - trans->setDataVariance(osg::Object::STATIC); - - group->addChild(trans); + auto emplaced = nodes.emplace(cnode, InstanceList()); + if (emplaced.second) + { + const_cast(cnode.get())->accept(analyzeVisitor); + emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); + } + emplaced.first->second.mInstances.push_back(&ref); } - if (mMergeGeometry) + osg::ref_ptr group = new osg::Group; + osg::ref_ptr mergeGroup = new osg::Group; + osg::ref_ptr templateRefs = new TemplateRef; + for (const auto& pair : nodes) + { + const osg::Node* cnode = pair.first; + + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + templateRefs->mObjects.push_back(cnode); + + const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; + + float mergeCost = analyzeResult.mNumVerts * size; + float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; + bool merge = mergeBenefit > mergeCost; + + for (auto cref : pair.second.mInstances) + { + const ESM::CellRef& ref = *cref; + osg::Vec3f pos = ref.mPos.asVec3(); + float d = (viewPoint - pos).length(); + + CopyOp co = CopyOp(merge); + co.mDistance = d; + osg::ref_ptr node = osg::clone(cnode, co); + node->setUserDataContainer(nullptr); + + osg::Matrixf matrix; + matrix.preMultTranslate(pos - worldCenter); + matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * + osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * + osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); + matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); + osg::ref_ptr trans = new osg::MatrixTransform(matrix); + trans->addChild(node); + trans->setDataVariance(osg::Object::STATIC); + + if (merge) + mergeGroup->addChild(trans); + else + group->addChild(trans); + } + } + + if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; - if ((relativeViewPoint - group->getBound().center()).length2() > group->getBound().radius2()) + if ((relativeViewPoint - mergeGroup->getBound().center()).length2() > mergeGroup->getBound().radius2()) { optimizer.setViewPoint(relativeViewPoint); optimizer.setMergeAlphaBlending(true); } optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; - optimizer.optimize(group, options); - } + optimizer.optimize(mergeGroup, options); + + group->addChild(mergeGroup); - auto ico = mSceneManager->getIncrementalCompileOperation(); - if (ico) ico->add(group); - else group->getBound(); + auto ico = mSceneManager->getIncrementalCompileOperation(); + if (compile && ico) ico->add(mergeGroup); + } + group->getBound(); group->setNodeMask(Mask_Static); + group->getOrCreateUserDataContainer()->addUserObject(templateRefs); + return group; } diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 8c3c2b4935..16de6e8bc6 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -27,9 +27,9 @@ namespace MWRender ObjectPaging(Resource::SceneManager* sceneManager); ~ObjectPaging() = default; - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) override; + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) override; - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, const osg::Vec3f& viewPoint, bool compile); virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } @@ -40,7 +40,7 @@ namespace MWRender private: Resource::SceneManager* mSceneManager; - bool mMergeGeometry; + float mMergeFactor; float mMinSize; OpenThreads::Mutex mDisabledMutex; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 127b56c94f..1dac18eaa9 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -477,7 +477,7 @@ namespace MWWorld { float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); - float distance = std::max(std::abs(centerY-pos.x()), std::abs(centerY-pos.y())); + float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold if (distance <= maxDistance) return *currentGridCenter; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 7bcccdbe33..985fd9ee25 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -18,6 +18,7 @@ #include "optimizer.hpp" +#include #include #include #include @@ -587,17 +588,36 @@ void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Node& node) traverse(node); } +bool needvbo(const osg::Geometry* geom) +{ +#if OSG_MIN_VERSION_REQUIRED(3,5,6) + return true; +#else + return geom->getUseVertexBufferObjects(); +#endif +} + +osg::Array* cloneArray(osg::Array* array, osg::VertexBufferObject*& vbo, const osg::Geometry* geom) +{ + array = osg::clone(array, osg::CopyOp::DEEP_COPY_ALL); + if (!vbo && needvbo(geom)) + vbo = new osg::VertexBufferObject; + if (vbo) + array->setVertexBufferObject(vbo); + return array; +} void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Drawable& drawable) { osg::Geometry *geometry = drawable.asGeometry(); if((geometry) && (isOperationPermissibleForObject(&drawable))) { + osg::VertexBufferObject* vbo = nullptr; if(geometry->getVertexArray() && geometry->getVertexArray()->referenceCount() > 1) { - geometry->setVertexArray(dynamic_cast(geometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setVertexArray(cloneArray(geometry->getVertexArray(), vbo, geometry)); } if(geometry->getNormalArray() && geometry->getNormalArray()->referenceCount() > 1) { - geometry->setNormalArray(dynamic_cast(geometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); + geometry->setNormalArray(cloneArray(geometry->getNormalArray(), vbo, geometry)); } } _drawableSet.insert(&drawable); @@ -1110,14 +1130,30 @@ void Optimizer::MergeGeometryVisitor::apply(osg::Group &group) popStateSet(); } -osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps) +osg::PrimitiveSet* clonePrimitive(osg::PrimitiveSet* ps, osg::ElementBufferObject*& ebo, const osg::Geometry* geom) { if (ps->referenceCount() <= 1) return ps; - ps = dynamic_cast(ps->clone(osg::CopyOp::DEEP_COPY_ALL)); + ps = osg::clone(ps, osg::CopyOp::DEEP_COPY_ALL); + + osg::DrawElements* drawElements = ps->getDrawElements(); + if (!drawElements) return ps; + + if (!ebo && needvbo(geom)) + ebo = new osg::ElementBufferObject; + if (ebo) + drawElements->setElementBufferObject(ebo); + return ps; } +bool containsSharedPrimitives(const osg::Geometry* geom) +{ + for (unsigned int i=0; igetNumPrimitiveSets(); ++i) + if (geom->getPrimitiveSet(i)->referenceCount() > 1) return true; + return false; +} + bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (!isOperationPermissibleForObject(&group)) return false; @@ -1305,6 +1341,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { osg::Geometry::PrimitiveSetList& primitives = geom->getPrimitiveSetList(); @@ -1317,12 +1354,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) { if (prim->getNumIndices()==3) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::TRIANGLES); } else if (prim->getNumIndices()==4) { - prim = clonePrimitive(prim); (*itr) = prim; + prim = clonePrimitive(prim, ebo, geom); (*itr) = prim; prim->setMode(osg::PrimitiveSet::QUADS); } } @@ -1337,6 +1374,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (!drawable) continue; osg::Geometry* geom = drawable->asGeometry(); + osg::ElementBufferObject* ebo = nullptr; if (geom) { if (geom->getNumPrimitiveSets()>0 && @@ -1379,7 +1417,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) if (combine) { - lhs = clonePrimitive(lhs); + lhs = clonePrimitive(lhs, ebo, geom); primitives[lhsNo] = lhs; switch(lhs->getType()) @@ -1499,6 +1537,12 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) } } #endif + if (doneCombine && !geom->containsSharedArrays() && !containsSharedPrimitives(geom)) + { + // prefer to use vbo for merged geometries as vbo uses less memory than display lists. + geom->setUseVertexBufferObjects(true); + geom->setUseDisplayList(false); + } } } @@ -1578,16 +1622,14 @@ class MergeArrayVisitor : public osg::ArrayVisitor bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs) { - if (lhs.containsSharedArrays()) - lhs.duplicateSharedArrays(); - MergeArrayVisitor merger; - + osg::VertexBufferObject* vbo = nullptr; unsigned int base = 0; if (lhs.getVertexArray() && rhs.getVertexArray()) { - base = lhs.getVertexArray()->getNumElements(); + if (lhs.getVertexArray()->referenceCount() > 1) + lhs.setVertexArray(cloneArray(lhs.getVertexArray(), vbo, &lhs)); if (!merger.merge(lhs.getVertexArray(),rhs.getVertexArray())) { OSG_DEBUG << "MergeGeometry: vertex array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getNormalArray()->referenceCount() > 1) + lhs.setNormalArray(cloneArray(lhs.getNormalArray(), vbo, &lhs)); if (!merger.merge(lhs.getNormalArray(),rhs.getNormalArray())) { OSG_DEBUG << "MergeGeometry: normal array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getColorArray()->referenceCount() > 1) + lhs.setColorArray(cloneArray(lhs.getColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getColorArray(),rhs.getColorArray())) { OSG_DEBUG << "MergeGeometry: color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getSecondaryColorArray()->referenceCount() > 1) + lhs.setSecondaryColorArray(cloneArray(lhs.getSecondaryColorArray(), vbo, &lhs)); if (!merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray())) { OSG_DEBUG << "MergeGeometry: secondary color array not merged. Some data may be lost." <getBinding()!=osg::Array::BIND_OVERALL) { + if (lhs.getFogCoordArray()->referenceCount() > 1) + lhs.setFogCoordArray(cloneArray(lhs.getFogCoordArray(), vbo, &lhs)); if (!merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray())) { OSG_DEBUG << "MergeGeometry: fog coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setTexCoordArray(unit, cloneArray(lhs.getTexCoordArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit))) { OSG_DEBUG << "MergeGeometry: tex coord array not merged. Some data may be lost." <referenceCount() > 1) + lhs.setVertexAttribArray(unit, cloneArray(lhs.getVertexAttribArray(unit), vbo, &lhs)); if (!merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit))) { OSG_DEBUG << "MergeGeometry: vertex attrib array not merged. Some data may be lost." <getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; @@ -1696,13 +1758,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUShort osg::DrawElementsUShort* new_primitive = new osg::DrawElementsUShort(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUByte->begin(),primitiveUByte->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1722,13 +1789,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { // must promote to a DrawElementsUInt osg::DrawElementsUInt* new_primitive = new osg::DrawElementsUInt(primitive->getMode()); + if (needvbo(&lhs)) + { + if (!ebo) ebo = new osg::ElementBufferObject; + new_primitive->setElementBufferObject(ebo); + } std::copy(primitiveUShort->begin(),primitiveUShort->end(),std::back_inserter(*new_primitive)); new_primitive->offsetIndices(base); (*primItr) = new_primitive; } else { - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); } } @@ -1738,7 +1810,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom case(osg::PrimitiveSet::DrawArrayLengthsPrimitiveType): case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): default: - (*primItr) = clonePrimitive(primitive); + (*primItr) = clonePrimitive(primitive, ebo, &lhs); (*primItr)->offsetIndices(base); break; } diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 1dc62cfb80..5f80b9d367 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -34,7 +34,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) { ChunkId id = std::make_tuple(center, lod, lodFlags); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -42,7 +42,7 @@ osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f &cen return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, lod, lodFlags); + osg::ref_ptr node = createChunk(size, center, lod, lodFlags, compile); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -160,7 +160,7 @@ std::vector > ChunkManager::createPasses(float chunk return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale); } -osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags) +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile) { osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); @@ -221,7 +221,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts); - if (mSceneManager->getIncrementalCompileOperation()) + if (compile && mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index d6f4dd98ec..e323da6a4f 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -35,7 +35,7 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile); void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; } void setCompositeMapLevel(float level) { mCompositeMapLevel = level; } @@ -53,7 +53,7 @@ namespace Terrain virtual void setExpiryDelay(double expiryDelay) override { mExpiryDelay = 0.5f; } private: - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile); osg::ref_ptr createCompositeMapRTT(); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 842dced208..d252149b2e 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -53,9 +53,10 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - DefaultLodCallback(float factor, float minSize) + DefaultLodCallback(float factor, float minSize, const osg::Vec4i& grid) : mFactor(factor) , mMinSize(minSize) + , mActiveGrid(grid) { } @@ -64,20 +65,27 @@ public: int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); - if (node->getSize()>1 && dist < (8192+1024)*1.41421356237) + if (node->getSize()>1) { + float halfSize = node->getSize()/2; + const osg::Vec2f& center = node->getCenter(); + osg::Vec4i nodeBounds (static_cast(center.x() - halfSize), static_cast(center.y() - halfSize), static_cast(center.x() + halfSize), static_cast(center.y() + halfSize)); + bool intersects = (std::max(nodeBounds.x(), mActiveGrid.x()) <= std::min(nodeBounds.z(), mActiveGrid.z()) && std::max(nodeBounds.y(), mActiveGrid.y()) <= std::min(nodeBounds.w(), mActiveGrid.w())); // to prevent making chunks who will cross the activegrid border - return false; + if (intersects) + return false; } - return nativeLodLevel <= lodLevel; } private: float mFactor; float mMinSize; + osg::Vec4i mActiveGrid; }; +const float MIN_SIZE = 1/8.f; + class RootNode : public QuadTreeNode { public: @@ -297,7 +305,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewD return lodFlags; } -void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers) +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector& chunkManagers, bool compile) { if (!vd->hasChanged() && entry.mRenderingNode) return; @@ -325,7 +333,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f for (QuadTreeWorld::ChunkManager* m : chunkManagers) { - osg::Node* n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint()); + osg::ref_ptr n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, far, vd->getViewPoint(), compile); if (n) pat->addChild(n); } entry.mRenderingNode = pat; @@ -411,6 +419,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { static ViewData sIntersectionViewData; vd = &sIntersectionViewData; + vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. } if (needsUpdate) @@ -430,7 +439,10 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->traverseTo(vd, 1, osg::Vec2f(x+0.5,y+0.5)); } else - mRootNode->traverseNodes(vd, cv->getViewPoint(), mLodCallback, mViewDistance); + { + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, mActiveGrid); + mRootNode->traverseNodes(vd, cv->getViewPoint(), &lodCallback, mViewDistance); + } } else { @@ -458,16 +470,13 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers, false); entry.mRenderingNode->accept(nv); } if (isCullVisitor) updateWaterCullingView(mHeightCullCallback, vd, static_cast(&nv), mStorage->getCellWorldSize(), !isGridEmpty()); - if (!isCullVisitor) - vd->clear(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. - vd->markUnchanged(); double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0; @@ -484,9 +493,7 @@ void QuadTreeWorld::ensureQuadTreeBuilt() if (mQuadTreeBuilt) return; - const float minSize = 1/8.f; - mLodCallback = new DefaultLodCallback(mLodFactor, minSize); - QuadTreeBuilder builder(mStorage, minSize); + QuadTreeBuilder builder(mStorage, MIN_SIZE); builder.build(); mRootNode = builder.getRootNode(); @@ -521,7 +528,7 @@ void QuadTreeWorld::cacheCell(View *view, int x, int y) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } } @@ -537,14 +544,15 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: ViewData* vd = static_cast(view); vd->setViewPoint(viewPoint); vd->setActiveGrid(grid); - mRootNode->traverseNodes(vd, viewPoint, mLodCallback, mViewDistance); + DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); + mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); - loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers); + loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 7634ea868f..2010548523 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -47,7 +47,7 @@ namespace Terrain { public: virtual ~ChunkManager(){} - virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint) = 0; + virtual osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool far, const osg::Vec3f& viewPoint, bool compile) = 0; virtual unsigned int getNodeMask() { return 0; } }; void addChunkManager(ChunkManager*); @@ -58,7 +58,6 @@ namespace Terrain osg::ref_ptr mRootNode; osg::ref_ptr mViewDataMap; - osg::ref_ptr mLodCallback; std::vector mChunkManagers; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 4398d1a143..5f99cd97eb 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -58,7 +58,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f()); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true); if (!node) return nullptr; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2653f2fc42..ec5e870168 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -109,8 +109,8 @@ max composite geometry size = 4.0 # Load far objects on terrain object paging = true -# Turn off to save memory but worse FPS -object paging merge geometry = true +# Affects the likelyhood of objects being merged. A higher value means merging is more likely and improves FPS at the cost of memory. +object paging merge factor = 1500 # Cull objects smaller than this size divided by distance object paging min size = 0.01