1、动画的实现方法:
要点1)、通过设置回调函数使得节点按路径移动。这个路径回调只能是添加到osg::PositionAttitudeTransform(位置变换节点)或者osg::MatrixTransform(矩阵变换节点)等节点上面才会有效,因为路径动画的实现原理就是改变节点的位置和旋转角度。
要点2)、动画的实体节点作为子节点添加到添加了路径动画回调的那个节点上面。
给节点添加回调函数
// 位置节点,用来添加动画路径(用一个旋转节点也行)
osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
//创建路径
osg::ref_ptr<osg::AnimationPath> animationPath = new osg::AnimationPath();
animationPath = createAnimationPath(ptStart, ptEnd, timeRange.GetStartTime(), timeRange.GetEndTime()) ;// 创建一个路径
//设置更新回调
pat->setUpdateCallback(new osg::AnimationPathCallback(animationPath.get(),0.0f,1.0f));
我们在来看看AnimationPathCallback::update函数里面做了些什么
bool AnimationPath::getInterpolatedControlPoint(double time,ControlPoint& controlPoint) const// 根据时间计算出该时间对应的位置和旋转量
{
if (_timeControlPointMap.empty()) return false;
switch(_loopMode)
{
case(SWING):
{
double modulated_time = (time - getFirstTime())/(getPeriod()*2.0);
double fraction_part = modulated_time - floor(modulated_time);
if (fraction_part>0.5) fraction_part = 1.0-fraction_part;
time = getFirstTime()+(fraction_part*2.0) * getPeriod();
break;
}
case(LOOP):
{
double modulated_time = (time - getFirstTime())/getPeriod();
double fraction_part = modulated_time - floor(modulated_time);
time = getFirstTime()+fraction_part * getPeriod();
break;
}
case(NO_LOOPING):
// no need to modulate the time.
break;
}
TimeControlPointMap::const_iterator second = _timeControlPointMap.lower_bound(time);
if (second==_timeControlPointMap.begin())
{
controlPoint = second->second;
}
else if (second!=_timeControlPointMap.end())
{
TimeControlPointMap::const_iterator first = second;
--first;
// we have both a lower bound and the next item.
// delta_time = second.time - first.time
double delta_time = second->first - first->first;
if (delta_time==0.0)
controlPoint = first->second;
else
{
controlPoint.interpolate((time - first->first)/delta_time,
first->second,
second->second);
}
}
else // (second==_timeControlPointMap.end())
{
controlPoint = _timeControlPointMap.rbegin()->second;
}
return true;
}
double AnimationPathCallback::getAnimationTime() const
{
return ((_latestTime-_firstTime)-_timeOffset)*_timeMultiplier;// 获得对应时间
}
void AnimationPathCallback::update(osg::Node& node)
{
AnimationPath::ControlPoint cp;
if (_animationPath->getInterpolatedControlPoint(getAnimationTime(),cp))
{
AnimationPathCallbackVisitor apcv(cp,_pivotPoint,_useInverseMatrix);
node.accept(apcv);
}
}
其它AnimationPathCallback相关具体代码参考osg源代码文件
反正就是在某个时间会更新它的_latestTime,然后调用update这个就获得这个时间的位置和旋转量,然后将其设置到对应节点上。
下面给出我写的直线路径的计算函数(起始时间在起点,到终止时间移动到终点)
//创建路径
osg::ref_ptr<osg::AnimationPath> createAnimationPath(osg::Vec3& ptStart,osg::Vec3 &ptEnd,float timeBegin, float timeEnd)
{
//创建一个Path对象
osg::ref_ptr<osg::AnimationPath> animationPath = new osg::AnimationPath() ;
//设置动画模式为循环(LOOP)(LOOP:循环,SWING:单摆,NO_LOOPING:不循环)
animationPath->setLoopMode(osg::AnimationPath::NO_LOOPING) ;
//插入Path,把关键点与时间压入形成Path
animationPath->insert(timeBegin,osg::AnimationPath::ControlPoint(ptStart));
animationPath->insert(timeEnd,osg::AnimationPath::ControlPoint(ptEnd));
//返回Path
return animationPath.get() ;
}
这个是最简单的路径,没有旋转,而且只有两个路径数据值。