Introduction
The basic elements of osganimation are introduced in the Animation foundation of Osganimation, in which the Channel channel class integrates key frame, interpolation algorithm, sampler and executing object (target), which makes up the essential element of animation. How do I apply these features to the scene animations, we look at Osganimation::channel and find that there are two member functions in it:
Const std::string& Gettargetname () const;
void Settargetname (const std::string& name);
Sets the name of the action object, what is the object of the action. What's it going to do?
First we want to use the results of the keyframe interpolation in the scene, so that the scene is constantly updated, we first think of the node's update callback. The update callback is called continuously in each frame, and the effect of the animation is achieved by applying our computed interpolation to the node, such as MatrixTransform, to continually modify the position, posture, or texture of the node. The role of the object will be loaded with the results of the application to the scene node of the task, it is all ready to only owe the East wind, the role of the object of this channel, all become the inevitable. List of Action objects
First, let's look at the inheritance diagram of the object
Fig1. Action Object Inheritance diagram
You can see that changes in position (such as skeletal animation, accelerated deceleration, and so on) can be achieved by inheriting from the Nodecallback object, but by inheriting stateattributecallback the animation is richer (the state in OpenGL, such as color, material, lighting, fog, etc. Below, we will explain the two types of animations separately. Updatematrixtransform
It is clear from the above inheritance diagram that this class inherits from Nodecallback, and from its implementation we see what it is going to do, and since it is nodecallback, it is necessary to overload the operator () operation
void Updatematrixtransform::operator () (osg::node* Node, osg::nodevisitor* nv)
{
if (NV && nv-> Getvisitortype () = = Osg::nodevisitor::update_visitor)
{
osg::matrixtransform* matrixtransform = dynamic_ cast<osg::matrixtransform*> (node);
if (MatrixTransform)
{
//Here we would prefer to has a flag inside transform stack in order to avoid update and A dirty state in MatrixTransform if it's not require.
_transforms.update ();
Const osg::matrix& Matrix = _transforms.getmatrix ();
Matrixtransform->setmatrix (matrix);
}
}
Traverse (NODE,NV);
}
You see,
It is used to modify the matrix of the MatrixTransform node in the update to achieve the animation effect for each frame transform position (that is, the class must be set to MatrixTransform update callback, cannot be set to the other type of node update callback)。 Here's another thing we need to know about is the member variable _transforms, which is actually an array similar to std::vector, which is defined as follows:
Class Stackedtransform:public osg::mixinvector<osg::ref_ptr<stackedtransformelement> >
{
Public:
stackedtransform ();
Stackedtransform (const stackedtransform&, const osg::copyop& COPYOP = osg::copyop::shallow_copy);
void Update (Float t = 0.0);
Const osg::matrix& Getmatrix () const;
Protected:
Osg::matrix _matrix;
};
Osg::mixinvector has re-implemented all the interfaces of the std::vector, and fixed the bug that Std::vector might appear, we have the right to be a std::vector. We have called the Stackedtransform Update method in operator ():
void Stackedtransform::update (float t)
{
int dirty = 0;
for (Stackedtransform::iterator it = begin (); it! = End (); ++it)
{
stackedtransformelement* element = It->get ( );
if (!element)
continue;
Update and check if there is changes
element->update (t);
if (element->isidentity ())
continue;
dirty++;
}
if (!dirty)
return;
Dirty Update Matrix
_matrix.makeidentity ();
for (Stackedtransform::iterator it = begin (); it! = End (); ++it)
{
stackedtransformelement* element = It->ge T ();
if (!element | | element->isidentity ())
continue;
Element->applytomatrix (_matrix);
}
}
In fact, it simply invokes each update method stored in the array (stackedtransformelement), and finally collects the matrix results after each element's update call. So what exactly is this stackedtransformelement, and we go on to look at its inheritance relationship:
Fig2 stackedtransformelement inheritance Diagram
It is easy to know from the name of the class on the inheriting class diagram that there are pointers to these class parent classes in the Stackedtransform array, which represent the transformation operations of the subclasses, such as: Translation (stackedtranslateelement), rotation ( Stackedquaternionelement), Scaling (stackedscaleelement), etc.
This class stackedtransformelement is a virtual class, its implementation is in the subclass of the present, with the translation of the stackedtranslateelement as an example:
void Stackedtranslateelement::update (float/*t*/)
{
if (_target.valid ())
_translate = _target-> GetValue ();
}
You can see that its update implementation simply acquires the value stored in _target, and by looking at the source code, you can see that all stackedtransformelement derived classes are simply getting the value of their member variable _target. So we can put
Updatematrixtransform::operator ()
The function is explained clearly.
Another explanation is the link function in Updatematrixtransform, which is implemented as follows:
bool Updatematrixtransform::link (osganimation::channel* Channel) {Const std::string&
ChannelName = Channel->getname (); Check if we can link a stackedtransformelement to the current Channel for (stackedtransform::iterator it = _TRANSFO Rms.begin (); It! = _transforms.end ();
++it) {stackedtransformelement* element = It->get ();
if (element &&!element->getname (). Empty () && ChannelName = = Element->getname ()) {
target* target = Element->getorcreatetarget ();
if (target && channel->settarget) return true; }} osg_info << "Updatematrixtransform::link Channel" << channel->getname () << "does not C
Ontain a symbolic name that can is linked to a stackedtransformelement. "<< Std::endl;
return false; }
You can see that the channel name in the channel must match the name of an element in the stackedtransformelement to bind successfully, that is, set the target of the element to the target of the channel, Let target pass the data as a common medium for them.
Now let's write an example that uses all the classes involved:
#include <osg/MatrixTransform> #include <osgViewer/Viewer> #include <osgDB/ReadFile> #include < osgviewer/viewer> #include <osg/ShapeDrawable> #include <osg/Shape> #include <osgga/ trackballmanipulator> #include <osgAnimation/Animation> #include <osganimation/animationupdatecallback > #include <osgAnimation/UpdateMatrixTransform> #include <osgAnimation/StackedTranslateElement> # Include <osgAnimation/StackedRotateAxisElement> class Playcallback:public Osg::nodecallback {public:playcallb ACK (osganimation::animation *anim): _animation (anim) {} void operator () (osg::node* Node, osg::nodevisitor* nv) {if
(Nv->getvisitortype () = = Osg::nodevisitor::update_visitor)
{osganimation::channellist& channels = _animation->getchannels (); for (Osganimation::channellist::iterator iter = Channels.begin (); ITER! = Channels.end (); ++iter) {Osganimation::t
Arget *target = (*iter)->gettarget (); Target->reset ();
} const Osg::framestamp *FS = Nv->getframestamp ();
_animation->update (Fs->getsimulationtime ());
} Traverse (node, NV);
} protected:osganimation::animation* _animation;
};
int main () {Osg::geode *node = new Osg::geode;
Node->adddrawable (New osg::shapedrawable (New Osg::sphere (OSG::VEC3 (0, 0, 0), 1));
Create an animated channel and insert a keyframe osganimation::vec3linearchannel *ch1 = new Osganimation::vec3linearchannel;
Ch1->setname ("position");
Ch1->settargetname ("Pathcallback"); Ch1->getorcreatesampler ()->getorcreatekeyframecontainer ()->push_back (osganimation::vec3keyframe (0, OSG
:: VEC3 (0,0,0)); Ch1->getorcreatesampler ()->getorcreatekeyframecontainer ()->push_back (Osganimation::vec3keyframe (1, OSG
:: VEC3 (10,0,0));
Set the animation, note that weight must be set greater than 0, because in the source code will determine whether weight is greater than 0 osganimation::animation *anim = new Osganimation::animation;
Anim->setplaymode (osganimation::animation::P pong);
Anim->setstarttime (0.0);
Anim->addchannel (CH1);Anim->setweight (1.0f); Set the Action object, note that the name of our channel must be the same as the element name in the object array osganimation::updatematrixtransform *umt = new osganimation::
Updatematrixtransform;
Umt->setname ("Pathcallback");
Umt->getstackedtransforms (). push_back (New osganimation::stackedtranslateelement ("position")); If you want to link animation here, then you must ensure that the Utm->setname settings and Ch1 settargetname exactly the same//umt->animationupdatecallback::link (ANIM)
;
If the direct link channel does not need to set two name of the same umt->link (CH1); The Action object Updatematrixtransform must be set to MatrixTransform updatecallback//set to other types of nodes (including postionattitudetransform) will fail OSG::
MatrixTransform *mt = new Osg::matrixtransform;
Mt->addchild (node);
Mt->addupdatecallback (UMT);
Osg::group *root = new Osg::group;
Root->addchild (MT);
Root->setupdatecallback (New Playcallback (Anim));
Osgviewer::viewer Viewer;
Viewer.setscenedata (root);
Osgga::trackballmanipulator *trackball = new Osgga::trackballmanipulator; Trackball->sethomeposition (OSG::VEC3 (0, -50, 0), OSG::VEC3 (0, 0, 0), Osg::y_axiS);
Viewer.setcameramanipulator (trackball);
return Viewer.run (); }
Updatemorph
This class actually acts as a morph animation, often referred to as vertex-wise animation, and is another common three-dimensional animation representation. It records the coordinates and offsets of a series of vertex positions, and moves each vertex to a new position at each frame of the animation, creating a continuous, smooth motion effect.
The class is defined in Osganimation/morphgeometry and osganimation/morphgeometry.cpp, and corresponds to the class morphgeometry, which derives from the Geometry object OSG: Geometry, let's look at its implementation, where the deformed piece of code is as follows:
struct Updatevertex:public osg::D rawable::updatecallback
{
virtual void update (osg::nodevisitor*, OSG::D rawable* drw)
{
morphgeometry* geom = dynamic_cast<morphgeometry*> (DRW);
if (!GEOM)
return;
Geom->transformsoftwaremethod ();
}
};
Inherits the Updatecallback function of drawable, modifies vertices in each frame, and implements the calculation of vertices in the Transformsoftwaremethod function. The implementation of this function is a bit long, only the selected part
Base * 1-(sum of weights) + sum of (weight * target)
float baseweight = 0;
for (unsigned int i=0; i < _morphtargets.size (); i++)
{
Baseweight + = _morphtargets[i].getweight ();
}
Baseweight = 1-baseweight;
if (baseweight! = 0)
{
initialized = true;
for (unsigned int i=0; i < pos->size (); i++)
{
(*pos) [i] = _positionsource[i] * baseweight;
}
if (_morphnormals)
{for
(unsigned int i=0; i < normal->size (); i++)
{
(*normal) [I] = _ Normalsource[i] * baseweight;}}
}
You can see that the new vertex positions are calculated based on all the geometry that are added to the list and their corresponding weights.
This is the case where a primitive shape is defined, and then a lot of geometry is added to the array inside the Morphgeometry (via the Addmorphtarget function), each geometry pair should have a weight, The final shape is calculated based on the combination of all the added geometry and their weights.
Next we look at the implementation of Updatemorph: As with the previous updatematrixtransform, it is also inherited from the Nodecallback, then it is necessary to overload the operator () function:
void Updatemorph::operator () (osg::node* Node, osg::nodevisitor* nv) {if (NV && nv->getvisitortype () = = OSG
:: Nodevisitor::update_visitor) {osg::geode* Geode = dynamic_cast<osg::geode*> (node);
if (geode) {unsigned int numdrawables = Geode->getnumdrawables (); for (unsigned int i = 0; I! = Numdrawables; ++i) {osganimation::morphgeometry* morph = Dynami
C_cast<osganimation::morphgeometry*> (Geode->getdrawable (i)); if (morph) {//Update morph weights std::map<int, osg::ref_ptr
<osgAnimation::FloatTarget> >::iterator iter = _weighttargets.begin (); while (iter! = _weighttargets.end ()) {if (Iter->second->getvalue () > = 0) {morph->setweight (Iter->first, iter->second-≫getvalue ());
} ++iter;
}}}}} traverse (NODE,NV); }
From it is realized we can know Updatemorph
update callback that must be set to Geode(unlike Updatematrixtransform), and this Geode node must have the drawable of the Morphgeometry node. The function of Updatemorph is to update the weights in Morphgeometry to update the geometry weights in the list before the update callback enters Morphgeometry Drawable::updatecallback. This allows new weights to be computed for the new vertex position in the later drawable update callback.
Updatemorph Another notable area is the implementation of the link function:
bool Updatemorph::link (osganimation::channel* Channel) {//typically morph geometries only the
Weights for Morph targets animated Std::istringstream ISS (Channel->getname ());
int weightindex;
ISS >> Weightindex;
if (Iss.fail ()) {return false;
} if (Weightindex >= 0) {osganimation::floattarget* ft = _weighttargets[weightindex].get ();
if (!ft) {ft = new Osganimation::floattarget;
_weighttargets[weightindex] = ft;
} return Channel->settarget (ft); } else {Osg_warn << Channel << channel->getname () << "does not contain a valid s
Ymbolic name for this class "<< Std::endl;
} return false; }
When linking channels in link, you need the name of the channel to create an index value, that is, our channel name must be set to "0", "1", "2" such as an integer string, set the channel name and the order in which they are added in Morphgeometry is consistent. This is somewhat similar to Updatematrixtransform, where the name of the element (Stackedtransform) in Updatematrixtransform is the same as the channel.
Examples of updatemorph reference osganimationmorph example updatematerial
Updatematerial can dynamically change the material of the object, it inherits from the Osg::stateattributecallback, we look at its implementation:
void Updatematerial::operator () (osg::stateattribute* SA, osg::nodevisitor* nv)
{
if (NV && nv-> Getvisitortype () = = Osg::nodevisitor::update_visitor)
{
osg::material* Material = dynamic_cast<osg:: Material*> (SA);
if (material)
update (*material);
}
}
As you can see for updatematerial, we have to set it to the update callback for the material object, and the update is implemented as follows:
osganimation::vec4target* Updatematerial::getdiffuse () {return _diffuse.get ();}
void Updatematerial::update (osg::material& Material)
{
Osg::vec4 diffuse = _diffuse->getvalue ();
Material.setdiffuse (osg::material::front_and_back, diffuse);
}
It's very simple to set up a material. Here's a look at what's special about the link channel:
BOOL Updatematerial::link (osganimation::channel* Channel)
{
if (channel->getname (). Find ("diffuse")! = Std::string::npos)
{
return Channel->settarget (_diffuse.get ());
}
else
{
Osg_warn << Channel << channel->getname () << "does not contain a valid symbolic Name for this class "<< className () << Std::endl;
}
return false;
}
See, the name of the channel must contain "diffuse" words can be correctly linked successfully, the entire implementation process is very concise, the following we use this class:
#include <osg/Geode> #include <osgViewer/Viewer> #include <osgViewer/ViewerEventHandlers> #include <osgGA/TrackballManipulator> #include <osgGA/StateSetManipulator> #include <osg/ShapeDrawable> # Include <osgAnimation/UpdateMaterial> #include <osgAnimation/BasicAnimationManager> #include <osgdb/
readfile> int main (int argc, char* argv[]) {osgviewer::viewer Viewer;
Osg::geode *boxgeode = new Osg::geode;
Boxgeode->adddrawable (New osg::shapedrawable (New Osg::box (OSG::VEC3 (0, 0, 0), 2));
osganimation::animation* Animation = new Osganimation::animation;
Osganimation::vec4linearchannel * channel0 = new Osganimation::vec4linearchannel; Channel0->getorcreatesampler ()->getorcreatekeyframecontainer ()->push_back (OsgAnimation::Vec4Keyframe (
0,OSG::VEC4 (1.0, 0.0, 0.0, 1.0))); Channel0->getorcreatesampler ()->getorcreatekeyframecontainer ()->push_back (OsgAnimation::Vec4Keyframe ( 4,OSG::VEC4 (0.0, 0.0, 1.0, 1.0)));
Set the Action object name, and the name of the updatematerial must be set to the same channel0->settargetname ("Updatematerialcallback");
The name of the channel must contain the "diffuse" character channel0->setname ("Diffusechannel");
Animation->addchannel (channel0);
Animation->setplaymode (osganimation::animation::P pong);
osganimation::basicanimationmanager* bam = new Osganimation::basicanimationmanager;
Bam->registeranimation (animation);
osganimation::updatematerial* updatematerial = new Osganimation::updatematerial ("Updatematerialcallback");
Osg::material *boxmaterial = new Osg::material;
Add updatematerial to material object in the update callback Boxmaterial->setupdatecallback (updatematerial);
Boxmaterial->setdiffuse (Osg::material::front_and_back, osg::vec4 (0, 0, 0, 1));
Boxgeode->getorcreatestateset ()->setattribute (boxmaterial, Osg::stateattribute::on);
Viewer.setcameramanipulator (New Osgga::trackballmanipulator ());
osg::group* scene = new Osg::group;
Scene->addupdatecallback (BAM); Scene->addchild (Boxgeode);
Viewer.addeventhandler (New Osgviewer::statshandler ());
Viewer.addeventhandler (New Osgviewer::windowsizehandler ());
Viewer.addeventhandler (New Osgga::statesetmanipulator (Viewer.getcamera ()->getorcreatestateset ()));
Viewer.setscenedata (Scene);
Viewer.realize ();
Bam->playanimation (animation);
while (!viewer.done ()) {viewer.frame ();
} return 0;
}
After running, you will see a cube constantly modifying the material, which is similar to Updatematrixtransform and Updatemorph, except that the update callback settings are in different locations and the channel name settings are different.
Summary
From the above analysis, the principle of the object animationupdatecallback is very similar, the use of the process is basically a similar process. Three common places:
1. All need to match the animation manager, the animation manager is set on the parent node;
2. The name of the channel has some settings that need attention
The difference:
1. Update callbacks for three different types of nodes
2. The key frame types in the corresponding channel are different (updatematrixtransform most flexible, updatemorph is float type, updatematerial is OSG::VEC4 type)