diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 59ba9ad4f..344cb2323 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -278,7 +278,7 @@ UVController::UVController(const Nif::NiUVData *data, std::set textureUnits } 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) , mVTrans(copy.mVTrans) , 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::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()) { - osg::StateSet* stateset = getWritableStateSet(node); float value = getInputValue(nv); float uTrans = interpKey(mUTrans->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); mat.setTrans(uTrans, vTrans, 0); - osg::TexMat* texMat = new osg::TexMat; - texMat->setMatrix(mat); - - for (std::set::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it) + // setting once is enough because all other texture units share the same TexMat (see setDefaults). + if (mTextureUnits.size()) { - stateset->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON); + osg::TexMat* texMat = static_cast(stateset->getTextureAttribute(*mTextureUnits.begin(), osg::StateAttribute::TEXMAT)); + texMat->setMatrix(mat); } } - traverse(node, nv); } VisController::VisController(const Nif::NiVisData *data) @@ -363,26 +367,21 @@ AlphaController::AlphaController() } AlphaController::AlphaController(const AlphaController ©, const osg::CopyOp ©op) - : osg::NodeCallback(copy, copyop), ValueInterpolator(), Controller(copy) + : StateSetController(copy, copyop), ValueInterpolator(), Controller(copy) , mData(copy.mData) { } -void AlphaController::operator () (osg::Node* node, osg::NodeVisitor* nv) +void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { if (hasInput()) { - osg::StateSet* stateset = getWritableStateSet(node); float value = interpKey(mData->mKeys, getInputValue(nv)); - osg::Material* mat = dynamic_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); - if (mat) - { - osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); - diffuse.a() = value; - mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse); - } + osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); + osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); + diffuse.a() = value; + mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse); } - traverse(node, nv); } MaterialColorController::MaterialColorController(const Nif::NiPosData *data) @@ -395,26 +394,21 @@ MaterialColorController::MaterialColorController() } MaterialColorController::MaterialColorController(const MaterialColorController ©, const osg::CopyOp ©op) - : osg::NodeCallback(copy, copyop), Controller(copy) + : StateSetController(copy, copyop), Controller(copy) , mData(copy.mData) { } -void MaterialColorController::operator() (osg::Node* node, osg::NodeVisitor* nv) +void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { if (hasInput()) { - osg::StateSet* stateset = getWritableStateSet(node); osg::Vec3f value = interpKey(mData->mKeys, getInputValue(nv)); - osg::Material* mat = dynamic_cast(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()); - mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse); - } + osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); + osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); + 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 > textures) @@ -429,7 +423,7 @@ FlipController::FlipController() } FlipController::FlipController(const FlipController ©, const osg::CopyOp ©op) - : osg::NodeCallback(copy, copyop) + : StateSetController(copy, copyop) , Controller(copy) , mTexSlot(copy.mTexSlot) , mDelta(copy.mDelta) @@ -437,15 +431,13 @@ FlipController::FlipController(const FlipController ©, 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) { - osg::StateSet* stateset = getWritableStateSet(node); int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); } - traverse(node, nv); } ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController *ctrl) @@ -507,12 +499,4 @@ void SourcedKeyframeController::operator ()(osg::Node* node, osg::NodeVisitor* n 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; -} - } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 1a90b8759..103a72046 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -8,6 +8,8 @@ #include +#include + #include #include //UVController @@ -189,19 +191,7 @@ namespace NifOsg bool mEnabled; }; - class StateSetController - { - 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 + class UVController : public SceneUtil::StateSetController, public Controller, public ValueInterpolator { public: UVController(); @@ -210,7 +200,8 @@ namespace NifOsg 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: Nif::FloatKeyMapPtr mUTrans; @@ -237,7 +228,7 @@ namespace NifOsg 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: Nif::FloatKeyMapPtr mData; @@ -247,12 +238,12 @@ namespace NifOsg AlphaController(); 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) }; - class MaterialColorController : public osg::NodeCallback, public Controller, public StateSetController, public ValueInterpolator + class MaterialColorController : public SceneUtil::StateSetController, public Controller, public ValueInterpolator { private: Nif::Vector3KeyMapPtr mData; @@ -264,10 +255,10 @@ namespace NifOsg 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: int mTexSlot; @@ -281,7 +272,7 @@ namespace NifOsg 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 diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index a85847351..8dc286a92 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -719,13 +719,13 @@ namespace NifOsg return skel; } - static void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, Resource::TextureManager* textureManager, std::map& boundTextures, int animflags) + static void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetController* composite, Resource::TextureManager* textureManager, std::map& boundTextures, int animflags) { const Nif::PropertyList& props = nifNode->props; for (size_t i = 0; i setNodeMask(0x1); } - applyNodeProperties(nifNode, transformNode, textureManager, boundTextures, animflags); + osg::ref_ptr composite = new SceneUtil::CompositeStateSetController; + + applyNodeProperties(nifNode, transformNode, composite, textureManager, boundTextures, animflags); if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes) { @@ -836,9 +838,12 @@ namespace NifOsg handleSkinnedTriShape(triShape, transformNode, boundTextures, animflags); 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) handleParticleSystem(nifNode, transformNode, animflags, particleflags, rootNode); @@ -866,7 +871,7 @@ namespace NifOsg return transformNode; } - static void handleMeshControllers(const Nif::Node *nifNode, osg::MatrixTransform *transformNode, const std::map &boundTextures, int animflags) + static void handleMeshControllers(const Nif::Node *nifNode, SceneUtil::CompositeStateSetController* composite, const std::map &boundTextures, int animflags) { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -881,8 +886,7 @@ namespace NifOsg osg::ref_ptr ctrl = new UVController(uvctrl->data.getPtr(), texUnits); setupController(uvctrl, ctrl, animflags); - - transformNode->addUpdateCallback(ctrl); + composite->addController(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 composite = new SceneUtil::CompositeStateSetController; for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) @@ -926,21 +931,23 @@ namespace NifOsg const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); osg::ref_ptr ctrl(new AlphaController(alphactrl->data.getPtr())); setupController(alphactrl, ctrl, animflags); - node->addUpdateCallback(ctrl); + composite->addController(ctrl); } else if (ctrl->recType == Nif::RC_NiMaterialColorController) { const Nif::NiMaterialColorController* matctrl = static_cast(ctrl.getPtr()); osg::ref_ptr ctrl(new MaterialColorController(matctrl->data.getPtr())); setupController(matctrl, ctrl, animflags); - node->addUpdateCallback(ctrl); + composite->addController(ctrl); } else 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) { @@ -972,7 +979,7 @@ namespace NifOsg } osg::ref_ptr callback(new FlipController(flipctrl, textures)); setupController(ctrl.getPtr(), callback, animflags); - node->addUpdateCallback(callback); + composite->addController(callback); } else std::cerr << "Unexpected texture controller " << ctrl->recName << std::endl; @@ -1419,7 +1426,7 @@ namespace NifOsg static void handleProperty(const Nif::Property *property, - osg::Node *node, Resource::TextureManager* textureManager, std::map& boundTextures, int animflags) + osg::Node *node, SceneUtil::CompositeStateSetController* composite, Resource::TextureManager* textureManager, std::map& boundTextures, int animflags) { osg::StateSet* stateset = node->getOrCreateStateSet(); @@ -1594,7 +1601,7 @@ namespace NifOsg stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF); boundTextures.erase(i); } - handleTextureControllers(texprop, node, textureManager, stateset, animflags); + handleTextureControllers(texprop, composite, textureManager, stateset, animflags); } break; } @@ -1646,7 +1653,7 @@ namespace NifOsg mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness); if (!matprop->controller.empty()) - handleMaterialControllers(matprop, node, stateset, animflags); + handleMaterialControllers(matprop, node, animflags); break; } diff --git a/components/sceneutil/statesetcontroller.cpp b/components/sceneutil/statesetcontroller.cpp index 301e709e4..079eaf6aa 100644 --- a/components/sceneutil/statesetcontroller.cpp +++ b/components/sceneutil/statesetcontroller.cpp @@ -28,4 +28,47 @@ namespace SceneUtil traverse(node, nv); } + StateSetController::StateSetController() + { + } + + StateSetController::StateSetController(const StateSetController ©, const osg::CopyOp ©op) + : osg::NodeCallback(copy, copyop) + { + } + + // ---------------------------------------------------------------------------------- + + void CompositeStateSetController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) + { + for (unsigned int i=0; iapply(stateset, nv); + } + + void CompositeStateSetController::setDefaults(osg::StateSet *stateset) + { + for (unsigned int i=0; isetDefaults(stateset); + } + + CompositeStateSetController::CompositeStateSetController() + { + } + + CompositeStateSetController::CompositeStateSetController(const CompositeStateSetController ©, const osg::CopyOp ©op) + : StateSetController(copy, copyop) + , mCtrls(copy.mCtrls) + { + } + + unsigned int CompositeStateSetController::getNumControllers() + { + return mCtrls.size(); + } + + void CompositeStateSetController::addController(StateSetController *ctrl) + { + mCtrls.push_back(ctrl); + } + } diff --git a/components/sceneutil/statesetcontroller.hpp b/components/sceneutil/statesetcontroller.hpp index 7c6c7e407..e34332e5f 100644 --- a/components/sceneutil/statesetcontroller.hpp +++ b/components/sceneutil/statesetcontroller.hpp @@ -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. /// After a frame is completed the places are swapped. /// @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 { public: + StateSetController(); + StateSetController(const StateSetController& copy, const osg::CopyOp& copyop); + + META_Object(SceneUtil, StateSetController) + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); - protected: /// Apply state - to override in derived classes /// @note Due to the double buffering approach you *have* to apply all state /// 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 /// @par May be used e.g. to allocate StateAttributes. @@ -34,6 +39,28 @@ namespace SceneUtil osg::ref_ptr 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 > mCtrls; + }; + } #endif