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:
parent
49c828c138
commit
da917fc9e7
2 changed files with 60 additions and 22 deletions
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue