这个例子只分析一下流程,共三个步骤:
数据绑定
就是将要改变的数据与更新对象绑定起来,由更新对象负责“如何去改变数据”。
这个操作是在AnimationManagerBase
第一次被回调的时候发生的。
-
BasicAnimationManager
调用operator()
- 调用
link()
,将UpdateMorph
与所有的Animation
连接起来 -
link
函数中用LinkVisitor
遍历所有AnimationUpdateCallback
,在咱们这个例子里那就是UpdateMorph
- 在
link(Channel*)
中,会把TargetName对上的Channel挑出来,调用它的setTarget,从而将UpdateMorph
里需要动态改变的值交给Channel
看着很深,其实代码却很简单,没几行,就不详细解释了。
跟这个步骤相关的有一个小地方值得提一下,就是例子代码里的这句:
channel0->setName("0");
它到底是干什么的呢?答案就在UpdateMorph::update
里面:
bool UpdateMorph::link(osgAnimation::Channel* channel)
{
// Typically morph geometries only have 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 symbolic name for this class" << std::endl;
}
return false;
}
看到了么,那个“0”是作为索引用的。其实我觉得这里直接用string作为索引也不是不可以啊^_^ 也许字符串作为键的搜索效率不如int吧,哈哈。
数据更新
-
AnimationManagerBase::operator()
里调用update()
- 遍历所有的
Animation
,调用它们的update()
,如果已经更新完事了,那就把它从Animation列表中去掉(保证不再重新更新) - Animation中会根据自己的播放类型(单次,循环,来回……)算出一个时间,遍历所有的
Channel::update()
,把时间值传给Channel - 在Channel的update会根据时间算出一个插值结果,并将它设到Target中
注意这里提到了Target,没错!它就是数据绑定中绑在UpdateMorph
里的那个值。
到这儿就完成了UpdateMorph的数据更新。
模型更新
UpdateMorph::operator()
虽然代码一大坨,但只干了一件事:setWeight
。也就是将之前得到的插值值作为权值设到MorphGeometry里。
跳到MorphGeometry
里,会发现一个函数特别长:transformSoftwareMethod
,没错,它才是这个例子的重点。
先解释一下程序是怎么调用到这个函数的,其实也很简单,osg又利用了UpdateCallback机制完成了这个调用。
- 定义一个回调类:MorphGeometry::UpdateVertex
- 构造函数中将这个类设为回调类:
setUpdateCallback(new UpdateVertex)
- 回调类的update函数再调用
transformSoftwareMethod
函数看着很长很长(100多行呢。。),但其实只实现了一个公式:
result = src * (1-weight) + dst * weight;
然后……好像就没啥然后了……整个流程到这儿就完结了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。