Introduction
Osganimation is a class library that provides scene animations in the OSG library, which gives us many classes related to scene animations, such as keyframes, interpolation, sampling, channels, skeletal animation, material changes, and so on. This lesson makes some analysis of the underlying classes in the Osganimation library. The following are my personal learning process in some of the records and experience, to facilitate their own review of the use. Start Keyframe
corresponding file Osganimation/keyframe
To learn the Osganimation library, you first need to understand the meaning of keyframes. KeyFrames are, as the name implies, a state in the animation of a moment, just like making cartoons. We know that the early cartoons were drawn by the author on a page-by-page, and that we felt the dynamic effect by switching quickly, where keyframes were the equivalent of one of the pages. In Osganimation, the keyframes are defined as follows:
Class Keyframe
{public
:
double getTime () const {return _time;}
void SetTime (double time) {_time = time;}
Protected:
double _time;
};
It's easy, you might think: There's nothing at all here. Time should correspond to a content ah. Because the corresponding content is ever-changing (possibly motion position, color, angle, and so on), it is handled in a derived class in the form of a template, namely:
Template <class t>
class Templatekeyframe:public keyframe
{
protected:
T _value;
Public:
Templatekeyframe () {}
~templatekeyframe () {}
Templatekeyframe (double time, const t& value) c9/>{
_time = time;
_value = value;
}
void SetValue (const t& value) {_value = value;}
Const t& GetValue () const {return _value;}
};
Now that you have a value that corresponds to time, you also need to define a container to store the values for ease of management, namely: Keyframecontainer, which is also defined in the same way as the following:
Class Keyframecontainer:public osg::referenced
{public
:
Keyframecontainer () {}
virtual unsigned int size () const = 0;
Protected:
~keyframecontainer () {}
std::string _name;
};
Template <class t>
class Templatekeyframecontainer:public std::vector<templatekeyframe<t>; Public Keyframecontainer
{public
:
Templatekeyframecontainer () {}
typedef templatekeyframe<t > KeyType;
virtual unsigned int size () const {return (unsigned int) std::vector<templatekeyframe<t> >::size ();}
};
You can see that the container inherits from Std::vector, so that we can insert keyframes into the container using the Push_back method.
Interpolator
corresponding file Osganimation/interpolator
With keyframes, we need to calculate the values for the time between KeyFrames, which is called interpolation, and the base class for defining interpolation is as follows:
Template <class type, class Key> class Templateinterpolatorbase {public://key corresponds to Keyframe type
typedef KEY KEYFRAMETYPE;
The type corresponds to the value of the keyframe corresponding to the typedef type USINGTYPE;
public:mutable int _lastkeyaccess;
Templateinterpolatorbase (): _lastkeyaccess ( -1) {} void Reset () {_lastkeyaccess =-1;} The current index value is calculated by time,/that is, the time between two keyframes int getkeyindexfromtime (const templatekeyframecontainer<key>& KE
YS, double time) const {int key_size = Keys.size (); if (!key_size) {osg::notify (Osg::warn) << templateinterpolatorbase::getkeyindexfromtime the Contai
Ner is empty, impossible-get key index from time "<< Std::endl;
return-1;
} const templatekeyframe<keyframetype>* Keysvector = &keys.front (); for (int i = 0; i < key_size-1; i++) {Double TIME0 = keysvector[i].gettime ();
Double time1 = Keysvector[i+1].gettime ();
if (Time >= TIME0 && time < time1) {_lastkeyaccess = i;
return i;
}} return-1; }
};
This base class is responsible for finding the two frames needed for interpolation, that is, the two keyframes closest to the time it takes to interpolate a value. Subsequent interpolation is relatively straightforward, and several interpolation methods are defined in OSG:
Template <class TYPE, class key=type>
class Templatestepinterpolator:public templateinterpolatorbase< type,key>
{public
:
templatestepinterpolator () {}
void GetValue (const templatekeyframecontainer<key>& keyframes, double time, type& result) Const
{
if (time >= Keyframes.back (). GetTime ())
{
result = Keyframes.back (). GetValue ();
return;
}
else if (Time <= keyframes.front (). GetTime ())
{
result = Keyframes.front (). GetValue ();
return;
}
int i = This->getkeyindexfromtime (keyframes,time);
result = Keyframes[i].getvalue ();
}
};
Stepinterpolator directly finds the value of the frame closest to the time, plus linear (linear interpolation), sphericallinear (interpolation of the Sphere), Cubicbezier (Bezier interpolation)
Sampler
corresponding file Osganimation/samper
With the key frame and the processing keyframe interpolation algorithm, in the osganimation using the Sampler (sampler) way to combine the two, wherein the member function realization at a glance, are called the function in the interpolator:
F the argument that needs to be passed in is a Interpolator class template <class f> class Templatesampler:public Sampler {public:
Keyframetype key Frame type typedef typename F::KEYFRAMETYPE Keyframetype;
Keyframe container type typedef templatekeyframecontainer<keyframetype> KEYFRAMECONTAINERTYPE;
The type of the value computed by the keyframe typedef typename F::usingtype Usingtype;
typedef F FUNCTORTYPE; Templatesampler () {} ~templatesampler () {} void getValueAt (double time, usingtype& result) const {_f
Unctor.getvalue (*_keyframes, time, result);}
void Setkeyframecontainer (keyframecontainertype* KF) {_keyframes = KF;}
Virtual keyframecontainer* Getkeyframecontainer () {return _keyframes.get ();}
Virtual const keyframecontainer* Getkeyframecontainer () const {return _keyframes.get ();}
keyframecontainertype* getkeyframecontainertyped () {return _keyframes.get ();} Const keyframecontainertype* getkeyframecontainertyped () const { return _keyframes.get ();} Safely get a keyframe container, it is recommended to use this method in your program keyframecontainertype* Getorcreatekeyframecontainer () {if (_keyfra
Mes! = 0) return _keyframes.get ();
_keyframes = new Keyframecontainertype;
return _keyframes.get (); } double GetStartTime () const {if (!_keyframes | | _keyframes->empty ()) re
Turn 0.0;
Return _keyframes->front (). GetTime (); } double Getendtime () const {if (!_keyframes | | _keyframes->empty ()) Retu
RN 0.0;
Return _keyframes->back (). GetTime ();
} Protected:functortype _functor;
Osg::ref_ptr<keyframecontainertype> _keyframes; };
Here we can already apply the sampler to our program, for example: Define the update callback, pass in the keyframe parameter, calculate the value of each moment according to the keyframe (such as object gesture), and update it to achieve the animation effect. A higher package is also carried out in the Osganimation, the channel (the concept of the animated channel)
Channel
corresponding files Osganimation/channel and osganimation/channel.cpp
In a channel encapsulates the sampler sampler and the execution object target, the execution object can be understood as the sampler computed by the interpolation results stored in this object, look at the target implementation is as follows:
Template <class t> class templatetarget:public Target {public:inline void Lerp (float T, c
Onst t& A, const t& B);
TODO: How to explain? Here's what I understand://If multiple channel shares an execution object target, then//during call to update, the void update must be in the order of precedence (float weight, const t&am P Val, int priority) {if (_weight | | _priorityweight) {if (_lastpriority!
= priority) {_weight + = _priorityweight * (1.0-_weight);
_priorityweight = 0;
_lastpriority = priority;
} _priorityweight + = weight;
Float T = (1.0-_weight) * Weight/_priorityweight;
Lerp (T, _target, Val);
} else {_priorityweight = weight;
_lastpriority = priority;
_target = val; }} const t& GetValue () const {RETUrn _target;
} void SetValue (const t& value) {_target = value;}
Protected://Record The final result of T _target; };
In the channel implementation there is also an update member function, its implementation reflects the role of the channel, through the sampler to calculate the value, and then through the target object updates update, Finally, the computed structure is stored in the target object's member variable _target for later use. The code is as follows: Osganimation/channel
virtual void update (double time, float weight, int priority)
{
//skip if weight = = 0
if (weight < 1e-4)
return;
TypeName Samplertype::usingtype value;
_sampler->getvalueat (time, value); Gets the value of the sampler interpolation value
_target->update (weight, value, priority);//value is weighted and the result is saved in the target object
}
Animation
corresponding files Osganimation/animation and osganimation/animation.cpp
The class that finally integrates these channels is the animation class animation, the code is as follows:
Class Osganimation_export Animation:public osg::object {public:meta_object (osganimation, Animation ) Animation (): _duration (0), _weight (0), _starttime (0), _playmode (LOOP) {} Animation (const osganimation:
: Animation&, const osg::copyop&);
Enum PlayMode {ONCE, stay, LOOP, ppong};
void AddChannel (channel* pchannel);
channellist& getchannels ();
Const channellist& getchannels () const;
void Setduration (double duration);
void Computeduration ();
Double getduration () const;
void Setweight (float weight);
float getweight () const;
BOOL Update (double time, int priority = 0);
void Resettargets ();
void Setplaymode (PlayMode mode) {_playmode = mode;}
PlayMode Getplaymode () const {return _playmode;}
void Setstarttime (double time) {_starttime = time;} Double GetStartTime () const {return _starttime;}
Protected:double computedurationfromchannels () const;
~animation () {} double _duration;
Double _originalduration;
float _weight;
Double _starttime;
PlayMode _playmode;
Channellist _channels; };
It is easy to understand that many channel is integrated, and the process is implemented by invoking the member functions in the channel. The playback mode can be set in animation, and the mode of playback is actually calculated by these modes.
Switch (_playmode)
{case
ONCE:
if (T > _originalduration)
return false;
break;
Case STAY:
if (T > _originalduration)
t = _originalduration;
break;
Case LOOP:
if (!_originalduration)
t = _starttime;
else if (T > _originalduration)
t = fmod (t, _originalduration); std::cout << "T" << t << "duration" << _duration << Std::endl;
break;
Case Ppong:
if (!_originalduration)
t = _starttime;
else
{
int tt = (int) (t/_originalduration);
t = fmod (t, _originalduration);
if (tt%2)
t = _originalduration-t;
}
break;
}
The above is an introduction to the basic sections of the Osganimation library, followed by a record of how these classes are used to complete a complete animation in the actual operation.