2019-06-13 13:37:00 +00:00
|
|
|
#include "objectpaging.hpp"
|
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
#include <unordered_map>
|
2022-07-26 21:53:06 +02:00
|
|
|
#include <vector>
|
2020-04-30 13:37:00 +00:00
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
#include <osg/LOD>
|
2020-05-05 13:37:00 +00:00
|
|
|
#include <osg/Material>
|
2019-06-13 13:37:00 +00:00
|
|
|
#include <osg/MatrixTransform>
|
2022-03-29 21:47:37 +02:00
|
|
|
#include <osg/Sequence>
|
2019-06-13 13:37:00 +00:00
|
|
|
#include <osg/Switch>
|
2023-01-05 01:34:26 +02:00
|
|
|
#include <osgAnimation/BasicAnimationManager>
|
2019-06-13 13:37:00 +00:00
|
|
|
#include <osgUtil/IncrementalCompileOperation>
|
|
|
|
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/esmreader.hpp>
|
2022-09-05 19:35:15 +02:00
|
|
|
#include <components/esm3/loadacti.hpp>
|
|
|
|
#include <components/esm3/loadcont.hpp>
|
|
|
|
#include <components/esm3/loaddoor.hpp>
|
|
|
|
#include <components/esm3/loadstat.hpp>
|
|
|
|
|
2022-06-01 22:53:18 +02:00
|
|
|
#include <components/esm3/readerscache.hpp>
|
2019-06-13 13:37:00 +00:00
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
|
|
|
#include <components/resource/scenemanager.hpp>
|
|
|
|
#include <components/sceneutil/optimizer.hpp>
|
2021-10-11 08:09:01 +00:00
|
|
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
2020-05-11 13:37:00 +00:00
|
|
|
#include <components/sceneutil/util.hpp>
|
|
|
|
#include <components/vfs/manager.hpp>
|
2019-06-13 13:37:00 +00:00
|
|
|
|
|
|
|
#include <osgParticle/ParticleProcessor>
|
|
|
|
#include <osgParticle/ParticleSystemUpdater>
|
|
|
|
|
2020-05-05 13:37:00 +00:00
|
|
|
#include <components/misc/rng.hpp>
|
2020-05-10 13:37:00 +00:00
|
|
|
#include <components/sceneutil/lightmanager.hpp>
|
2019-06-13 13:37:00 +00:00
|
|
|
#include <components/sceneutil/morphgeometry.hpp>
|
|
|
|
#include <components/sceneutil/riggeometry.hpp>
|
2022-06-21 15:27:34 +00:00
|
|
|
#include <components/sceneutil/riggeometryosgaextension.hpp>
|
2023-09-09 19:29:26 +02:00
|
|
|
#include <components/settings/values.hpp>
|
2019-06-13 13:37:00 +00:00
|
|
|
|
|
|
|
#include "apps/openmw/mwbase/environment.hpp"
|
|
|
|
#include "apps/openmw/mwbase/world.hpp"
|
|
|
|
#include "apps/openmw/mwworld/esmstore.hpp"
|
|
|
|
|
|
|
|
#include "vismask.hpp"
|
|
|
|
|
2022-06-29 00:32:11 +02:00
|
|
|
#include <condition_variable>
|
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
namespace MWRender
|
|
|
|
{
|
|
|
|
|
2020-05-11 13:37:00 +00:00
|
|
|
bool typeFilter(int type, bool far)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case ESM::REC_STAT:
|
2020-05-11 13:37:00 +00:00
|
|
|
case ESM::REC_ACTI:
|
2020-05-10 13:37:00 +00:00
|
|
|
case ESM::REC_DOOR:
|
2020-05-11 13:37:00 +00:00
|
|
|
return true;
|
2019-06-13 13:37:00 +00:00
|
|
|
case ESM::REC_CONT:
|
2020-05-11 13:37:00 +00:00
|
|
|
return !far;
|
2020-05-10 13:37:00 +00:00
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
std::string getModel(int type, const ESM::RefId& id, const MWWorld::ESMStore& store)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
|
|
|
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;
|
2020-05-03 13:37:00 +00:00
|
|
|
default:
|
2022-03-30 09:27:00 +02:00
|
|
|
return {};
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 13:37:00 +00:00
|
|
|
osg::ref_ptr<osg::Node> ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod,
|
|
|
|
unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2022-08-31 22:02:19 +02:00
|
|
|
lod = static_cast<unsigned char>(lodFlags >> (4 * 4));
|
2020-05-10 13:37:00 +00:00
|
|
|
if (activeGrid && !mActiveGrid)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
ChunkId id = std::make_tuple(center, size, activeGrid);
|
2019-06-13 13:37:00 +00:00
|
|
|
|
|
|
|
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);
|
|
|
|
if (obj)
|
2021-09-10 15:58:57 +00:00
|
|
|
return static_cast<osg::Node*>(obj.get());
|
2019-06-13 13:37:00 +00:00
|
|
|
else
|
|
|
|
{
|
2022-08-31 22:02:19 +02:00
|
|
|
osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile, lod);
|
2019-06-13 13:37:00 +00:00
|
|
|
mCache->addEntryToObjectCache(id, node.get());
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback
|
|
|
|
{
|
|
|
|
public:
|
2020-10-16 22:18:54 +04:00
|
|
|
bool isOperationPermissibleForObjectImplementation(
|
|
|
|
const SceneUtil::Optimizer* optimizer, const osg::Drawable* node, unsigned int option) const override
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2020-10-16 22:18:54 +04:00
|
|
|
bool isOperationPermissibleForObjectImplementation(
|
|
|
|
const SceneUtil::Optimizer* optimizer, const osg::Node* node, unsigned int option) const override
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2020-05-16 13:37:00 +00:00
|
|
|
return (node->getDataVariance() != osg::Object::DYNAMIC);
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-09-26 17:51:05 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
using LODRange = osg::LOD::MinMaxPair;
|
|
|
|
|
|
|
|
LODRange intersection(const LODRange& left, const LODRange& right)
|
|
|
|
{
|
|
|
|
return { std::max(left.first, right.first), std::min(left.second, right.second) };
|
|
|
|
}
|
|
|
|
|
|
|
|
bool empty(const LODRange& r)
|
|
|
|
{
|
|
|
|
return r.first >= r.second;
|
|
|
|
}
|
|
|
|
|
|
|
|
LODRange operator/(const LODRange& r, float div)
|
|
|
|
{
|
|
|
|
return { r.first / div, r.second / div };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
class CopyOp : public osg::CopyOp
|
|
|
|
{
|
|
|
|
public:
|
2020-05-16 13:37:00 +00:00
|
|
|
bool mOptimizeBillboards = true;
|
2022-09-26 17:51:05 +02:00
|
|
|
LODRange mDistances = { 0.f, 0.f };
|
2020-05-04 13:37:00 +00:00
|
|
|
osg::Vec3f mViewVector;
|
2021-05-09 14:10:35 +04:00
|
|
|
osg::Node::NodeMask mCopyMask = ~0u;
|
2020-05-04 13:37:00 +00:00
|
|
|
mutable std::vector<const osg::Node*> mNodePath;
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
void copy(const osg::Node* toCopy, osg::Group* attachTo)
|
|
|
|
{
|
|
|
|
const osg::Group* groupToCopy = toCopy->asGroup();
|
|
|
|
if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy)
|
|
|
|
attachTo->addChild(operator()(toCopy));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < groupToCopy->getNumChildren(); ++i)
|
|
|
|
attachTo->addChild(operator()(groupToCopy->getChild(i)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-16 22:18:54 +04:00
|
|
|
osg::Node* operator()(const osg::Node* node) const override
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2021-05-09 14:10:35 +04:00
|
|
|
if (!(node->getNodeMask() & mCopyMask))
|
|
|
|
return nullptr;
|
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
if (const osg::Drawable* d = node->asDrawable())
|
|
|
|
return operator()(d);
|
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
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))
|
|
|
|
{
|
2022-09-26 17:51:05 +02:00
|
|
|
std::vector<std::pair<osg::ref_ptr<osg::Node>, LODRange>> children;
|
2019-06-13 13:37:00 +00:00
|
|
|
for (unsigned int i = 0; i < lod->getNumChildren(); ++i)
|
2022-09-26 17:51:05 +02:00
|
|
|
if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r))
|
|
|
|
children.emplace_back(operator()(lod->getChild(i)), lod->getRangeList()[i]);
|
|
|
|
if (children.empty())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (children.size() == 1)
|
2022-11-09 21:57:17 +01:00
|
|
|
return children.front().first.release();
|
2022-09-26 17:51:05 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
osg::LOD* n = new osg::LOD;
|
|
|
|
for (const auto& [child, range] : children)
|
|
|
|
n->addChild(child, range.first, range.second);
|
|
|
|
n->setDataVariance(osg::Object::STATIC);
|
|
|
|
return n;
|
|
|
|
}
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
2022-03-29 21:47:37 +02:00
|
|
|
if (const osg::Sequence* sq = dynamic_cast<const osg::Sequence*>(node))
|
|
|
|
{
|
|
|
|
osg::Group* n = new osg::Group;
|
2022-03-31 21:55:14 +02:00
|
|
|
n->addChild(operator()(sq->getChild(sq->getValue() != -1 ? sq->getValue() : 0)));
|
2022-03-29 21:47:37 +02:00
|
|
|
n->setDataVariance(osg::Object::STATIC);
|
|
|
|
return n;
|
|
|
|
}
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-05-04 13:37:00 +00:00
|
|
|
mNodePath.push_back(node);
|
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
osg::Node* cloned = static_cast<osg::Node*>(node->clone(*this));
|
2020-05-04 13:37:00 +00:00
|
|
|
cloned->setDataVariance(osg::Object::STATIC);
|
|
|
|
cloned->setUserDataContainer(nullptr);
|
|
|
|
cloned->setName("");
|
|
|
|
|
|
|
|
mNodePath.pop_back();
|
|
|
|
|
|
|
|
handleCallbacks(node, cloned);
|
|
|
|
|
|
|
|
return cloned;
|
|
|
|
}
|
|
|
|
void handleCallbacks(const osg::Node* node, osg::Node* cloned) const
|
|
|
|
{
|
2020-05-16 13:37:00 +00:00
|
|
|
for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr;
|
|
|
|
callback = callback->getNestedCallback())
|
2020-05-04 13:37:00 +00:00
|
|
|
{
|
|
|
|
if (callback->className() == std::string("BillboardCallback"))
|
2020-05-12 13:37:00 +00:00
|
|
|
{
|
2020-05-16 13:37:00 +00:00
|
|
|
if (mOptimizeBillboards)
|
2020-05-12 13:37:00 +00:00
|
|
|
{
|
2020-05-16 13:37:00 +00:00
|
|
|
handleBillboard(cloned);
|
|
|
|
continue;
|
2020-05-12 13:37:00 +00:00
|
|
|
}
|
|
|
|
else
|
2020-05-16 13:37:00 +00:00
|
|
|
cloned->setDataVariance(osg::Object::DYNAMIC);
|
2020-05-12 13:37:00 +00:00
|
|
|
}
|
2020-05-16 13:37:00 +00:00
|
|
|
|
|
|
|
if (node->getCullCallback()->getNestedCallback())
|
|
|
|
{
|
|
|
|
osg::Callback* clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY);
|
|
|
|
clonedCallback->setNestedCallback(nullptr);
|
|
|
|
cloned->addCullCallback(clonedCallback);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cloned->addCullCallback(const_cast<osg::Callback*>(callback));
|
2020-05-04 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
void handleBillboard(osg::Node* node) const
|
|
|
|
{
|
|
|
|
osg::Transform* transform = node->asTransform();
|
|
|
|
if (!transform)
|
|
|
|
return;
|
|
|
|
osg::MatrixTransform* matrixTransform = transform->asMatrixTransform();
|
|
|
|
if (!matrixTransform)
|
|
|
|
return;
|
|
|
|
|
|
|
|
osg::Matrix worldToLocal = osg::Matrix::identity();
|
2020-06-26 09:49:26 +04:00
|
|
|
for (auto pathNode : mNodePath)
|
|
|
|
if (const osg::Transform* t = pathNode->asTransform())
|
2020-05-04 13:37:00 +00:00
|
|
|
t->computeWorldToLocalMatrix(worldToLocal, nullptr);
|
|
|
|
worldToLocal = osg::Matrix::orthoNormal(worldToLocal);
|
|
|
|
|
|
|
|
osg::Matrix billboardMatrix;
|
|
|
|
osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans());
|
|
|
|
viewVector.normalize();
|
|
|
|
osg::Vec3f right = viewVector ^ osg::Vec3f(0, 0, 1);
|
|
|
|
right.normalize();
|
|
|
|
osg::Vec3f up = right ^ viewVector;
|
|
|
|
up.normalize();
|
|
|
|
billboardMatrix.makeLookAt(osg::Vec3f(0, 0, 0), viewVector, up);
|
|
|
|
billboardMatrix.invert(billboardMatrix);
|
|
|
|
|
|
|
|
const osg::Matrix& oldMatrix = matrixTransform->getMatrix();
|
|
|
|
float mag[3]; // attempt to preserve scale
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
|
|
mag[i] = std::sqrt(oldMatrix(0, i) * oldMatrix(0, i) + oldMatrix(1, i) * oldMatrix(1, i)
|
|
|
|
+ oldMatrix(2, i) * oldMatrix(2, i));
|
|
|
|
osg::Matrix newMatrix;
|
|
|
|
worldToLocal.setTrans(0, 0, 0);
|
|
|
|
newMatrix *= worldToLocal;
|
|
|
|
newMatrix.preMult(billboardMatrix);
|
|
|
|
newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2]));
|
|
|
|
newMatrix.setTrans(oldMatrix.getTrans());
|
|
|
|
|
|
|
|
matrixTransform->setMatrix(newMatrix);
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
2020-10-16 22:18:54 +04:00
|
|
|
osg::Drawable* operator()(const osg::Drawable* drawable) const override
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2021-05-09 14:10:35 +04:00
|
|
|
if (!(drawable->getNodeMask() & mCopyMask))
|
|
|
|
return nullptr;
|
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
if (dynamic_cast<const osgParticle::ParticleSystem*>(drawable))
|
|
|
|
return nullptr;
|
|
|
|
|
2022-06-21 15:27:34 +00:00
|
|
|
if (dynamic_cast<const SceneUtil::OsgaRigGeometry*>(drawable))
|
|
|
|
return nullptr;
|
2019-06-13 13:37:00 +00:00
|
|
|
if (const SceneUtil::RigGeometry* rig = dynamic_cast<const SceneUtil::RigGeometry*>(drawable))
|
2020-04-30 13:37:00 +00:00
|
|
|
return operator()(rig->getSourceGeometry());
|
2019-06-13 13:37:00 +00:00
|
|
|
if (const SceneUtil::MorphGeometry* morph = dynamic_cast<const SceneUtil::MorphGeometry*>(drawable))
|
2020-04-30 13:37:00 +00:00
|
|
|
return operator()(morph->getSourceGeometry());
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
if (getCopyFlags() & DEEP_COPY_DRAWABLES)
|
|
|
|
{
|
2020-05-16 13:37:00 +00:00
|
|
|
osg::Drawable* d = static_cast<osg::Drawable*>(drawable->clone(*this));
|
2020-04-30 13:37:00 +00:00
|
|
|
d->setDataVariance(osg::Object::STATIC);
|
|
|
|
d->setUserDataContainer(nullptr);
|
|
|
|
d->setName("");
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
else
|
2020-05-16 13:37:00 +00:00
|
|
|
return const_cast<osg::Drawable*>(drawable);
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
2020-10-16 22:18:54 +04:00
|
|
|
osg::Callback* operator()(const osg::Callback* callback) const override { return nullptr; }
|
2019-06-13 13:37:00 +00:00
|
|
|
};
|
|
|
|
|
2020-05-10 13:37:00 +00:00
|
|
|
class RefnumSet : public osg::Object
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
RefnumSet() {}
|
|
|
|
RefnumSet(const RefnumSet& copy, const osg::CopyOp&)
|
|
|
|
: mRefnums(copy.mRefnums)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
META_Object(MWRender, RefnumSet)
|
2022-07-26 21:53:06 +02:00
|
|
|
std::vector<ESM::RefNum> mRefnums;
|
2020-05-10 13:37:00 +00:00
|
|
|
};
|
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
class AnalyzeVisitor : public osg::NodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
2021-05-09 14:10:35 +04:00
|
|
|
AnalyzeVisitor(osg::Node::NodeMask analyzeMask)
|
2020-04-30 13:37:00 +00:00
|
|
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
2021-05-09 14:10:35 +04:00
|
|
|
, mCurrentStateSet(nullptr)
|
2021-09-21 20:39:31 +00:00
|
|
|
{
|
|
|
|
setTraversalMask(analyzeMask);
|
|
|
|
}
|
2020-04-30 13:37:00 +00:00
|
|
|
|
|
|
|
typedef std::unordered_map<osg::StateSet*, unsigned int> StateSetCounter;
|
|
|
|
struct Result
|
|
|
|
{
|
|
|
|
StateSetCounter mStateSetCounter;
|
|
|
|
unsigned int mNumVerts = 0;
|
|
|
|
};
|
|
|
|
|
2020-10-16 22:18:54 +04:00
|
|
|
void apply(osg::Node& node) override
|
2020-04-30 13:37:00 +00:00
|
|
|
{
|
|
|
|
if (node.getStateSet())
|
|
|
|
mCurrentStateSet = node.getStateSet();
|
2021-05-09 15:43:13 +04:00
|
|
|
|
|
|
|
if (osg::Switch* sw = node.asSwitch())
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < sw->getNumChildren(); ++i)
|
|
|
|
if (sw->getValue(i))
|
|
|
|
traverse(*sw->getChild(i));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (osg::LOD* lod = dynamic_cast<osg::LOD*>(&node))
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < lod->getNumChildren(); ++i)
|
2022-09-26 17:51:05 +02:00
|
|
|
if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r))
|
2021-05-09 15:43:13 +04:00
|
|
|
traverse(*lod->getChild(i));
|
|
|
|
return;
|
|
|
|
}
|
2022-03-29 21:47:37 +02:00
|
|
|
if (osg::Sequence* sq = dynamic_cast<osg::Sequence*>(&node))
|
|
|
|
{
|
2022-03-31 21:55:14 +02:00
|
|
|
traverse(*sq->getChild(sq->getValue() != -1 ? sq->getValue() : 0));
|
2022-03-29 21:47:37 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-05-09 15:43:13 +04:00
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
traverse(node);
|
|
|
|
}
|
2020-10-16 22:18:54 +04:00
|
|
|
void apply(osg::Geometry& geom) override
|
2020-04-30 13:37:00 +00:00
|
|
|
{
|
2020-09-01 10:06:31 +04:00
|
|
|
if (osg::Array* array = geom.getVertexArray())
|
|
|
|
mResult.mNumVerts += array->getNumElements();
|
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
++mResult.mStateSetCounter[mCurrentStateSet];
|
|
|
|
++mGlobalStateSetCounter[mCurrentStateSet];
|
|
|
|
}
|
|
|
|
Result retrieveResult()
|
|
|
|
{
|
|
|
|
Result result = mResult;
|
|
|
|
mResult = Result();
|
|
|
|
mCurrentStateSet = nullptr;
|
|
|
|
return result;
|
|
|
|
}
|
2020-05-06 13:37:00 +00:00
|
|
|
void addInstance(const Result& result)
|
|
|
|
{
|
|
|
|
for (auto pair : result.mStateSetCounter)
|
|
|
|
mGlobalStateSetCounter[pair.first] += pair.second;
|
|
|
|
}
|
2020-04-30 13:37:00 +00:00
|
|
|
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;
|
2022-09-26 17:51:05 +02:00
|
|
|
LODRange mDistances = { 0.f, 0.f };
|
2020-04-30 13:37:00 +00:00
|
|
|
};
|
|
|
|
|
2020-05-05 13:37:00 +00:00
|
|
|
class DebugVisitor : public osg::NodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DebugVisitor()
|
|
|
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
|
|
|
{
|
|
|
|
}
|
2020-10-16 22:18:54 +04:00
|
|
|
void apply(osg::Drawable& node) override
|
2020-05-05 13:37:00 +00:00
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::Material> m(new osg::Material);
|
|
|
|
osg::Vec4f color(
|
|
|
|
Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f);
|
|
|
|
color.normalize();
|
|
|
|
m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f, 0.1f, 0.1f, 1.f));
|
|
|
|
m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f, 0.1f, 0.1f, 1.f));
|
|
|
|
m->setColorMode(osg::Material::OFF);
|
|
|
|
m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color));
|
|
|
|
osg::ref_ptr<osg::StateSet> stateset
|
|
|
|
= node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet;
|
|
|
|
stateset->setAttribute(m);
|
|
|
|
stateset->addUniform(new osg::Uniform("colorMode", 0));
|
2021-07-11 23:03:55 -07:00
|
|
|
stateset->addUniform(new osg::Uniform("emissiveMult", 1.f));
|
2021-11-10 19:58:06 +03:00
|
|
|
stateset->addUniform(new osg::Uniform("specStrength", 1.f));
|
2020-05-05 13:37:00 +00:00
|
|
|
node.setStateSet(stateset);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-05-11 13:37:00 +00:00
|
|
|
class AddRefnumMarkerVisitor : public osg::NodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
2023-09-15 19:38:09 +02:00
|
|
|
AddRefnumMarkerVisitor(ESM::RefNum refnum)
|
2020-05-11 13:37:00 +00:00
|
|
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
|
|
|
, mRefnum(refnum)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
ESM::RefNum mRefnum;
|
2020-10-16 22:18:54 +04:00
|
|
|
void apply(osg::Geometry& node) override
|
2020-05-11 13:37:00 +00:00
|
|
|
{
|
|
|
|
osg::ref_ptr<RefnumMarker> marker(new RefnumMarker);
|
|
|
|
marker->mRefnum = mRefnum;
|
|
|
|
if (osg::Array* array = node.getVertexArray())
|
|
|
|
marker->mNumVertices = array->getNumElements();
|
|
|
|
node.getOrCreateUserDataContainer()->addUserObject(marker);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-07-15 22:45:05 +03:00
|
|
|
ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager, ESM::RefId worldspace)
|
2023-09-09 19:29:26 +02:00
|
|
|
: GenericResourceManager<ChunkId>(nullptr, Settings::cells().mCacheExpiryDelay)
|
2023-07-15 22:45:05 +03:00
|
|
|
, Terrain::QuadTreeWorld::ChunkManager(worldspace)
|
2019-06-13 13:37:00 +00:00
|
|
|
, mSceneManager(sceneManager)
|
2020-05-12 13:37:00 +00:00
|
|
|
, mRefTrackerLocked(false)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2020-05-10 13:37:00 +00:00
|
|
|
mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain");
|
2021-09-08 19:59:53 +02:00
|
|
|
mDebugBatches = Settings::Manager::getBool("debug chunks", "Terrain");
|
2020-04-30 13:37:00 +00:00
|
|
|
mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain");
|
2019-06-13 13:37:00 +00:00
|
|
|
mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain");
|
2020-05-05 13:37:00 +00:00
|
|
|
mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain");
|
|
|
|
mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain");
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
2023-07-15 22:45:05 +03:00
|
|
|
std::map<ESM::RefNum, ESM::CellRef> ObjectPaging::collectESM3References(
|
|
|
|
float size, const osg::Vec2i& startCell, ESM::ReadersCache& readers) const
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2019-08-03 13:37:00 +00:00
|
|
|
std::map<ESM::RefNum, ESM::CellRef> refs;
|
2023-07-15 22:45:05 +03:00
|
|
|
const auto& store = MWBase::Environment::get().getWorld()->getStore();
|
2019-06-13 13:37:00 +00:00
|
|
|
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
|
|
|
|
{
|
2022-06-01 22:53:18 +02:00
|
|
|
const std::size_t index = static_cast<std::size_t>(cell->mContextList[i].index);
|
|
|
|
const ESM::ReadersCache::BusyItem reader = readers.get(index);
|
|
|
|
cell->restore(*reader, i);
|
2019-06-13 13:37:00 +00:00
|
|
|
ESM::CellRef ref;
|
2021-07-07 08:18:38 +10:00
|
|
|
ESM::MovedCellRef cMRef;
|
2019-06-13 13:37:00 +00:00
|
|
|
bool deleted = false;
|
2021-07-12 17:30:39 +02:00
|
|
|
bool moved = false;
|
2022-06-01 22:53:18 +02:00
|
|
|
while (ESM::Cell::getNextRef(
|
|
|
|
*reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2021-07-12 17:30:39 +02:00
|
|
|
if (moved)
|
|
|
|
continue;
|
2021-07-07 08:18:38 +10:00
|
|
|
|
2022-04-08 00:54:41 +02:00
|
|
|
if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum)
|
|
|
|
!= cell->mMovedRefs.end())
|
|
|
|
continue;
|
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
int type = store.findStatic(ref.mRefID);
|
2020-05-11 13:37:00 +00:00
|
|
|
if (!typeFilter(type, size >= 2))
|
|
|
|
continue;
|
2019-08-03 13:37:00 +00:00
|
|
|
if (deleted)
|
|
|
|
{
|
|
|
|
refs.erase(ref.mRefNum);
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-15 02:29:50 +02:00
|
|
|
refs[ref.mRefNum] = std::move(ref);
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-19 15:43:00 +04:00
|
|
|
catch (std::exception&)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2021-05-16 09:40:41 +02:00
|
|
|
for (auto [ref, deleted] : cell->mLeasedRefs)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2022-04-08 00:54:41 +02:00
|
|
|
if (deleted)
|
|
|
|
{
|
|
|
|
refs.erase(ref.mRefNum);
|
|
|
|
continue;
|
|
|
|
}
|
2020-05-16 13:37:00 +00:00
|
|
|
int type = store.findStatic(ref.mRefID);
|
2020-05-11 13:37:00 +00:00
|
|
|
if (!typeFilter(type, size >= 2))
|
|
|
|
continue;
|
2021-05-15 02:29:50 +02:00
|
|
|
refs[ref.mRefNum] = std::move(ref);
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-07-15 22:45:05 +03:00
|
|
|
return refs;
|
|
|
|
}
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Node> ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid,
|
|
|
|
const osg::Vec3f& viewPoint, bool compile, unsigned char lod)
|
|
|
|
{
|
|
|
|
osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size / 2.f), std::floor(center.y() - size / 2.f));
|
|
|
|
|
|
|
|
osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0) * getCellSize(mWorldspace);
|
|
|
|
osg::Vec3f relativeViewPoint = viewPoint - worldCenter;
|
|
|
|
|
|
|
|
std::map<ESM::RefNum, ESM::CellRef> refs;
|
|
|
|
ESM::ReadersCache readers;
|
|
|
|
const auto& world = MWBase::Environment::get().getWorld();
|
|
|
|
const auto& store = world->getStore();
|
|
|
|
|
|
|
|
if (mWorldspace == ESM::Cell::sDefaultWorldspaceId)
|
|
|
|
{
|
|
|
|
refs = collectESM3References(size, startCell, readers);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
if (activeGrid)
|
2019-08-03 13:37:00 +00:00
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
2020-05-16 13:37:00 +00:00
|
|
|
for (auto ref : getRefTracker().mBlacklist)
|
|
|
|
refs.erase(ref);
|
2019-08-03 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
osg::Vec2f minBound = (center - osg::Vec2f(size / 2.f, size / 2.f));
|
|
|
|
osg::Vec2f maxBound = (center + osg::Vec2f(size / 2.f, size / 2.f));
|
2020-04-30 13:37:00 +00:00
|
|
|
struct InstanceList
|
|
|
|
{
|
|
|
|
std::vector<const ESM::CellRef*> mInstances;
|
|
|
|
AnalyzeVisitor::Result mAnalyzeResult;
|
2020-05-05 13:37:00 +00:00
|
|
|
bool mNeedCompile = false;
|
2020-04-30 13:37:00 +00:00
|
|
|
};
|
|
|
|
typedef std::map<osg::ref_ptr<const osg::Node>, InstanceList> NodeMap;
|
|
|
|
NodeMap nodes;
|
2020-05-10 13:37:00 +00:00
|
|
|
osg::ref_ptr<RefnumSet> refnumSet = activeGrid ? new RefnumSet : nullptr;
|
2021-05-09 14:10:35 +04:00
|
|
|
|
|
|
|
// Mask_UpdateVisitor is used in such cases in NIF loader:
|
|
|
|
// 1. For collision nodes, which is not supposed to be rendered.
|
|
|
|
// 2. For nodes masked via Flag_Hidden (VisController can change this flag value at runtime).
|
|
|
|
// Since ObjectPaging does not handle VisController, we can just ignore both types of nodes.
|
|
|
|
constexpr auto copyMask = ~Mask_UpdateVisitor;
|
|
|
|
|
2023-07-15 22:45:05 +03:00
|
|
|
auto cellSize = getCellSize(mWorldspace);
|
|
|
|
const auto smallestDistanceToChunk = (size > 1 / 8.f) ? (size * cellSize) : 0.f;
|
2022-11-09 21:57:17 +01:00
|
|
|
const auto higherDistanceToChunk = [&] {
|
|
|
|
if (!activeGrid)
|
|
|
|
return smallestDistanceToChunk + 1;
|
2023-07-15 22:45:05 +03:00
|
|
|
return ((size < 1) ? 5 : 3) * cellSize * size + 1;
|
2022-11-09 21:57:17 +01:00
|
|
|
}();
|
2022-09-26 17:51:05 +02:00
|
|
|
|
2021-05-09 14:10:35 +04:00
|
|
|
AnalyzeVisitor analyzeVisitor(copyMask);
|
2020-05-05 13:37:00 +00:00
|
|
|
float minSize = mMinSize;
|
|
|
|
if (mMinSizeMergeFactor)
|
|
|
|
minSize *= mMinSizeMergeFactor;
|
2019-08-03 13:37:00 +00:00
|
|
|
for (const auto& pair : refs)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
2019-08-03 13:37:00 +00:00
|
|
|
const ESM::CellRef& ref = pair.second;
|
2019-06-13 13:37:00 +00:00
|
|
|
|
|
|
|
osg::Vec3f pos = ref.mPos.asVec3();
|
|
|
|
if (size < 1.f)
|
|
|
|
{
|
2023-07-15 22:45:05 +03:00
|
|
|
osg::Vec3f cellPos = pos / cellSize;
|
2020-05-16 13:37:00 +00:00
|
|
|
if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x())
|
|
|
|
|| (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())
|
2021-08-09 22:05:12 +02:00
|
|
|
|| (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x())
|
|
|
|
|| (maxBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y()))
|
2019-06-13 13:37:00 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-15 13:37:00 +00:00
|
|
|
float dSqr = (viewPoint - pos).length2();
|
2020-05-11 13:37:00 +00:00
|
|
|
if (!activeGrid)
|
2020-05-04 13:37:00 +00:00
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mSizeCacheMutex);
|
2020-05-04 13:37:00 +00:00
|
|
|
SizeCache::iterator found = mSizeCache.find(pair.first);
|
2020-05-16 13:37:00 +00:00
|
|
|
if (found != mSizeCache.end() && found->second < dSqr * minSize * minSize)
|
|
|
|
continue;
|
2020-05-04 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 19:49:20 +02:00
|
|
|
if (Misc::ResourceHelpers::isHiddenMarker(ref.mRefID))
|
2021-06-29 03:47:23 +02:00
|
|
|
continue;
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
int type = store.findStatic(ref.mRefID);
|
|
|
|
std::string model = getModel(type, ref.mRefID, store);
|
2020-04-30 13:37:00 +00:00
|
|
|
if (model.empty())
|
|
|
|
continue;
|
2022-06-29 00:32:11 +02:00
|
|
|
model = Misc::ResourceHelpers::correctMeshPath(model, mSceneManager->getVFS());
|
2020-05-11 13:37:00 +00:00
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
if (activeGrid && type != ESM::REC_STAT)
|
2020-05-11 13:37:00 +00:00
|
|
|
{
|
2020-04-30 13:37:00 +00:00
|
|
|
model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS());
|
2020-05-16 13:37:00 +00:00
|
|
|
std::string kfname = Misc::StringUtils::lowerCase(model);
|
2023-05-22 19:51:27 +02:00
|
|
|
if (kfname.size() > 4 && kfname.ends_with(".nif"))
|
2020-05-11 13:37:00 +00:00
|
|
|
{
|
2020-05-16 13:37:00 +00:00
|
|
|
kfname.replace(kfname.size() - 4, 4, ".kf");
|
|
|
|
if (mSceneManager->getVFS()->exists(kfname))
|
|
|
|
continue;
|
2020-05-11 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-31 22:02:19 +02:00
|
|
|
if (!activeGrid)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mLODNameCacheMutex);
|
|
|
|
LODNameCacheKey key{ model, lod };
|
|
|
|
LODNameCache::const_iterator found = mLODNameCache.lower_bound(key);
|
|
|
|
if (found != mLODNameCache.end() && found->first == key)
|
|
|
|
model = found->second;
|
|
|
|
else
|
2022-09-03 18:50:38 +02:00
|
|
|
model = mLODNameCache
|
|
|
|
.insert(found,
|
|
|
|
{ key,
|
|
|
|
Misc::ResourceHelpers::getLODMeshName(
|
|
|
|
world->getESMVersions()[ref.mRefNum.mContentFile], model,
|
|
|
|
mSceneManager->getVFS(), lod) })
|
|
|
|
->second;
|
2022-08-31 22:02:19 +02:00
|
|
|
}
|
|
|
|
|
2020-05-05 13:37:00 +00:00
|
|
|
osg::ref_ptr<const osg::Node> cnode = mSceneManager->getTemplate(model, false);
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-05-11 13:37:00 +00:00
|
|
|
if (activeGrid)
|
2020-05-11 13:37:00 +00:00
|
|
|
{
|
|
|
|
if (cnode->getNumChildrenRequiringUpdateTraversal() > 0
|
|
|
|
|| SceneUtil::hasUserDescription(cnode, Constants::NightDayLabel)
|
2023-01-04 16:07:47 +02:00
|
|
|
|| SceneUtil::hasUserDescription(cnode, Constants::HerbalismLabel)
|
2023-01-05 01:34:26 +02:00
|
|
|
|| (cnode->getName() == "Collada visual scene group"
|
|
|
|
&& dynamic_cast<const osgAnimation::BasicAnimationManager*>(cnode->getUpdateCallback())))
|
2020-05-11 13:37:00 +00:00
|
|
|
continue;
|
|
|
|
else
|
2022-07-26 21:53:06 +02:00
|
|
|
refnumSet->mRefnums.push_back(pair.first);
|
2020-05-11 13:37:00 +00:00
|
|
|
}
|
2020-05-11 13:37:00 +00:00
|
|
|
|
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
2020-05-12 13:37:00 +00:00
|
|
|
if (getRefTracker().mDisabled.count(pair.first))
|
2020-05-11 13:37:00 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-15 13:37:00 +00:00
|
|
|
float radius2 = cnode->getBound().radius2() * ref.mScale * ref.mScale;
|
2020-05-16 13:37:00 +00:00
|
|
|
if (radius2 < dSqr * minSize * minSize && !activeGrid)
|
2020-05-04 13:37:00 +00:00
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mSizeCacheMutex);
|
2020-05-16 13:37:00 +00:00
|
|
|
mSizeCache[pair.first] = radius2;
|
2019-06-13 13:37:00 +00:00
|
|
|
continue;
|
2020-05-04 13:37:00 +00:00
|
|
|
}
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
auto emplaced = nodes.emplace(cnode, InstanceList());
|
|
|
|
if (emplaced.second)
|
|
|
|
{
|
2022-09-26 17:51:05 +02:00
|
|
|
analyzeVisitor.mDistances = LODRange{ smallestDistanceToChunk, higherDistanceToChunk } / ref.mScale;
|
2020-05-03 13:37:00 +00:00
|
|
|
const_cast<osg::Node*>(cnode.get())
|
|
|
|
->accept(
|
|
|
|
analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor
|
2020-04-30 13:37:00 +00:00
|
|
|
emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult();
|
2020-05-05 13:37:00 +00:00
|
|
|
emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3;
|
2020-04-30 13:37:00 +00:00
|
|
|
}
|
2020-05-06 13:37:00 +00:00
|
|
|
else
|
|
|
|
analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult);
|
2020-04-30 13:37:00 +00:00
|
|
|
emplaced.first->second.mInstances.push_back(&ref);
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
osg::ref_ptr<osg::Group> group = new osg::Group;
|
|
|
|
osg::ref_ptr<osg::Group> mergeGroup = new osg::Group;
|
2021-01-13 13:33:46 +04:00
|
|
|
osg::ref_ptr<Resource::TemplateMultiRef> templateRefs = new Resource::TemplateMultiRef;
|
2020-05-05 13:37:00 +00:00
|
|
|
osgUtil::StateToCompile stateToCompile(0, nullptr);
|
2020-05-16 13:37:00 +00:00
|
|
|
CopyOp copyop;
|
2021-05-09 14:10:35 +04:00
|
|
|
copyop.mCopyMask = copyMask;
|
2020-04-30 13:37:00 +00:00
|
|
|
for (const auto& pair : nodes)
|
|
|
|
{
|
|
|
|
const osg::Node* cnode = pair.first;
|
|
|
|
|
|
|
|
const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult;
|
|
|
|
|
|
|
|
float mergeCost = analyzeResult.mNumVerts * size;
|
|
|
|
float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor;
|
|
|
|
bool merge = mergeBenefit > mergeCost;
|
|
|
|
|
2020-05-05 13:37:00 +00:00
|
|
|
float minSizeMerged = mMinSize;
|
|
|
|
float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1;
|
|
|
|
float minSizeMergeFactor2 = (1 - factor2) * mMinSizeMergeFactor + factor2;
|
|
|
|
if (minSizeMergeFactor2 > 0)
|
|
|
|
minSizeMerged *= minSizeMergeFactor2;
|
2020-05-05 13:37:00 +00:00
|
|
|
|
2020-05-05 13:37:00 +00:00
|
|
|
unsigned int numinstances = 0;
|
2020-04-30 13:37:00 +00:00
|
|
|
for (auto cref : pair.second.mInstances)
|
|
|
|
{
|
|
|
|
const ESM::CellRef& ref = *cref;
|
|
|
|
osg::Vec3f pos = ref.mPos.asVec3();
|
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
if (!activeGrid && minSizeMerged != minSize
|
|
|
|
&& cnode->getBound().radius2() * cref->mScale * cref->mScale
|
|
|
|
< (viewPoint - pos).length2() * minSizeMerged * minSizeMerged)
|
2020-05-15 13:37:00 +00:00
|
|
|
continue;
|
2020-05-05 13:37:00 +00:00
|
|
|
|
2021-10-11 08:09:01 +00:00
|
|
|
osg::Vec3f nodePos = pos - worldCenter;
|
|
|
|
osg::Quat nodeAttitude = osg::Quat(ref.mPos.rot[2], osg::Vec3f(0, 0, -1))
|
2020-04-30 13:37:00 +00:00
|
|
|
* osg::Quat(ref.mPos.rot[1], osg::Vec3f(0, -1, 0))
|
2021-10-11 08:09:01 +00:00
|
|
|
* osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1, 0, 0));
|
|
|
|
osg::Vec3f nodeScale = osg::Vec3f(ref.mScale, ref.mScale, ref.mScale);
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Group> trans;
|
|
|
|
if (merge)
|
|
|
|
{
|
|
|
|
// Optimizer currently supports only MatrixTransforms.
|
|
|
|
osg::Matrixf matrix;
|
|
|
|
matrix.preMultTranslate(nodePos);
|
|
|
|
matrix.preMultRotate(nodeAttitude);
|
|
|
|
matrix.preMultScale(nodeScale);
|
|
|
|
trans = new osg::MatrixTransform(matrix);
|
|
|
|
trans->setDataVariance(osg::Object::STATIC);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
trans = new SceneUtil::PositionAttitudeTransform;
|
|
|
|
SceneUtil::PositionAttitudeTransform* pat
|
|
|
|
= static_cast<SceneUtil::PositionAttitudeTransform*>(trans.get());
|
|
|
|
pat->setPosition(nodePos);
|
|
|
|
pat->setScale(nodeScale);
|
|
|
|
pat->setAttitude(nodeAttitude);
|
|
|
|
}
|
2020-04-30 13:37:00 +00:00
|
|
|
|
2021-10-23 08:31:46 +00:00
|
|
|
// DO NOT COPY AND PASTE THIS CODE. Cloning osg::Geometry without also cloning its contained Arrays is
|
|
|
|
// generally unsafe. In this specific case the operation is safe under the following two assumptions:
|
|
|
|
// - When Arrays are removed or replaced in the cloned geometry, the original Arrays in their place must
|
|
|
|
// outlive the cloned geometry regardless. (ensured by TemplateMultiRef)
|
|
|
|
// - Arrays that we add or replace in the cloned geometry must be explicitely forbidden from reusing
|
|
|
|
// BufferObjects of the original geometry. (ensured by needvbo() in optimizer.cpp)
|
2020-05-16 13:37:00 +00:00
|
|
|
copyop.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES
|
|
|
|
: osg::CopyOp::DEEP_COPY_NODES);
|
|
|
|
copyop.mOptimizeBillboards = (size > 1 / 4.f);
|
|
|
|
copyop.mNodePath.push_back(trans);
|
2022-09-26 17:51:05 +02:00
|
|
|
copyop.mDistances = LODRange{ smallestDistanceToChunk, higherDistanceToChunk } / ref.mScale;
|
2020-05-16 13:37:00 +00:00
|
|
|
copyop.mViewVector = (viewPoint - worldCenter);
|
|
|
|
copyop.copy(cnode, trans);
|
2020-06-18 16:16:16 +02:00
|
|
|
copyop.mNodePath.pop_back();
|
2020-05-04 13:37:00 +00:00
|
|
|
|
2020-05-11 13:37:00 +00:00
|
|
|
if (activeGrid)
|
|
|
|
{
|
|
|
|
if (merge)
|
|
|
|
{
|
|
|
|
AddRefnumMarkerVisitor visitor(ref.mRefNum);
|
2020-05-16 13:37:00 +00:00
|
|
|
trans->accept(visitor);
|
2020-05-11 13:37:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
osg::ref_ptr<RefnumMarker> marker = new RefnumMarker;
|
|
|
|
marker->mRefnum = ref.mRefNum;
|
2020-05-16 13:37:00 +00:00
|
|
|
trans->getOrCreateUserDataContainer()->addUserObject(marker);
|
2020-05-11 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-16 13:37:00 +00:00
|
|
|
osg::Group* attachTo = merge ? mergeGroup : group;
|
|
|
|
attachTo->addChild(trans);
|
2020-05-05 13:37:00 +00:00
|
|
|
++numinstances;
|
|
|
|
}
|
|
|
|
if (numinstances > 0)
|
|
|
|
{
|
2021-10-23 08:31:46 +00:00
|
|
|
// add a ref to the original template to help verify the safety of shallow cloning operations
|
|
|
|
// in addition, we hint to the cache that it's still being used and should be kept in cache
|
2021-01-13 13:33:46 +04:00
|
|
|
templateRefs->addRef(cnode);
|
2020-05-05 13:37:00 +00:00
|
|
|
|
|
|
|
if (pair.second.mNeedCompile)
|
|
|
|
{
|
|
|
|
int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES;
|
|
|
|
if (!merge)
|
|
|
|
mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS;
|
|
|
|
stateToCompile._mode = mode;
|
|
|
|
const_cast<osg::Node*>(cnode)->accept(stateToCompile);
|
|
|
|
}
|
2020-04-30 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mergeGroup->getNumChildren())
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
|
|
|
SceneUtil::Optimizer optimizer;
|
2020-05-16 13:37:00 +00:00
|
|
|
if (size > 1 / 8.f)
|
2019-06-13 13:37:00 +00:00
|
|
|
{
|
|
|
|
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;
|
2021-09-27 18:41:24 +00:00
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
optimizer.optimize(mergeGroup, options);
|
|
|
|
|
|
|
|
group->addChild(mergeGroup);
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-05-05 13:37:00 +00:00
|
|
|
if (mDebugBatches)
|
|
|
|
{
|
|
|
|
DebugVisitor dv;
|
|
|
|
mergeGroup->accept(dv);
|
|
|
|
}
|
2020-05-12 13:37:00 +00:00
|
|
|
if (compile)
|
|
|
|
{
|
|
|
|
stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS;
|
|
|
|
mergeGroup->accept(stateToCompile);
|
|
|
|
}
|
2020-04-30 13:37:00 +00:00
|
|
|
}
|
2019-06-13 13:37:00 +00:00
|
|
|
|
2020-05-05 13:37:00 +00:00
|
|
|
auto ico = mSceneManager->getIncrementalCompileOperation();
|
|
|
|
if (!stateToCompile.empty() && ico)
|
|
|
|
{
|
|
|
|
auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group);
|
|
|
|
compileSet->buildCompileMap(ico->getContextSet(), stateToCompile);
|
|
|
|
ico->add(compileSet, false);
|
|
|
|
}
|
|
|
|
|
2020-04-30 13:37:00 +00:00
|
|
|
group->getBound();
|
2019-06-13 13:37:00 +00:00
|
|
|
group->setNodeMask(Mask_Static);
|
2020-05-10 13:37:00 +00:00
|
|
|
osg::UserDataContainer* udc = group->getOrCreateUserDataContainer();
|
|
|
|
if (activeGrid)
|
|
|
|
{
|
2022-07-26 21:53:06 +02:00
|
|
|
std::sort(refnumSet->mRefnums.begin(), refnumSet->mRefnums.end());
|
|
|
|
refnumSet->mRefnums.erase(
|
|
|
|
std::unique(refnumSet->mRefnums.begin(), refnumSet->mRefnums.end()), refnumSet->mRefnums.end());
|
2020-05-10 13:37:00 +00:00
|
|
|
udc->addUserObject(refnumSet);
|
|
|
|
group->addCullCallback(new SceneUtil::LightListCallback);
|
|
|
|
}
|
|
|
|
udc->addUserObject(templateRefs);
|
2020-04-30 13:37:00 +00:00
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int ObjectPaging::getNodeMask()
|
|
|
|
{
|
|
|
|
return Mask_Static;
|
|
|
|
}
|
|
|
|
|
2020-05-08 13:37:00 +00:00
|
|
|
struct ClearCacheFunctor
|
2019-08-03 13:37:00 +00:00
|
|
|
{
|
2020-05-08 13:37:00 +00:00
|
|
|
void operator()(MWRender::ChunkId id, osg::Object* obj)
|
|
|
|
{
|
|
|
|
if (intersects(id, mPosition))
|
|
|
|
mToClear.insert(id);
|
|
|
|
}
|
|
|
|
bool intersects(ChunkId id, osg::Vec3f pos)
|
|
|
|
{
|
2020-05-11 13:37:00 +00:00
|
|
|
if (mActiveGridOnly && !std::get<2>(id))
|
|
|
|
return false;
|
2023-07-15 22:45:05 +03:00
|
|
|
pos /= getCellSize(mWorldspace);
|
2020-07-09 23:17:01 +02:00
|
|
|
clampToCell(pos);
|
2020-05-08 13:37:00 +00:00
|
|
|
osg::Vec2f center = std::get<0>(id);
|
|
|
|
float halfSize = std::get<1>(id) / 2;
|
|
|
|
return pos.x() >= center.x() - halfSize && pos.y() >= center.y() - halfSize
|
|
|
|
&& pos.x() <= center.x() + halfSize && pos.y() <= center.y() + halfSize;
|
|
|
|
}
|
2020-07-09 23:17:01 +02:00
|
|
|
void clampToCell(osg::Vec3f& cellPos)
|
|
|
|
{
|
2021-11-06 07:30:28 +03:00
|
|
|
cellPos.x() = std::clamp<float>(cellPos.x(), mCell.x(), mCell.x() + 1);
|
|
|
|
cellPos.y() = std::clamp<float>(cellPos.y(), mCell.y(), mCell.y() + 1);
|
2020-07-09 23:17:01 +02:00
|
|
|
}
|
2020-05-08 13:37:00 +00:00
|
|
|
osg::Vec3f mPosition;
|
2020-07-09 23:17:01 +02:00
|
|
|
osg::Vec2i mCell;
|
2023-07-15 22:45:05 +03:00
|
|
|
ESM::RefId mWorldspace;
|
2020-05-08 13:37:00 +00:00
|
|
|
std::set<MWRender::ChunkId> mToClear;
|
2020-05-11 13:37:00 +00:00
|
|
|
bool mActiveGridOnly = false;
|
2020-05-08 13:37:00 +00:00
|
|
|
};
|
|
|
|
|
2020-07-09 23:17:01 +02:00
|
|
|
bool ObjectPaging::enableObject(
|
2023-09-15 19:38:09 +02:00
|
|
|
int type, ESM::RefNum refnum, const osg::Vec3f& pos, const osg::Vec2i& cell, bool enabled)
|
2020-05-08 13:37:00 +00:00
|
|
|
{
|
2020-05-11 13:37:00 +00:00
|
|
|
if (!typeFilter(type, false))
|
2020-05-08 13:37:00 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
2020-05-12 13:37:00 +00:00
|
|
|
if (enabled && !getWritableRefTracker().mDisabled.erase(refnum))
|
|
|
|
return false;
|
|
|
|
if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second)
|
|
|
|
return false;
|
|
|
|
if (mRefTrackerLocked)
|
|
|
|
return false;
|
2020-05-08 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClearCacheFunctor ccf;
|
|
|
|
ccf.mPosition = pos;
|
2020-07-09 23:17:01 +02:00
|
|
|
ccf.mCell = cell;
|
2023-07-15 22:45:05 +03:00
|
|
|
ccf.mWorldspace = mWorldspace;
|
2020-05-08 13:37:00 +00:00
|
|
|
mCache->call(ccf);
|
2020-05-12 13:37:00 +00:00
|
|
|
if (ccf.mToClear.empty())
|
|
|
|
return false;
|
2021-05-15 19:50:01 +02:00
|
|
|
for (const auto& chunk : ccf.mToClear)
|
2020-05-08 13:37:00 +00:00
|
|
|
mCache->removeFromObjectCache(chunk);
|
|
|
|
return true;
|
2019-08-03 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
2023-09-15 19:38:09 +02:00
|
|
|
bool ObjectPaging::blacklistObject(int type, ESM::RefNum refnum, const osg::Vec3f& pos, const osg::Vec2i& cell)
|
2020-05-11 13:37:00 +00:00
|
|
|
{
|
2020-05-11 13:37:00 +00:00
|
|
|
if (!typeFilter(type, false))
|
2020-05-11 13:37:00 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
2020-05-12 13:37:00 +00:00
|
|
|
if (!getWritableRefTracker().mBlacklist.insert(refnum).second)
|
|
|
|
return false;
|
|
|
|
if (mRefTrackerLocked)
|
|
|
|
return false;
|
2020-05-11 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClearCacheFunctor ccf;
|
|
|
|
ccf.mPosition = pos;
|
2020-07-09 23:17:01 +02:00
|
|
|
ccf.mCell = cell;
|
2020-05-11 13:37:00 +00:00
|
|
|
ccf.mActiveGridOnly = true;
|
2023-07-15 22:45:05 +03:00
|
|
|
ccf.mWorldspace = mWorldspace;
|
2020-05-11 13:37:00 +00:00
|
|
|
mCache->call(ccf);
|
|
|
|
if (ccf.mToClear.empty())
|
|
|
|
return false;
|
2021-05-15 19:50:01 +02:00
|
|
|
for (const auto& chunk : ccf.mToClear)
|
2020-05-11 13:37:00 +00:00
|
|
|
mCache->removeFromObjectCache(chunk);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-03 13:37:00 +00:00
|
|
|
void ObjectPaging::clear()
|
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
2020-05-12 13:37:00 +00:00
|
|
|
mRefTrackerNew.mDisabled.clear();
|
|
|
|
mRefTrackerNew.mBlacklist.clear();
|
|
|
|
mRefTrackerLocked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ObjectPaging::unlockCache()
|
|
|
|
{
|
|
|
|
if (!mRefTrackerLocked)
|
|
|
|
return false;
|
2020-05-08 13:37:00 +00:00
|
|
|
{
|
2020-06-25 21:46:07 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
|
2020-05-12 13:37:00 +00:00
|
|
|
mRefTrackerLocked = false;
|
|
|
|
if (mRefTracker == mRefTrackerNew)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
mRefTracker = mRefTrackerNew;
|
2020-05-08 13:37:00 +00:00
|
|
|
}
|
|
|
|
mCache->clear();
|
2020-05-12 13:37:00 +00:00
|
|
|
return true;
|
2019-08-03 13:37:00 +00:00
|
|
|
}
|
2020-05-04 13:37:00 +00:00
|
|
|
|
2020-05-10 13:37:00 +00:00
|
|
|
struct GetRefnumsFunctor
|
|
|
|
{
|
2022-07-26 21:53:06 +02:00
|
|
|
GetRefnumsFunctor(std::vector<ESM::RefNum>& output)
|
|
|
|
: mOutput(output)
|
|
|
|
{
|
|
|
|
}
|
2020-05-10 13:37:00 +00:00
|
|
|
void operator()(MWRender::ChunkId chunkId, osg::Object* obj)
|
|
|
|
{
|
|
|
|
if (!std::get<2>(chunkId))
|
|
|
|
return;
|
|
|
|
const osg::Vec2f& center = std::get<0>(chunkId);
|
|
|
|
bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y()
|
|
|
|
|| center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w());
|
|
|
|
if (!activeGrid)
|
|
|
|
return;
|
|
|
|
|
|
|
|
osg::UserDataContainer* udc = obj->getUserDataContainer();
|
|
|
|
if (udc && udc->getNumUserObjects())
|
|
|
|
{
|
|
|
|
RefnumSet* refnums = dynamic_cast<RefnumSet*>(udc->getUserObject(0));
|
|
|
|
if (!refnums)
|
|
|
|
return;
|
2022-07-26 21:53:06 +02:00
|
|
|
mOutput.insert(mOutput.end(), refnums->mRefnums.begin(), refnums->mRefnums.end());
|
2020-05-10 13:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
osg::Vec4i mActiveGrid;
|
2022-07-26 21:53:06 +02:00
|
|
|
std::vector<ESM::RefNum>& mOutput;
|
2020-05-10 13:37:00 +00:00
|
|
|
};
|
|
|
|
|
2022-07-26 21:53:06 +02:00
|
|
|
void ObjectPaging::getPagedRefnums(const osg::Vec4i& activeGrid, std::vector<ESM::RefNum>& out)
|
2020-05-10 13:37:00 +00:00
|
|
|
{
|
|
|
|
GetRefnumsFunctor grf(out);
|
|
|
|
grf.mActiveGrid = activeGrid;
|
|
|
|
mCache->call(grf);
|
2022-07-26 21:53:06 +02:00
|
|
|
std::sort(out.begin(), out.end());
|
|
|
|
out.erase(std::unique(out.begin(), out.end()), out.end());
|
2020-05-10 13:37:00 +00:00
|
|
|
}
|
|
|
|
|
2020-05-04 13:37:00 +00:00
|
|
|
void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats* stats) const
|
|
|
|
{
|
|
|
|
stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize());
|
|
|
|
}
|
|
|
|
|
2019-06-13 13:37:00 +00:00
|
|
|
}
|