Port nifloader to the more efficient StateSetController

c++11
scrawl 10 years ago
parent 5dd1ab24fe
commit de2c85e0f8

@ -278,7 +278,7 @@ UVController::UVController(const Nif::NiUVData *data, std::set<int> textureUnits
} }
UVController::UVController(const UVController& copy, const osg::CopyOp& copyop) UVController::UVController(const UVController& copy, const osg::CopyOp& copyop)
: osg::Object(copy, copyop), osg::NodeCallback(copy, copyop), Controller(copy) : osg::Object(copy, copyop), StateSetController(copy, copyop), Controller(copy)
, mUTrans(copy.mUTrans) , mUTrans(copy.mUTrans)
, mVTrans(copy.mVTrans) , mVTrans(copy.mVTrans)
, mUScale(copy.mUScale) , mUScale(copy.mUScale)
@ -287,11 +287,17 @@ UVController::UVController(const UVController& copy, const osg::CopyOp& copyop)
{ {
} }
void UVController::operator()(osg::Node* node, osg::NodeVisitor* nv) void UVController::setDefaults(osg::StateSet *stateset)
{
osg::TexMat* texMat = new osg::TexMat;
for (std::set<int>::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it)
stateset->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON);
}
void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
{ {
if (hasInput()) if (hasInput())
{ {
osg::StateSet* stateset = getWritableStateSet(node);
float value = getInputValue(nv); float value = getInputValue(nv);
float uTrans = interpKey(mUTrans->mKeys, value, 0.0f); float uTrans = interpKey(mUTrans->mKeys, value, 0.0f);
float vTrans = interpKey(mVTrans->mKeys, value, 0.0f); float vTrans = interpKey(mVTrans->mKeys, value, 0.0f);
@ -301,15 +307,13 @@ void UVController::operator()(osg::Node* node, osg::NodeVisitor* nv)
osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1); osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
mat.setTrans(uTrans, vTrans, 0); mat.setTrans(uTrans, vTrans, 0);
osg::TexMat* texMat = new osg::TexMat; // setting once is enough because all other texture units share the same TexMat (see setDefaults).
texMat->setMatrix(mat); if (mTextureUnits.size())
for (std::set<int>::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it)
{ {
stateset->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON); osg::TexMat* texMat = static_cast<osg::TexMat*>(stateset->getTextureAttribute(*mTextureUnits.begin(), osg::StateAttribute::TEXMAT));
texMat->setMatrix(mat);
} }
} }
traverse(node, nv);
} }
VisController::VisController(const Nif::NiVisData *data) VisController::VisController(const Nif::NiVisData *data)
@ -363,26 +367,21 @@ AlphaController::AlphaController()
} }
AlphaController::AlphaController(const AlphaController &copy, const osg::CopyOp &copyop) AlphaController::AlphaController(const AlphaController &copy, const osg::CopyOp &copyop)
: osg::NodeCallback(copy, copyop), ValueInterpolator(), Controller(copy) : StateSetController(copy, copyop), ValueInterpolator(), Controller(copy)
, mData(copy.mData) , mData(copy.mData)
{ {
} }
void AlphaController::operator () (osg::Node* node, osg::NodeVisitor* nv) void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
{ {
if (hasInput()) if (hasInput())
{ {
osg::StateSet* stateset = getWritableStateSet(node);
float value = interpKey(mData->mKeys, getInputValue(nv)); float value = interpKey(mData->mKeys, getInputValue(nv));
osg::Material* mat = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
if (mat) osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
{ diffuse.a() = value;
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
diffuse.a() = value;
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
}
} }
traverse(node, nv);
} }
MaterialColorController::MaterialColorController(const Nif::NiPosData *data) MaterialColorController::MaterialColorController(const Nif::NiPosData *data)
@ -395,26 +394,21 @@ MaterialColorController::MaterialColorController()
} }
MaterialColorController::MaterialColorController(const MaterialColorController &copy, const osg::CopyOp &copyop) MaterialColorController::MaterialColorController(const MaterialColorController &copy, const osg::CopyOp &copyop)
: osg::NodeCallback(copy, copyop), Controller(copy) : StateSetController(copy, copyop), Controller(copy)
, mData(copy.mData) , mData(copy.mData)
{ {
} }
void MaterialColorController::operator() (osg::Node* node, osg::NodeVisitor* nv) void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
{ {
if (hasInput()) if (hasInput())
{ {
osg::StateSet* stateset = getWritableStateSet(node);
osg::Vec3f value = interpKey(mData->mKeys, getInputValue(nv)); osg::Vec3f value = interpKey(mData->mKeys, getInputValue(nv));
osg::Material* mat = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
if (mat) osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
{ diffuse.set(value.x(), value.y(), value.z(), diffuse.a());
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
diffuse.set(value.x(), value.y(), value.z(), diffuse.a());
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
}
} }
traverse(node, nv);
} }
FlipController::FlipController(const Nif::NiFlipController *ctrl, std::vector<osg::ref_ptr<osg::Texture2D> > textures) FlipController::FlipController(const Nif::NiFlipController *ctrl, std::vector<osg::ref_ptr<osg::Texture2D> > textures)
@ -429,7 +423,7 @@ FlipController::FlipController()
} }
FlipController::FlipController(const FlipController &copy, const osg::CopyOp &copyop) FlipController::FlipController(const FlipController &copy, const osg::CopyOp &copyop)
: osg::NodeCallback(copy, copyop) : StateSetController(copy, copyop)
, Controller(copy) , Controller(copy)
, mTexSlot(copy.mTexSlot) , mTexSlot(copy.mTexSlot)
, mDelta(copy.mDelta) , mDelta(copy.mDelta)
@ -437,15 +431,13 @@ FlipController::FlipController(const FlipController &copy, const osg::CopyOp &co
{ {
} }
void FlipController::operator() (osg::Node* node, osg::NodeVisitor* nv) void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
{ {
if (hasInput() && mDelta != 0) if (hasInput() && mDelta != 0)
{ {
osg::StateSet* stateset = getWritableStateSet(node);
int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size();
stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]);
} }
traverse(node, nv);
} }
ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController *ctrl) ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController *ctrl)
@ -507,12 +499,4 @@ void SourcedKeyframeController::operator ()(osg::Node* node, osg::NodeVisitor* n
traverse(node, nv); traverse(node, nv);
} }
osg::StateSet *StateSetController::getWritableStateSet(osg::Node *node)
{
osg::StateSet* orig = node->getOrCreateStateSet();
osg::StateSet* cloned = new osg::StateSet(*orig, osg::CopyOp::SHALLOW_COPY);
node->setStateSet(cloned);
return cloned;
}
} }

@ -8,6 +8,8 @@
#include <components/nifcache/nifcache.hpp> #include <components/nifcache/nifcache.hpp>
#include <components/sceneutil/statesetcontroller.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <set> //UVController #include <set> //UVController
@ -189,19 +191,7 @@ namespace NifOsg
bool mEnabled; bool mEnabled;
}; };
class StateSetController class UVController : public SceneUtil::StateSetController, public Controller, public ValueInterpolator
{
protected:
// Clones a StateSet to make it "writable". This is to prevent race conditions when the OSG draw thread of the last frame
// queues up a StateSet that we want to modify. Note, we could also 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.
// If the StateSet allocations per frame are proving too much of an overhead we could "reuse" StateSets from previous frames,
// kind of like a double buffering scheme.
osg::StateSet* getWritableStateSet(osg::Node* node);
};
// Note we're using NodeCallback instead of StateSet::Callback because the StateSet callback doesn't support nesting
class UVController : public osg::NodeCallback, public Controller, public StateSetController, public ValueInterpolator
{ {
public: public:
UVController(); UVController();
@ -210,7 +200,8 @@ namespace NifOsg
META_Object(NifOsg,UVController) META_Object(NifOsg,UVController)
virtual void operator() (osg::Node*, osg::NodeVisitor*); virtual void setDefaults(osg::StateSet* stateset);
virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv);
private: private:
Nif::FloatKeyMapPtr mUTrans; Nif::FloatKeyMapPtr mUTrans;
@ -237,7 +228,7 @@ namespace NifOsg
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
}; };
class AlphaController : public osg::NodeCallback, public Controller, public StateSetController, public ValueInterpolator class AlphaController : public SceneUtil::StateSetController, public Controller, public ValueInterpolator
{ {
private: private:
Nif::FloatKeyMapPtr mData; Nif::FloatKeyMapPtr mData;
@ -247,12 +238,12 @@ namespace NifOsg
AlphaController(); AlphaController();
AlphaController(const AlphaController& copy, const osg::CopyOp& copyop); AlphaController(const AlphaController& copy, const osg::CopyOp& copyop);
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv);
META_Object(NifOsg, AlphaController) META_Object(NifOsg, AlphaController)
}; };
class MaterialColorController : public osg::NodeCallback, public Controller, public StateSetController, public ValueInterpolator class MaterialColorController : public SceneUtil::StateSetController, public Controller, public ValueInterpolator
{ {
private: private:
Nif::Vector3KeyMapPtr mData; Nif::Vector3KeyMapPtr mData;
@ -264,10 +255,10 @@ namespace NifOsg
META_Object(NifOsg, MaterialColorController) META_Object(NifOsg, MaterialColorController)
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv);
}; };
class FlipController : public osg::NodeCallback, public Controller, public StateSetController class FlipController : public SceneUtil::StateSetController, public Controller
{ {
private: private:
int mTexSlot; int mTexSlot;
@ -281,7 +272,7 @@ namespace NifOsg
META_Object(NifOsg, FlipController) META_Object(NifOsg, FlipController)
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv);
}; };
class ParticleSystemController : public osg::NodeCallback, public Controller class ParticleSystemController : public osg::NodeCallback, public Controller

@ -719,13 +719,13 @@ namespace NifOsg
return skel; return skel;
} }
static void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, Resource::TextureManager* textureManager, std::map<int, int>& boundTextures, int animflags) static void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetController* composite, Resource::TextureManager* textureManager, std::map<int, int>& boundTextures, int animflags)
{ {
const Nif::PropertyList& props = nifNode->props; const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i) for (size_t i = 0; i <props.length();++i)
{ {
if (!props[i].empty()) if (!props[i].empty())
handleProperty(props[i].getPtr(), applyTo, textureManager, boundTextures, animflags); handleProperty(props[i].getPtr(), applyTo, composite, textureManager, boundTextures, animflags);
} }
} }
@ -825,7 +825,9 @@ namespace NifOsg
transformNode->setNodeMask(0x1); transformNode->setNodeMask(0x1);
} }
applyNodeProperties(nifNode, transformNode, textureManager, boundTextures, animflags); osg::ref_ptr<SceneUtil::CompositeStateSetController> composite = new SceneUtil::CompositeStateSetController;
applyNodeProperties(nifNode, transformNode, composite, textureManager, boundTextures, animflags);
if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes) if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes)
{ {
@ -836,9 +838,12 @@ namespace NifOsg
handleSkinnedTriShape(triShape, transformNode, boundTextures, animflags); handleSkinnedTriShape(triShape, transformNode, boundTextures, animflags);
if (!nifNode->controller.empty()) if (!nifNode->controller.empty())
handleMeshControllers(nifNode, transformNode, boundTextures, animflags); handleMeshControllers(nifNode, composite, boundTextures, animflags);
} }
if (composite->getNumControllers() > 0)
transformNode->addUpdateCallback(composite);
if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles)
handleParticleSystem(nifNode, transformNode, animflags, particleflags, rootNode); handleParticleSystem(nifNode, transformNode, animflags, particleflags, rootNode);
@ -866,7 +871,7 @@ namespace NifOsg
return transformNode; return transformNode;
} }
static void handleMeshControllers(const Nif::Node *nifNode, osg::MatrixTransform *transformNode, const std::map<int, int> &boundTextures, int animflags) static void handleMeshControllers(const Nif::Node *nifNode, SceneUtil::CompositeStateSetController* composite, const std::map<int, int> &boundTextures, int animflags)
{ {
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
{ {
@ -881,8 +886,7 @@ namespace NifOsg
osg::ref_ptr<UVController> ctrl = new UVController(uvctrl->data.getPtr(), texUnits); osg::ref_ptr<UVController> ctrl = new UVController(uvctrl->data.getPtr(), texUnits);
setupController(uvctrl, ctrl, animflags); setupController(uvctrl, ctrl, animflags);
composite->addController(ctrl);
transformNode->addUpdateCallback(ctrl);
} }
} }
} }
@ -915,8 +919,9 @@ namespace NifOsg
} }
} }
static void handleMaterialControllers(const Nif::Property *materialProperty, osg::Node* node, osg::StateSet *stateset, int animflags) static void handleMaterialControllers(const Nif::Property *materialProperty, osg::Node* node, int animflags)
{ {
osg::ref_ptr<SceneUtil::CompositeStateSetController> composite = new SceneUtil::CompositeStateSetController;
for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next)
{ {
if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))
@ -926,21 +931,23 @@ namespace NifOsg
const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr()); const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr());
osg::ref_ptr<AlphaController> ctrl(new AlphaController(alphactrl->data.getPtr())); osg::ref_ptr<AlphaController> ctrl(new AlphaController(alphactrl->data.getPtr()));
setupController(alphactrl, ctrl, animflags); setupController(alphactrl, ctrl, animflags);
node->addUpdateCallback(ctrl); composite->addController(ctrl);
} }
else if (ctrl->recType == Nif::RC_NiMaterialColorController) else if (ctrl->recType == Nif::RC_NiMaterialColorController)
{ {
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr()); const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
osg::ref_ptr<MaterialColorController> ctrl(new MaterialColorController(matctrl->data.getPtr())); osg::ref_ptr<MaterialColorController> ctrl(new MaterialColorController(matctrl->data.getPtr()));
setupController(matctrl, ctrl, animflags); setupController(matctrl, ctrl, animflags);
node->addUpdateCallback(ctrl); composite->addController(ctrl);
} }
else else
std::cerr << "Unexpected material controller " << ctrl->recType << std::endl; std::cerr << "Unexpected material controller " << ctrl->recType << std::endl;
} }
if (composite->getNumControllers() > 0)
node->addUpdateCallback(composite);
} }
static void handleTextureControllers(const Nif::Property *texProperty, osg::Node* node, Resource::TextureManager* textureManager, osg::StateSet *stateset, int animflags) static void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetController* composite, Resource::TextureManager* textureManager, osg::StateSet *stateset, int animflags)
{ {
for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next)
{ {
@ -972,7 +979,7 @@ namespace NifOsg
} }
osg::ref_ptr<FlipController> callback(new FlipController(flipctrl, textures)); osg::ref_ptr<FlipController> callback(new FlipController(flipctrl, textures));
setupController(ctrl.getPtr(), callback, animflags); setupController(ctrl.getPtr(), callback, animflags);
node->addUpdateCallback(callback); composite->addController(callback);
} }
else else
std::cerr << "Unexpected texture controller " << ctrl->recName << std::endl; std::cerr << "Unexpected texture controller " << ctrl->recName << std::endl;
@ -1419,7 +1426,7 @@ namespace NifOsg
static void handleProperty(const Nif::Property *property, static void handleProperty(const Nif::Property *property,
osg::Node *node, Resource::TextureManager* textureManager, std::map<int, int>& boundTextures, int animflags) osg::Node *node, SceneUtil::CompositeStateSetController* composite, Resource::TextureManager* textureManager, std::map<int, int>& boundTextures, int animflags)
{ {
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
@ -1594,7 +1601,7 @@ namespace NifOsg
stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF); stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF);
boundTextures.erase(i); boundTextures.erase(i);
} }
handleTextureControllers(texprop, node, textureManager, stateset, animflags); handleTextureControllers(texprop, composite, textureManager, stateset, animflags);
} }
break; break;
} }
@ -1646,7 +1653,7 @@ namespace NifOsg
mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness); mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness);
if (!matprop->controller.empty()) if (!matprop->controller.empty())
handleMaterialControllers(matprop, node, stateset, animflags); handleMaterialControllers(matprop, node, animflags);
break; break;
} }

@ -28,4 +28,47 @@ namespace SceneUtil
traverse(node, nv); traverse(node, nv);
} }
StateSetController::StateSetController()
{
}
StateSetController::StateSetController(const StateSetController &copy, const osg::CopyOp &copyop)
: osg::NodeCallback(copy, copyop)
{
}
// ----------------------------------------------------------------------------------
void CompositeStateSetController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
{
for (unsigned int i=0; i<mCtrls.size(); ++i)
mCtrls[i]->apply(stateset, nv);
}
void CompositeStateSetController::setDefaults(osg::StateSet *stateset)
{
for (unsigned int i=0; i<mCtrls.size(); ++i)
mCtrls[i]->setDefaults(stateset);
}
CompositeStateSetController::CompositeStateSetController()
{
}
CompositeStateSetController::CompositeStateSetController(const CompositeStateSetController &copy, const osg::CopyOp &copyop)
: StateSetController(copy, copyop)
, mCtrls(copy.mCtrls)
{
}
unsigned int CompositeStateSetController::getNumControllers()
{
return mCtrls.size();
}
void CompositeStateSetController::addController(StateSetController *ctrl)
{
mCtrls.push_back(ctrl);
}
} }

@ -15,16 +15,21 @@ namespace SceneUtil
/// the first StateSet is the one we can write to, the second is the one currently in use by the draw traversal of the last frame. /// the first StateSet is the one we can write to, the second is the one currently in use by the draw traversal of the last frame.
/// After a frame is completed the places are swapped. /// After a frame is completed the places are swapped.
/// @par Must be set as UpdateCallback on a Node. /// @par Must be set as UpdateCallback on a Node.
/// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetController.
class StateSetController : public osg::NodeCallback class StateSetController : public osg::NodeCallback
{ {
public: public:
StateSetController();
StateSetController(const StateSetController& copy, const osg::CopyOp& copyop);
META_Object(SceneUtil, StateSetController)
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
protected:
/// Apply state - to override in derived classes /// Apply state - to override in derived classes
/// @note Due to the double buffering approach you *have* to apply all state /// @note Due to the double buffering approach you *have* to apply all state
/// even if it has not changed since the last frame. /// even if it has not changed since the last frame.
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) = 0; virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) {}
/// Set default state - optionally override in derived classes /// Set default state - optionally override in derived classes
/// @par May be used e.g. to allocate StateAttributes. /// @par May be used e.g. to allocate StateAttributes.
@ -34,6 +39,28 @@ namespace SceneUtil
osg::ref_ptr<osg::StateSet> mStateSets[2]; osg::ref_ptr<osg::StateSet> mStateSets[2];
}; };
/// @brief A variant of the StateSetController that can be made up of multiple controllers all controlling the same target.
class CompositeStateSetController : public StateSetController
{
public:
CompositeStateSetController();
CompositeStateSetController(const CompositeStateSetController& copy, const osg::CopyOp& copyop);
META_Object(SceneUtil, CompositeStateSetController)
unsigned int getNumControllers();
void addController(StateSetController* ctrl);
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv);
protected:
virtual void setDefaults(osg::StateSet *stateset);
std::vector<osg::ref_ptr<StateSetController> > mCtrls;
};
} }
#endif #endif

Loading…
Cancel
Save