1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-27 23:37:20 +00:00

Place Drawables directly in the scene graph when built with OSG 3.4

OSG 3.4 adds the ability to place Drawables directly in the scene graph, without a Geode decorating them. Leveraging this should give a small performance boost, because the redundant Geodes increase culling overhead.

There is still an oustanding issue with the RemoveDrawableVisitor no longer working correctly, because Drawables can have multiple parents.
This commit is contained in:
scrawl 2015-11-10 18:21:56 +01:00
parent 35459f20d5
commit f1ac408f35
7 changed files with 138 additions and 56 deletions

@ -11,6 +11,7 @@
#include <osg/Geode>
#include <osg/BlendFunc>
#include <osg/Material>
#include <osg/Version>
#include <osgParticle/ParticleSystem>
@ -196,10 +197,18 @@ namespace
traverse(node);
}
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3)
virtual void apply(osg::Drawable& drw)
{
mToRemove.push_back(&drw);
}
#endif
void remove()
{
for (std::vector<osg::Node*>::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)
{
// FIXME: a Drawable might have more than one parent
osg::Node* node = *it;
if (node->getNumParents())
node->getParent(0)->removeChild(node);
@ -219,6 +228,18 @@ namespace
}
virtual void apply(osg::Geode &node)
{
applyImpl(node);
}
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3)
virtual void apply(osg::Drawable& drw)
{
applyImpl(drw);
}
#endif
void applyImpl(osg::Node& node)
{
const std::string toFind = "tri bip";
if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0)
@ -232,6 +253,7 @@ namespace
{
for (std::vector<osg::Node*>::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)
{
// FIXME: a Drawable might have more than one parent
osg::Node* node = *it;
if (node->getNumParents())
node->getParent(0)->removeChild(node);

@ -6,6 +6,7 @@
#include <osg/Geode>
#include <osg/PositionAttitudeTransform>
#include <osg/UserDataContainer>
#include <osg/Version>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleProcessor>
@ -54,11 +55,19 @@ namespace
for (std::vector<osgParticle::ParticleSystem*>::iterator it = partsysVector.begin(); it != partsysVector.end(); ++it)
geode.removeDrawable(*it);
}
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3)
virtual void apply(osg::Drawable& drw)
{
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&drw))
mToRemove.push_back(partsys);
}
#endif
void remove()
{
for (std::vector<osg::ref_ptr<osg::Node> >::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)
{
// FIXME: a Drawable might have more than one parent
osg::Node* node = *it;
if (node->getNumParents())
node->getParent(0)->removeChild(node);

@ -351,33 +351,36 @@ public:
for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
{
osg::Drawable* drw = geode.getDrawable(i);
apply(*drw);
}
}
void apply(osg::Drawable& drw)
{
osg::Geometry* geom = drw.asGeometry();
if (!geom)
return;
osg::Geometry* geom = drw->asGeometry();
if (!geom)
continue;
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(geom->getVertexArray()->getNumElements());
for (unsigned int i=0; i<colors->size(); ++i)
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(geom->getVertexArray()->getNumElements());
for (unsigned int i=0; i<colors->size(); ++i)
{
float alpha = 1.f;
if (mMeshType == 0) alpha = i%2 ? 0.f : 1.f; // this is a cylinder, so every second vertex belongs to the bottom-most row
else if (mMeshType == 1)
{
float alpha = 1.f;
if (mMeshType == 0) alpha = i%2 ? 0.f : 1.f; // this is a cylinder, so every second vertex belongs to the bottom-most row
else if (mMeshType == 1)
{
if (i>= 49 && i <= 64) alpha = 0.f; // bottom-most row
else if (i>= 33 && i <= 48) alpha = 0.25098; // second row
else alpha = 1.f;
}
else if (mMeshType == 2)
{
osg::Vec4Array* origColors = static_cast<osg::Vec4Array*>(geom->getColorArray());
alpha = ((*origColors)[i].x() == 1.f) ? 1.f : 0.f;
}
(*colors)[i] = osg::Vec4f(0.f, 0.f, 0.f, alpha);
if (i>= 49 && i <= 64) alpha = 0.f; // bottom-most row
else if (i>= 33 && i <= 48) alpha = 0.25098; // second row
else alpha = 1.f;
}
else if (mMeshType == 2)
{
osg::Vec4Array* origColors = static_cast<osg::Vec4Array*>(geom->getColorArray());
alpha = ((*origColors)[i].x() == 1.f) ? 1.f : 0.f;
}
geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
(*colors)[i] = osg::Vec4f(0.f, 0.f, 0.f, alpha);
}
geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
}
private:

@ -5,6 +5,7 @@
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Array>
#include <osg/Version>
// resource
#include <components/misc/stringops.hpp>
@ -885,9 +886,6 @@ namespace NifOsg
// localToWorldMatrix for transforming to particle space
handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf);
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(partsys);
std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(nifNode, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);
@ -907,13 +905,21 @@ namespace NifOsg
updater->addParticleSystem(partsys);
parentNode->addChild(updater);
#if not(OSG_MIN_VERSION_REQUIRED(3,3,3))
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(partsys);
osg::Node* toAttach = geode.get();
#else
osg::Node* toAttach = partsys.get();
#endif
if (rf == osgParticle::ParticleProcessor::RELATIVE_RF)
parentNode->addChild(geode);
parentNode->addChild(toAttach);
else
{
osg::MatrixTransform* trans = new osg::MatrixTransform;
trans->setUpdateCallback(new InverseWorldMatrix);
trans->addChild(geode);
trans->addChild(toAttach);
parentNode->addChild(trans);
}
}
@ -957,8 +963,6 @@ namespace NifOsg
void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<int>& boundTextures, int animflags)
{
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
osg::ref_ptr<osg::Geometry> geometry;
for (Nif::ControllerPtr ctrl = triShape->controller; !ctrl.empty(); ctrl = ctrl->next)
{
@ -981,21 +985,36 @@ namespace NifOsg
triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags);
#if not(OSG_MIN_VERSION_REQUIRED(3,3,3))
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(geometry);
#endif
if (geometry->getDataVariance() == osg::Object::DYNAMIC)
{
// Add a copy, we will alternate between the two copies every other frame using the FrameSwitch
// This is so we can set the DataVariance as STATIC, giving a huge performance boost
geometry->setDataVariance(osg::Object::STATIC);
osg::ref_ptr<osg::Geode> geode2 = static_cast<osg::Geode*>(osg::clone(geode.get(), osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES));
osg::ref_ptr<FrameSwitch> frameswitch = new FrameSwitch;
#if not(OSG_MIN_VERSION_REQUIRED(3,3,3))
osg::ref_ptr<osg::Geode> geode2 = static_cast<osg::Geode*>(osg::clone(geode.get(), osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES));
frameswitch->addChild(geode);
frameswitch->addChild(geode2);
#else
osg::ref_ptr<osg::Geometry> geom2 = static_cast<osg::Geometry*>(osg::clone(geometry.get(), osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES));
frameswitch->addChild(geometry);
frameswitch->addChild(geom2);
#endif
parentNode->addChild(frameswitch);
}
else
#if not(OSG_MIN_VERSION_REQUIRED(3,3,3))
parentNode->addChild(geode);
#else
parentNode->addChild(geometry);
#endif
}
osg::ref_ptr<osg::Geometry> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher)
@ -1056,8 +1075,6 @@ namespace NifOsg
void handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite,
const std::vector<int>& boundTextures, int animflags)
{
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags);
@ -1090,17 +1107,27 @@ namespace NifOsg
}
rig->setInfluenceMap(map);
geode->addDrawable(rig);
// Add a copy, we will alternate between the two copies every other frame using the FrameSwitch
// This is so we can set the DataVariance as STATIC, giving a huge performance boost
rig->setDataVariance(osg::Object::STATIC);
osg::ref_ptr<FrameSwitch> frameswitch = new FrameSwitch;
#if not(OSG_MIN_VERSION_REQUIRED(3,3,3))
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(rig);
osg::Geode* geode2 = static_cast<osg::Geode*>(osg::clone(geode.get(), osg::CopyOp::DEEP_COPY_NODES|
osg::CopyOp::DEEP_COPY_DRAWABLES));
osg::ref_ptr<FrameSwitch> frameswitch = new FrameSwitch;
frameswitch->addChild(geode);
frameswitch->addChild(geode2);
#else
SceneUtil::RigGeometry* rig2 = static_cast<SceneUtil::RigGeometry*>(osg::clone(rig.get(), osg::CopyOp::DEEP_COPY_NODES|
osg::CopyOp::DEEP_COPY_DRAWABLES));
frameswitch->addChild(rig);
frameswitch->addChild(rig2);
#endif
parentNode->addChild(frameswitch);
}

@ -3,6 +3,7 @@
#include <osg/Node>
#include <osg/Geode>
#include <osg/UserDataContainer>
#include <osg/Version>
#include <osgParticle/ParticleSystem>
@ -32,31 +33,48 @@ namespace
{
}
void apply(osg::Node& node)
bool isWorldSpaceParticleSystem(osgParticle::ParticleSystem* partsys)
{
if (osg::Geode* geode = node.asGeode())
// HACK: ParticleSystem has no getReferenceFrame()
return (partsys->getUserDataContainer()
&& partsys->getUserDataContainer()->getNumDescriptions() > 0
&& partsys->getUserDataContainer()->getDescriptions()[0] == "worldspace");
}
void apply(osg::Geode& geode)
{
for (unsigned int i=0;i<geode.getNumDrawables();++i)
{
for (unsigned int i=0;i<geode->getNumDrawables();++i)
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(geode.getDrawable(i)))
{
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(geode->getDrawable(i)))
if (isWorldSpaceParticleSystem(partsys))
{
// HACK: ParticleSystem has no getReferenceFrame()
if (partsys->getUserDataContainer()
&& partsys->getUserDataContainer()->getNumDescriptions() > 0
&& partsys->getUserDataContainer()->getDescriptions()[0] == "worldspace")
{
// HACK: Ignore the InverseWorldMatrix transform the geode is attached to
if (geode->getNumParents() && geode->getParent(0)->getNumParents())
transformInitialParticles(partsys, geode->getParent(0)->getParent(0));
}
geode->setNodeMask(mMask);
// HACK: Ignore the InverseWorldMatrix transform the geode is attached to
if (geode.getNumParents() && geode.getParent(0)->getNumParents())
transformInitialParticles(partsys, geode.getParent(0)->getParent(0));
}
geode.setNodeMask(mMask);
}
}
traverse(node);
}
#if OSG_MIN_VERSION_REQUIRED(3,3,3)
// in OSG 3.3 and up Drawables can be directly in the scene graph without a Geode decorating them.
void apply(osg::Drawable& drw)
{
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&drw))
{
if (isWorldSpaceParticleSystem(partsys))
{
// HACK: Ignore the InverseWorldMatrix transform the particle system is attached to
if (partsys->getNumParents() && partsys->getParent(0)->getNumParents())
transformInitialParticles(partsys, partsys->getParent(0)->getParent(0));
}
partsys->setNodeMask(mMask);
}
}
#endif
void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node)
{
osg::MatrixList mats = node->getWorldMatrices();

@ -22,11 +22,13 @@ namespace SceneUtil
void DisableFreezeOnCullVisitor::apply(osg::Geode &geode)
{
for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
{
osg::Drawable* drw = geode.getDrawable(i);
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(drw))
partsys->setFreezeOnCull(false);
}
apply(*geode.getDrawable(i));
}
void DisableFreezeOnCullVisitor::apply(osg::Drawable& drw)
{
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(&drw))
partsys->setFreezeOnCull(false);
}
}

@ -35,6 +35,7 @@ namespace SceneUtil
}
virtual void apply(osg::Geode &geode);
virtual void apply(osg::Drawable& drw);
};
}