1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-31 15:06:41 +00:00

Stereo friendly StateSetUpdater

(cherry picked from commit 496b3aef88b8fd867dcdd23a6ca43144573b1b2f)
This commit is contained in:
madsbuvi 2021-01-24 10:34:33 +01:00
parent 49c828c138
commit da917fc9e7
2 changed files with 60 additions and 22 deletions

View file

@ -10,36 +10,58 @@ namespace SceneUtil
void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
bool isCullVisitor = nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR;
if (!mStateSets[0])
if (isCullVisitor)
return applyCull(node, static_cast<osgUtil::CullVisitor*>(nv));
else
return applyUpdate(node, nv);
}
void StateSetUpdater::applyUpdate(osg::Node* node, osg::NodeVisitor* nv)
{
if (!mStateSetsUpdate[0])
{
for (int i=0; i<2; ++i)
for (int i = 0; i < 2; ++i)
{
if (!isCullVisitor)
mStateSets[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults
else
mStateSets[i] = new osg::StateSet;
setDefaults(mStateSets[i]);
mStateSetsUpdate[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults
setDefaults(mStateSetsUpdate[i]);
}
}
osg::ref_ptr<osg::StateSet> stateset = mStateSets[nv->getTraversalNumber()%2];
osg::ref_ptr<osg::StateSet> stateset = nullptr;
stateset = mStateSetsUpdate[nv->getTraversalNumber() % 2];
apply(stateset, nv);
if (!isCullVisitor)
node->setStateSet(stateset);
else
static_cast<osgUtil::CullVisitor*>(nv)->pushStateSet(stateset);
node->setStateSet(stateset);
traverse(node, nv);
}
void StateSetUpdater::applyCull(osg::Node* node, osgUtil::CullVisitor* cv)
{
auto stateset = getCvDependentStateset(cv);
apply(stateset, cv);
cv->pushStateSet(stateset);
traverse(node, cv);
cv->popStateSet();
}
if (isCullVisitor)
static_cast<osgUtil::CullVisitor*>(nv)->popStateSet();
osg::StateSet* StateSetUpdater::getCvDependentStateset(osgUtil::CullVisitor* cv)
{
auto it = mStateSetsCull.find(cv);
if (it == mStateSetsCull.end())
{
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
mStateSetsCull.emplace(cv, stateset);
setDefaults(stateset);
return stateset;
}
return it->second;
}
void StateSetUpdater::reset()
{
mStateSets[0] = nullptr;
mStateSets[1] = nullptr;
mStateSetsUpdate[0] = nullptr;
mStateSetsUpdate[1] = nullptr;
mStateSetsCull.clear();
}
StateSetUpdater::StateSetUpdater()

View file

@ -3,6 +3,13 @@
#include <osg/NodeCallback>
#include <map>
namespace osgUtil
{
class CullVisitor;
}
namespace SceneUtil
{
@ -11,11 +18,15 @@ namespace SceneUtil
/// queues up a StateSet that we want to modify for the next frame. To solve this we could set the StateSet to
/// DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw
/// traversals run in parallel can yield up to 200% framerates.
/// @par Race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns,
/// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet,
/// otherwise it operates on a clone of the node's existing StateSet.
/// @par If set as an UpdateCallback, race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns,
/// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame.
/// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet, otherwise it operates on a clone of the node's existing StateSet.
/// @par If set as a CullCallback, race conditions are prevented by mapping statesets to cull visitors - OSG has two cull visitors that take turns,
/// allowing the updater to automatically scale for the number of views.
/// @note When used as a CullCallback, StateSetUpdater will have no effect on leaf nodes such as osg::Geometry and must be used on branch nodes only.
/// @note Do not add the same StateSetUpdater to multiple nodes.
/// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater.
/// @note Do not add multiple StateSetUpdaters on the same Node as they will conflict - instead use the CompositeStateSetUpdater.
class StateSetUpdater : public osg::NodeCallback
{
public:
@ -40,7 +51,12 @@ namespace SceneUtil
void reset();
private:
osg::ref_ptr<osg::StateSet> mStateSets[2];
void applyCull(osg::Node* node, osgUtil::CullVisitor* cv);
void applyUpdate(osg::Node* node, osg::NodeVisitor* nv);
osg::StateSet* getCvDependentStateset(osgUtil::CullVisitor* cv);
osg::ref_ptr<osg::StateSet> mStateSetsUpdate[2];
std::map<osgUtil::CullVisitor*, osg::ref_ptr<osg::StateSet>> mStateSetsCull;
};
/// @brief A variant of the StateSetController that can be made up of multiple controllers all controlling the same target.