forked from mirror/openmw-tes3mp
Port Animation::addEffect
This commit is contained in:
parent
083c41c950
commit
edc5cad79e
17 changed files with 294 additions and 61 deletions
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <components/sceneutil/controller.hpp>
|
||||
|
||||
#include <osgGA/TrackballManipulator>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
|
||||
|
@ -129,6 +131,9 @@ int main(int argc, char** argv)
|
|||
Resource::TextureManager texMgr(&resourceMgr);
|
||||
newNode->addChild(loader.load(nif, &texMgr));
|
||||
|
||||
SceneUtil::AssignControllerSourcesVisitor visitor(boost::shared_ptr<SceneUtil::FrameTimeSource>(new SceneUtil::FrameTimeSource));
|
||||
newNode->accept(visitor);
|
||||
|
||||
osg::PositionAttitudeTransform* trans = new osg::PositionAttitudeTransform;
|
||||
root->addChild(trans);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ set(GAME_HEADER
|
|||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||
|
||||
add_openmw_dir (mwrender
|
||||
actors objects renderingmanager animation sky npcanimation
|
||||
actors objects renderingmanager animation sky npcanimation vismask
|
||||
# debugging camera creatureanimation activatoranimation
|
||||
# renderinginterface localmap occlusionquery water shadows
|
||||
# characterpreview globalmap ripplesimulation refraction
|
||||
|
|
|
@ -10,13 +10,18 @@
|
|||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/texturemanager.hpp>
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include <components/sceneutil/statesetupdater.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "vismask.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
@ -62,6 +67,30 @@ namespace
|
|||
std::vector<osg::ref_ptr<osg::Texture2D> > mTextures;
|
||||
};
|
||||
|
||||
class FindMaxControllerLengthVisitor : public SceneUtil::ControllerVisitor
|
||||
{
|
||||
public:
|
||||
FindMaxControllerLengthVisitor()
|
||||
: SceneUtil::ControllerVisitor()
|
||||
, mMaxLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void visit(osg::Node& , SceneUtil::Controller& ctrl)
|
||||
{
|
||||
if (ctrl.mFunction)
|
||||
mMaxLength = std::max(mMaxLength, ctrl.mFunction->getMaximum());
|
||||
}
|
||||
|
||||
float getMaxLength() const
|
||||
{
|
||||
return mMaxLength;
|
||||
}
|
||||
|
||||
private:
|
||||
float mMaxLength;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
|
@ -83,6 +112,8 @@ namespace MWRender
|
|||
|
||||
osg::Vec3f Animation::runAnimation(float duration)
|
||||
{
|
||||
updateEffects(duration);
|
||||
|
||||
return osg::Vec3f();
|
||||
}
|
||||
|
||||
|
@ -147,6 +178,133 @@ namespace MWRender
|
|||
return result;
|
||||
}
|
||||
|
||||
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, std::string texture)
|
||||
{
|
||||
// Early out if we already have this effect
|
||||
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
|
||||
if (it->mLoop && loop && it->mEffectId == effectId && it->mBoneName == bonename)
|
||||
return;
|
||||
|
||||
EffectParams params;
|
||||
params.mModelName = model;
|
||||
osg::ref_ptr<osg::Group> parentNode;
|
||||
if (bonename.empty())
|
||||
parentNode = mObjectRoot->asGroup();
|
||||
else
|
||||
{
|
||||
SceneUtil::FindByNameVisitor visitor(bonename);
|
||||
mObjectRoot->accept(visitor);
|
||||
if (!visitor.mFoundNode)
|
||||
throw std::runtime_error("Can't find bone " + bonename);
|
||||
parentNode = visitor.mFoundNode;
|
||||
}
|
||||
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->createInstance(model, parentNode);
|
||||
params.mObjects = PartHolderPtr(new PartHolder(node));
|
||||
|
||||
FindMaxControllerLengthVisitor findMaxLengthVisitor;
|
||||
node->accept(findMaxLengthVisitor);
|
||||
|
||||
params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength();
|
||||
|
||||
node->setNodeMask(Mask_Effect);
|
||||
|
||||
params.mLoop = loop;
|
||||
params.mEffectId = effectId;
|
||||
params.mBoneName = bonename;
|
||||
|
||||
params.mAnimTime = boost::shared_ptr<EffectAnimationTime>(new EffectAnimationTime);
|
||||
|
||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr<SceneUtil::ControllerSource>(params.mAnimTime));
|
||||
node->accept(assignVisitor);
|
||||
|
||||
if (!texture.empty())
|
||||
{
|
||||
std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture, mResourceSystem->getVFS());
|
||||
// Not sure if wrap settings should be pulled from the overridden texture?
|
||||
osg::ref_ptr<osg::Texture2D> tex = mResourceSystem->getTextureManager()->getTexture2D(correctedTexture, osg::Texture2D::CLAMP,
|
||||
osg::Texture2D::CLAMP);
|
||||
osg::ref_ptr<osg::StateSet> stateset;
|
||||
if (node->getStateSet())
|
||||
stateset = static_cast<osg::StateSet*>(node->getStateSet()->clone(osg::CopyOp::SHALLOW_COPY));
|
||||
else
|
||||
stateset = new osg::StateSet;
|
||||
|
||||
stateset->setTextureAttribute(0, tex, osg::StateAttribute::OVERRIDE);
|
||||
|
||||
node->setStateSet(stateset);
|
||||
}
|
||||
|
||||
// TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box.
|
||||
|
||||
mEffects.push_back(params);
|
||||
}
|
||||
|
||||
void Animation::removeEffect(int effectId)
|
||||
{
|
||||
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
|
||||
{
|
||||
if (it->mEffectId == effectId)
|
||||
{
|
||||
mEffects.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::getLoopingEffects(std::vector<int> &out)
|
||||
{
|
||||
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
|
||||
{
|
||||
if (it->mLoop)
|
||||
out.push_back(it->mEffectId);
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::updateEffects(float duration)
|
||||
{
|
||||
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); )
|
||||
{
|
||||
it->mAnimTime->addTime(duration);
|
||||
|
||||
if (it->mAnimTime->getTime() >= it->mMaxControllerLength)
|
||||
{
|
||||
if (it->mLoop)
|
||||
{
|
||||
// Start from the beginning again; carry over the remainder
|
||||
// Not sure if this is actually needed, the controller function might already handle loops
|
||||
float remainder = it->mAnimTime->getTime() - it->mMaxControllerLength;
|
||||
it->mAnimTime->resetTime(remainder);
|
||||
}
|
||||
else
|
||||
{
|
||||
it = mEffects.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
float Animation::EffectAnimationTime::getValue(osg::NodeVisitor*)
|
||||
{
|
||||
return mTime;
|
||||
}
|
||||
|
||||
void Animation::EffectAnimationTime::addTime(float duration)
|
||||
{
|
||||
mTime += duration;
|
||||
}
|
||||
|
||||
void Animation::EffectAnimationTime::resetTime(float time)
|
||||
{
|
||||
mTime = time;
|
||||
}
|
||||
|
||||
float Animation::EffectAnimationTime::getTime() const
|
||||
{
|
||||
return mTime;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
ObjectAnimation::ObjectAnimation(const MWWorld::Ptr &ptr, const std::string &model, Resource::ResourceSystem* resourceSystem)
|
||||
|
@ -159,11 +317,6 @@ namespace MWRender
|
|||
if (!ptr.getClass().getEnchantment(ptr).empty())
|
||||
addGlow(mObjectRoot, getEnchantmentColor(ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// No model given. Create an object root anyway, so that lights can be added to it if needed.
|
||||
//mObjectRoot = NifOgre::ObjectScenePtr (new NifOgre::ObjectScene(mInsert->getCreator()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -61,17 +61,18 @@ protected:
|
|||
virtual void setValue(Ogre::Real value);
|
||||
};
|
||||
|
||||
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
class EffectAnimationTime : public SceneUtil::ControllerSource
|
||||
{
|
||||
private:
|
||||
float mTime;
|
||||
public:
|
||||
EffectAnimationTime() : mTime(0) { }
|
||||
void addTime(float time) { mTime += time; }
|
||||
void resetTime(float value) { mTime = value; }
|
||||
virtual float getValue(osg::NodeVisitor* nv);
|
||||
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value);
|
||||
void addTime(float duration);
|
||||
void resetTime(float time);
|
||||
float getTime() const;
|
||||
|
||||
EffectAnimationTime() : mTime(0) { }
|
||||
};
|
||||
|
||||
class NullAnimationTime : public SceneUtil::ControllerSource
|
||||
|
@ -124,6 +125,44 @@ protected:
|
|||
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
|
||||
/// @brief Detaches the node from its parent when the object goes out of scope.
|
||||
class PartHolder
|
||||
{
|
||||
public:
|
||||
PartHolder(osg::ref_ptr<osg::Node> node)
|
||||
: mNode(node)
|
||||
{
|
||||
}
|
||||
|
||||
~PartHolder()
|
||||
{
|
||||
if (mNode->getNumParents())
|
||||
mNode->getParent(0)->removeChild(mNode);
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> getNode()
|
||||
{
|
||||
return mNode;
|
||||
}
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Node> mNode;
|
||||
};
|
||||
typedef boost::shared_ptr<PartHolder> PartHolderPtr;
|
||||
|
||||
struct EffectParams
|
||||
{
|
||||
std::string mModelName; // Just here so we don't add the same effect twice
|
||||
PartHolderPtr mObjects;
|
||||
boost::shared_ptr<EffectAnimationTime> mAnimTime;
|
||||
float mMaxControllerLength;
|
||||
int mEffectId;
|
||||
bool mLoop;
|
||||
std::string mBoneName;
|
||||
};
|
||||
|
||||
std::vector<EffectParams> mEffects;
|
||||
|
||||
/* Sets the appropriate animations on the bone groups based on priority.
|
||||
*/
|
||||
//void resetActiveGroups();
|
||||
|
@ -192,12 +231,12 @@ public:
|
|||
* @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true,
|
||||
* you need to remove it manually using removeEffect when the effect should end.
|
||||
* @param bonename Bone to attach to, or empty string to use the scene node instead
|
||||
* @param texture override the texture specified in the model's materials
|
||||
* @param texture override the texture specified in the model's materials - if empty, do not override
|
||||
* @note Will not add an effect twice.
|
||||
*/
|
||||
//void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", std::string texture = "");
|
||||
//void removeEffect (int effectId);
|
||||
//void getLoopingEffects (std::vector<int>& out);
|
||||
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", std::string texture = "");
|
||||
void removeEffect (int effectId);
|
||||
void getLoopingEffects (std::vector<int>& out);
|
||||
|
||||
//void updatePtr(const MWWorld::Ptr &ptr);
|
||||
|
||||
|
@ -273,6 +312,9 @@ public:
|
|||
//float getVelocity(const std::string &groupname) const;
|
||||
|
||||
virtual osg::Vec3f runAnimation(float duration);
|
||||
|
||||
/// This is typically called as part of runAnimation, but may be called manually if needed.
|
||||
void updateEffects(float duration);
|
||||
};
|
||||
|
||||
class ObjectAnimation : public Animation {
|
||||
|
|
|
@ -587,12 +587,15 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, int group, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor)
|
||||
Animation::PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, int group, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor)
|
||||
{
|
||||
osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->createInstance(model);
|
||||
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, bonename);
|
||||
if (enchantedGlow)
|
||||
addGlow(attached, *glowColor);
|
||||
|
||||
// TODO: set group userdata for inventory picking
|
||||
|
||||
return PartHolderPtr(new PartHolder(attached));
|
||||
}
|
||||
|
||||
|
|
|
@ -50,26 +50,6 @@ public:
|
|||
};
|
||||
*/
|
||||
|
||||
/// @brief Detaches the node from its parent when the object goes out of scope.
|
||||
class PartHolder
|
||||
{
|
||||
public:
|
||||
PartHolder(osg::ref_ptr<osg::Node> node)
|
||||
: mNode(node)
|
||||
{
|
||||
}
|
||||
|
||||
~PartHolder()
|
||||
{
|
||||
if (mNode->getNumParents())
|
||||
mNode->getParent(0)->removeChild(mNode);
|
||||
}
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Node> mNode;
|
||||
};
|
||||
typedef boost::shared_ptr<PartHolder> PartHolderPtr;
|
||||
|
||||
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -246,7 +246,7 @@ void Objects::removeCell(const MWWorld::CellStore* store)
|
|||
}
|
||||
}
|
||||
|
||||
void Objects::update(float dt, Ogre::Camera* camera)
|
||||
void Objects::update(float dt)
|
||||
{
|
||||
PtrAnimationMap::const_iterator it = mObjects.begin();
|
||||
for(;it != mObjects.end();++it)
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
|
||||
Animation* getAnimation(const MWWorld::Ptr &ptr);
|
||||
|
||||
void update (float dt, Ogre::Camera* camera);
|
||||
void update (float dt);
|
||||
///< per-frame update
|
||||
|
||||
//Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*);
|
||||
|
|
|
@ -160,4 +160,9 @@ namespace MWRender
|
|||
return mSky.get();
|
||||
}
|
||||
|
||||
void RenderingManager::update(float dt, bool paused)
|
||||
{
|
||||
mObjects->update(dt);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ namespace MWRender
|
|||
|
||||
osg::Vec3f getEyePos();
|
||||
|
||||
void update(float dt, bool paused);
|
||||
|
||||
private:
|
||||
osgViewer::Viewer& mViewer;
|
||||
osg::ref_ptr<osg::Group> mRootNode;
|
||||
|
|
17
apps/openmw/mwrender/vismask.hpp
Normal file
17
apps/openmw/mwrender/vismask.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef OPENMW_MWRENDER_VISMASK_H
|
||||
#define OPENMW_MWRENDER_VISMASK_H
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
/// Node masks used for controlling visibility of game objects.
|
||||
enum VisMask
|
||||
{
|
||||
Mask_UpdateVisitor = 0x1, // reserved for separating UpdateVisitors from CullVisitors
|
||||
|
||||
Mask_Effect = 0x2
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -180,7 +180,7 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
//mRendering.update (duration, paused);
|
||||
mRendering.update (duration, paused);
|
||||
}
|
||||
|
||||
void Scene::unloadCell (CellStoreCollection::iterator iter)
|
||||
|
|
|
@ -1606,13 +1606,13 @@ namespace MWWorld
|
|||
goToJail();
|
||||
|
||||
updateWeather(duration, paused);
|
||||
/*
|
||||
|
||||
if (!paused)
|
||||
doPhysics (duration);
|
||||
//if (!paused)
|
||||
// doPhysics (duration);
|
||||
|
||||
mWorldScene->update (duration, paused);
|
||||
|
||||
/*
|
||||
performUpdateSceneQueries ();
|
||||
|
||||
updateWindowManager ();
|
||||
|
|
|
@ -64,6 +64,11 @@ float ControllerFunction::calculate(float value)
|
|||
}
|
||||
}
|
||||
|
||||
float ControllerFunction::getMaximum() const
|
||||
{
|
||||
return mStopTime;
|
||||
}
|
||||
|
||||
KeyframeController::KeyframeController()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -95,6 +95,8 @@ namespace NifOsg
|
|||
ControllerFunction(const Nif::Controller *ctrl);
|
||||
|
||||
float calculate(float value);
|
||||
|
||||
virtual float getMaximum() const;
|
||||
};
|
||||
|
||||
class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller, public ValueInterpolator
|
||||
|
|
|
@ -32,31 +32,26 @@ namespace SceneUtil
|
|||
return nv->getFrameStamp()->getSimulationTime();
|
||||
}
|
||||
|
||||
AssignControllerSourcesVisitor::AssignControllerSourcesVisitor()
|
||||
ControllerVisitor::ControllerVisitor()
|
||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AssignControllerSourcesVisitor::AssignControllerSourcesVisitor(boost::shared_ptr<ControllerSource> toAssign)
|
||||
: mToAssign(toAssign)
|
||||
, osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||
{
|
||||
}
|
||||
|
||||
void AssignControllerSourcesVisitor::apply(osg::Node &node)
|
||||
void ControllerVisitor::apply(osg::Node &node)
|
||||
{
|
||||
osg::NodeCallback* callback = node.getUpdateCallback();
|
||||
while (callback)
|
||||
{
|
||||
if (Controller* ctrl = dynamic_cast<Controller*>(callback))
|
||||
assign(node, *ctrl);
|
||||
visit(node, *ctrl);
|
||||
if (CompositeStateSetUpdater* composite = dynamic_cast<CompositeStateSetUpdater*>(callback))
|
||||
{
|
||||
for (unsigned int i=0; i<composite->getNumControllers(); ++i)
|
||||
{
|
||||
StateSetUpdater* statesetcontroller = composite->getController(i);
|
||||
if (Controller* ctrl = dynamic_cast<Controller*>(statesetcontroller))
|
||||
assign(node, *ctrl);
|
||||
visit(node, *ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,18 +61,29 @@ namespace SceneUtil
|
|||
traverse(node);
|
||||
}
|
||||
|
||||
void AssignControllerSourcesVisitor::apply(osg::Geode &geode)
|
||||
void ControllerVisitor::apply(osg::Geode &geode)
|
||||
{
|
||||
for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
|
||||
{
|
||||
osg::Drawable* drw = geode.getDrawable(i);
|
||||
osg::Drawable::UpdateCallback* callback = drw->getUpdateCallback();
|
||||
if (Controller* ctrl = dynamic_cast<Controller*>(callback))
|
||||
assign(geode, *ctrl);
|
||||
visit(geode, *ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
void AssignControllerSourcesVisitor::assign(osg::Node&, Controller &ctrl)
|
||||
AssignControllerSourcesVisitor::AssignControllerSourcesVisitor()
|
||||
: ControllerVisitor()
|
||||
{
|
||||
}
|
||||
|
||||
AssignControllerSourcesVisitor::AssignControllerSourcesVisitor(boost::shared_ptr<ControllerSource> toAssign)
|
||||
: ControllerVisitor()
|
||||
, mToAssign(toAssign)
|
||||
{
|
||||
}
|
||||
|
||||
void AssignControllerSourcesVisitor::visit(osg::Node&, Controller &ctrl)
|
||||
{
|
||||
if (!ctrl.mSource.get())
|
||||
ctrl.mSource = mToAssign;
|
||||
|
|
|
@ -25,6 +25,10 @@ namespace SceneUtil
|
|||
{
|
||||
public:
|
||||
virtual float calculate(float input) = 0;
|
||||
|
||||
/// Get the "stop time" of the controller function, typically the maximum of the calculate() function.
|
||||
/// May not be meaningful for all types of controller functions.
|
||||
virtual float getMaximum() const = 0;
|
||||
};
|
||||
|
||||
class Controller
|
||||
|
@ -42,18 +46,27 @@ namespace SceneUtil
|
|||
boost::shared_ptr<ControllerFunction> mFunction;
|
||||
};
|
||||
|
||||
class AssignControllerSourcesVisitor : public osg::NodeVisitor
|
||||
/// Pure virtual base class - visit() all controllers that are attached as UpdateCallbacks in a scene graph.
|
||||
class ControllerVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
ControllerVisitor();
|
||||
|
||||
virtual void apply(osg::Node& node);
|
||||
virtual void apply(osg::Geode& geode);
|
||||
|
||||
virtual void visit(osg::Node& node, Controller& ctrl) = 0;
|
||||
};
|
||||
|
||||
class AssignControllerSourcesVisitor : public ControllerVisitor
|
||||
{
|
||||
public:
|
||||
AssignControllerSourcesVisitor();
|
||||
AssignControllerSourcesVisitor(boost::shared_ptr<ControllerSource> toAssign);
|
||||
|
||||
virtual void apply(osg::Node& node);
|
||||
virtual void apply(osg::Geode& geode);
|
||||
|
||||
/// Assign the wanted ControllerSource. May be overriden in derived classes.
|
||||
/// By default assigns the ControllerSource passed to the constructor of this class if no ControllerSource is assigned to that controller yet.
|
||||
virtual void assign(osg::Node& node, Controller& ctrl);
|
||||
virtual void visit(osg::Node& node, Controller& ctrl);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<ControllerSource> mToAssign;
|
||||
|
|
Loading…
Reference in a new issue