osganimationsolid示例主要使用了osgAnimation中的动画管理器来管理多个动画Animtion,这其中还涉及到一些其他的概念,比如动画中包含了一个或者多个频道Channel,频道设置作用对象setTargetName等等。
osg::ref_ptr<osg::Geode> createAxis() { osg::ref_ptr<osg::Geode> geode (new osg::Geode()); osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry()); //设置三个坐标轴线坐标值 osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array()); vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); vertices->push_back (osg::Vec3 ( 10.0, 0.0, 0.0)); vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); vertices->push_back (osg::Vec3 ( 0.0, 10.0, 0.0)); vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); vertices->push_back (osg::Vec3 ( 0.0, 0.0, 10.0)); geometry->setVertexArray (vertices.get()); //设置三个坐标轴线的颜色 osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array()); colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); geometry->setColorArray (colors.get(), osg::Array::BIND_PER_VERTEX); geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,6)); geode->addDrawable( geometry.get() ); //关闭光照 geode->getOrCreateStateSet()->setMode(GL_LIGHTING, false); return geode; }创建坐标系的代码很简单,设置顶点、颜色数组并以线段(GL_LINES)的方式绘制
osgAnimation::UpdateMatrixTransform* updatecb = new osgAnimation::UpdateMatrixTransform("AnimatedCallback"); //设置两个变换平移和旋转 updatecb->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("osition")); updatecb->getStackedTransforms().push_back(new osgAnimation::StackedRotateAxisElement("euler",osg::Vec3(1,0,0),0)); trans->setUpdateCallback(updatecb);创建作用对象,设置它的名称必须与Animation中Channel的名字相对应,也就是设置Channel的setTargetName和UpdateMatrixTransform的名称一致,设置Channel中的Name和UpdateMatrixTransform中数组元素的名称一致,也就是设置Channel的名称为"osition"和"euler",这样才能正确创建执行对象。
osgAnimation::Vec3LinearChannel* channelAnimation1 = new osgAnimation::Vec3LinearChannel; channelAnimation1->setTargetName("AnimatedCallback"); channelAnimation1->setName("osition"); channelAnimation1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0,0,0))); channelAnimation1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(1,1,0))); osgAnimation::Animation* anim1 = new osgAnimation::Animation; anim1->addChannel(channelAnimation1); anim1->setPlayMode(osgAnimation::Animation::PPONG); osgAnimation::FloatLinearChannel* channelAnimation2 = new osgAnimation::FloatLinearChannel; channelAnimation2->setTargetName("AnimatedCallback"); channelAnimation2->setName("euler"); channelAnimation2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(0, 0)); channelAnimation2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(1.5, 2*osg::PI)); osgAnimation::Animation* anim2 = new osgAnimation::Animation; anim2->addChannel(channelAnimation2); anim2->setPlayMode(osgAnimation::Animation::LOOP);动画频道创建它的TargetName设置和作用对象一致,另外它的Name也需要和作用对象数组中元素的名称一致
osg::Group* grp = new osg::Group; //BasicAnimationManager必须设置为需要动画子场景(在这个例子中是立方体)的父节点 //因为BasicAnimationManager会遍历它的子节点并设置子节点中的执行对象Target osgAnimation::BasicAnimationManager* mng = new osgAnimation::BasicAnimationManager(); grp->setUpdateCallback(mng); grp->addChild(root);管理器必须设置为需要动画场景的父节点的UpdateCallback
最后注册动画、播放动画,代码如下:
mng->registerAnimation(anim1); mng->registerAnimation(anim2); mng->playAnimation(anim1); mng->playAnimation(anim2);
updatecb->getStackedTransforms().push_back(new osgAnimation::StackedRotateAxisElement("euler",osg::Vec3(1,0,0),0)); updatecb->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("osition"));运行之后发现立方体运行的轨迹完全变换了,这是什么原因呢?从字面意思看StackedTransform,也就是类似于栈一样LIFO(后进先出)的模式,因此如果是例子中先平移后旋转,那么更新矩阵就等于M = Mr(旋转) * Mt(平移),而修改之后就变成先平移之后旋转,M=Mt(平移) * Mr(旋转),矩阵没有交换律,因此计算的结果就大相径庭了。