一 OGRE支持的四种动画

Ogre默认支持四种形式的动画:1.骨骼动画 2.节点动画 3.顶点动画 4.数值动画

其中骨骼动画也是节点动画,因为bone继承于node

1.骨骼动画 :

骨骼动画的基础:在ogre中骨骼动画主要涉及两个类,mesh和skeleton,skeleton由许多Bone组成,这些bone之间有层级关系,skeleton就是一个骨架关系,某个mesh可以被绑定在这个skeleton上,绑定的实质就是某个vertex受某些bone的影响,也就是某个bone运动,就会导致和他有关系的那些vertex运动。所以对照本页最上面的图,他最末端控制的店就是skeleton中的每个bone,一个skeleton中可以蕴含多套animation。

一般情况下骨骼动画都是在maya或3Dmax之类的工具里面制作好的,然后通过导出方式,导出到一个mesh文件和一个skeleton文件。ogre在加载模型的时候已经把这些动画加载好,需要直接使用就行了。

AnimationState* as=entity->getAnimationState("动画名字");

动态创作骨骼动画:在另外一些情况下,我们只是在skeleton文件中设置网格顶点与骨骼的绑定关系,而不描述骨骼的运动轨迹。运动轨迹会动态的根据实际情况生成,这对于物理碰撞检测后的动画效果是很常见的。

//1.把skeleton和mesh文件内容加载到entity,此时绑定了骨骼与顶点但是没有实现动画效果...
//获取entity中的相应骨骼
SkeletonInstance* ske=entiy->getSkeleton();
//为骨骼添加一个动画
Animation* anim=ske->createAnimation("My",1);
//为每一块骨头创建track(动作集)
Bone* b0=ske->getRootBone();
NodeAnimationTrack* t0=anim->createNodeTrack(0,b0);//例如这是为root bone创建
//为每个bone的track创建关键帧...
//更新这个新加入的动画
entity->refreshAvailableAnimationState();

2.场景节点动画:

是指让场景中的某些结点运动,如创建camera的动画等。

// 创建Animation
Animation* anim=smgr->createAnimation();
// 设置插值方式:线性的和样条线(样条线的更加平滑)
anim->setInterpolationMode(Animation::IM_SPLINE);
//为需要动画的节点创建一个Track,节点动画是以节点为单位
NodeAnimationTrack* t0=anim->createNodeTrack(0,node);
//对于每个track创建它的每个关键帧
TransformKeyFrame* k_t0_0=t0->createNodeKeyFrame(0);//设置该帧的时间点
k_t0_0->setTranslate(Ogre::Vector3(0,0,0));//设置该帧处这个结点的位置和其他几何状态
TransformKeyFrame* k_t0_1=t0->createNodeKeyFrame(1);
k_t0_1->setTranslate(Ogre::Vector3(100,0,0));
//创建一个AnimationState用于控制动画
AnimationState* as=smgr->createAnimationState(该动画名字);
as->setEnabled(true); //必须设置为true才能被播放
as->setTimePosition();//在帧监听中通过调用这个函数来播放动画

3.顶点动画:

顶点动画大多情况下利用gpu shader来实现以提高效率,所以较少直接使用ogre的动画机制。

4.数值动画:我的理解是跟传统的一副一副图片组成的动画类似,这种方式效率低下,游戏肯定是不会用到的。

二 详解Animation AnimationTrack AnimationStates AnimationStateSet KeyFrame

在说API之前先了解一下动画的原理:以一个人行走为例,要想使人能够行走,必须在这个人模型上设置一些特真点,然后让这些点在每一帧按照一定的规则进行动,从而改变整个模型的状态。

在ogre里面,Keyframe类描述的是一个特征点(一个结点、一根骨头、一个顶点)的某一个关键帧的状态(位置、缩放、朝向等)和对应的时间点。

一个可驱动点的所有keyframe组合成一个track,每个可驱动点都有一个他的track,这就好比某个点在整段动画中的轨迹,其中keyframe要由track来创建。

多个track组合在一起就成为了一段动画,用Animation类来表示,这就是一个动画,例如对于骨骼动画,他的每一个骨头都有一个track,那么所有的骨头的track的组合在一起也就是整个骨骼动画了。track由Animation来创建。Animation则可以通过skeleton或者scenenmanager等来创建。

AnimationSate类:通常我们操控一段动画的播放等都不是直接操纵animation

类,而是通过一个类AnimationState,它是animation的一个实例,通过场景管理器创建某个animation的一个animationState,然后就可以利用这个animationstate来播放这个animation了。

AnimationState :这个来描述了一些动画的状态,用于控制动画的播放。

BoneBlendMask* mBlendMask;   // ???
String mAnimationName; //Animation名字
AnimationStateSet* mParent; // AnimationState 的管理类
Real mTimePos; // 时间点
Real mLength;  // 时间长度
Real mWeight;  // 骨骼的权重
bool mEnabled; // 是否播放
bool mLoop;   // 是否循环播放

AnimationState只是控制动画的状态,其并不具备播放功能。另外,AnimationStateSet作为一个容器,不仅存储着AnimationState 而且维护着AnimationState 的状态变更,一旦AnimationState 的某个属性值被改变,AnimationStateSet维护的状态就会变脏。

Animation :

1.插值模式:

enum InterpolationMode{ // 插值状态
IM_LINEAR,  // 线性插值
IM_SPLINE   // 样条插值(较好)
};
enum RotationInterpolationMode{ //现状插值状态
RIM_LINEAR,  // 线性插值
RIM_SPHERICAL // 样条插值
};

2.三种track:

ogre实现了四种动画(1.骨骼动画 2.场景节点动画 3.顶点动画 4.数值动画),在Animation类里面管理了三种track(NumericAnimationTrack,NodeAnimationTrack,VertexAnimationTrack)他们继承于AnimationTrack。

NodeTrackList mNodeTrackList; // 一个<unsigned short,NodeAnimationTrack*>map
NumericTrackList mNumericTrackList; // 一个<unsigned short,NumericAnimationTrack*>
VertexTrackList mVertexTrackList; // 一个<unsigned short,VertexAnimationTrack*>

3.播放动画:

动画播放主要用到的方法是:apply,这个方法有好几个重载的方法,其中一个是播放所有的动画,其它的几个都是播放某个节点的方法。
//播放所有动画
void Animation::apply(Real timePos, Real weight, Real scale)
{
_applyBaseKeyFrame();
// Calculate time index for fast keyframe search
TimeIndex timeIndex = _getTimeIndex(timePos);
NodeTrackList::iterator i;
for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
{
i->second->apply(timeIndex, weight, scale);
}
NumericTrackList::iterator j;
for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j)
{
j->second->apply(timeIndex, weight, scale);
}
VertexTrackList::iterator k;
for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k)
{
k->second->apply(timeIndex, weight, scale);
}
}
//播放当前Skeleton的动画
void Animation::apply(Skeleton* skel, Real timePos, float weight,
const AnimationState::BoneBlendMask* blendMask, Real scale)
{
_applyBaseKeyFrame();
// Calculate time index for fast keyframe search
TimeIndex timeIndex = _getTimeIndex(timePos);
NodeTrackList::iterator i;
for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i)
{
// get bone to apply to
Bone* b = skel->getBone(i->first);
i->second->applyToNode(b, timeIndex, (*blendMask)[b->getHandle()] * weight, scale);
}
}

AnimationTrack :动画轨迹

KeyFrameList mKeyFrames;
KeyFrameIndexMap mKeyFrameIndexMap;

AnimationTrack 除了管理KeyFrame以外,还有几个有用的方法,只不过现在不了解,就先不贴出来分析了。

1.NumericAnimationTrack:

void NumericAnimationTrack::applyToAnimable(const AnimableValuePtr& anim, const TimeIndex& timeIndex,
Real weight, Real scale)
{
// Nothing to do if no keyframes or zero weight, scale
if (mKeyFrames.empty() || !weight || !scale)
return;
NumericKeyFrame kf(0, timeIndex.getTimePos());
getInterpolatedKeyFrame(timeIndex, &kf);
// add to existing. Weights are not relative, but treated as
// absolute multipliers for the animation
AnyNumeric val = kf.getValue() * (weight * scale);
anim->applyDeltaValue(val);
}

最终动画是由AnimableValue对象播放的。但是,我看了一下AnimableValue的applyDeltaValue方法,都没有被实现,只是抛了一个异常,这是否意味着我们在使用的时候需要自己实现一个AnimableValue类,由于这种动画并不是需要重点关注的,暂时不往下继续了。

2.VertexAnimationTrack :顶点动画:

enum VertexAnimationType  // 顶点动画类型
{
VAT_NONE = 0,    // 没有动画
VAT_MORPH = 1,  // 形变动画
VAT_POSE = 2     // 姿态动画
};

顶点的轨迹可能有两种产生方式,所以在播放的时候也有两种方式:

void VertexAnimationTrack::applyToVertexData(VertexData* data,
const TimeIndex& timeIndex, Real weight, const PoseList* poseList)
{
// Nothing to do if no keyframes or no vertex data
if (mKeyFrames.empty() || !data)
return;
// Get keyframes
KeyFrame *kf1, *kf2;
Real t = getKeyFramesAtTime(timeIndex, &kf1, &kf2);
if (mAnimationType == VAT_MORPH)
{
VertexMorphKeyFrame* vkf1 = static_cast<VertexMorphKeyFrame*>(kf1);
VertexMorphKeyFrame* vkf2 = static_cast<VertexMorphKeyFrame*>(kf2);
if (mTargetMode == TM_HARDWARE)
{
// If target mode is hardware, need to bind our 2 keyframe buffers,
// one to main pos, one to morph target texcoord
assert(!data->hwAnimationDataList.empty() &&
"Haven't set up hardware vertex animation elements!");
// no use for TempBlendedBufferInfo here btw
// NB we assume that position buffer is unshared, except for normals
// VertexDeclaration::getAutoOrganisedDeclaration should see to that
const VertexElement* posElem =
data->vertexDeclaration->findElementBySemantic(VES_POSITION);
// Set keyframe1 data as original position
data->vertexBufferBinding->setBinding(
posElem->getSource(), vkf1->getVertexBuffer());
// Set keyframe2 data as derived
data->vertexBufferBinding->setBinding(
data->hwAnimationDataList[0].targetBufferIndex,
vkf2->getVertexBuffer());
// save T for use later
data->hwAnimationDataList[0].parametric = t;
}
else
{
// If target mode is software, need to software interpolate each vertex
Mesh::softwareVertexMorph(
t, vkf1->getVertexBuffer(), vkf2->getVertexBuffer(), data);
}
}
else
{
// Pose
VertexPoseKeyFrame* vkf1 = static_cast<VertexPoseKeyFrame*>(kf1);
VertexPoseKeyFrame* vkf2 = static_cast<VertexPoseKeyFrame*>(kf2);
// For each pose reference in key 1, we need to locate the entry in
// key 2 and interpolate the influence
const VertexPoseKeyFrame::PoseRefList& poseList1 = vkf1->getPoseReferences();
const VertexPoseKeyFrame::PoseRefList& poseList2 = vkf2->getPoseReferences();
for (VertexPoseKeyFrame::PoseRefList::const_iterator p1 = poseList1.begin();
p1 != poseList1.end(); ++p1)
{
Real startInfluence = p1->influence;
Real endInfluence = 0;
// Search for entry in keyframe 2 list (if not there, will be 0)
for (VertexPoseKeyFrame::PoseRefList::const_iterator p2 = poseList2.begin();
p2 != poseList2.end(); ++p2)
{
if (p1->poseIndex == p2->poseIndex)
{
endInfluence = p2->influence;
break;
}
}
// Interpolate influence
Real influence = startInfluence + t*(endInfluence - startInfluence);
// Scale by animation weight
influence = weight * influence;
// Get pose
assert (p1->poseIndex < poseList->size());
Pose* pose = (*poseList)[p1->poseIndex];
// apply
applyPoseToVertexData(pose, data, influence);
}
// Now deal with any poses in key 2 which are not in key 1
for (VertexPoseKeyFrame::PoseRefList::const_iterator p2 = poseList2.begin();
p2 != poseList2.end(); ++p2)
{
bool found = false;
for (VertexPoseKeyFrame::PoseRefList::const_iterator p1 = poseList1.begin();
p1 != poseList1.end(); ++p1)
{
if (p1->poseIndex == p2->poseIndex)
{
found = true;
break;
}
}
if (!found)
{
// Need to apply this pose too, scaled from 0 start
Real influence = t * p2->influence;
// Scale by animation weight
influence = weight * influence;
// Get pose
assert (p2->poseIndex <= poseList->size());
const Pose* pose = (*poseList)[p2->poseIndex];
// apply
applyPoseToVertexData(pose, data, influence);
}
} // key 2 iteration
} // morph or pose animation
}

3.NodeAnimationTrack :

void NodeAnimationTrack::applyToNode(Node* node, const TimeIndex& timeIndex, Real weight,
Real scl)
{
// Nothing to do if no keyframes or zero weight or no node
if (mKeyFrames.empty() || !weight || !node)
return;
TransformKeyFrame kf(0, timeIndex.getTimePos());
getInterpolatedKeyFrame(timeIndex, &kf);
// add to existing. Weights are not relative, but treated as absolute multipliers for the animation
Vector3 translate = kf.getTranslate() * weight * scl;
node->translate(translate);
// interpolate between no-rotation and full rotation, to point 'weight', so 0 = no rotate, 1 = full
Quaternion rotate;
Animation::RotationInterpolationMode rim =
mParent->getRotationInterpolationMode();
if (rim == Animation::RIM_LINEAR)
{
rotate = Quaternion::nlerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath);
}
else //if (rim == Animation::RIM_SPHERICAL)
{
rotate = Quaternion::Slerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath);
}
node->rotate(rotate);
Vector3 scale = kf.getScale();
// Not sure how to modify scale for cumulative anims... leave it alone
//scale = ((Vector3::UNIT_SCALE - kf.getScale()) * weight) + Vector3::UNIT_SCALE;
if (scale != Vector3::UNIT_SCALE)
{
if (scl != 1.0f)
scale = Vector3::UNIT_SCALE + (scale - Vector3::UNIT_SCALE) * scl;
else if (weight != 1.0f)
scale = Vector3::UNIT_SCALE + (scale - Vector3::UNIT_SCALE) * weight;
}
node->scale(scale);
}

KeyFrame :KeyFrame 的内容比较简单,所有关键的东西都在Track里面处理了

NumericKeyFrame          // KeyFrame
TransformKeyFrame       // KeyFrame
VertexMorphKeyFrame  //  形变动画的KeyFrame
VertexPoseKeyFrame    //  姿态动画的KeyFrame

总结:对于整个ogre的动画体系来说,Animation 是动画的管理类,而AnimationState 是动画状态的控制类。Animation 的动画播放其实是调用其相应的track类来播放的,KeyFrame 更多的是起到数据单元的作用。所以,完全弄清楚四种动画的底层实现,重点需要关注的是track类,这里没有写多少因为我自己也不太清楚,后面在写例子的时候,再从使用者的角度详细描述一下ogre的动画的底部执行过程。

三 SceneManager 对动画的管理及播放:

1.动画对象的管理:

AnimationList mAnimationsList;
AnimationStateSet mAnimationStates;
Animation* SceneManager::createAnimation(const String& name, Real length)
{
OGRE_LOCK_MUTEX(mAnimationsListMutex)
if (mAnimationsList.find(name) != mAnimationsList.end())
{
OGRE_EXCEPT(
Exception::ERR_DUPLICATE_ITEM,
"An animation with the name " + name + " already exists",
"SceneManager::createAnimation" );
}
Animation* pAnim = OGRE_NEW Animation(name, length);
mAnimationsList[name] = pAnim;
return pAnim;
}
AnimationState* SceneManager::createAnimationState(const String& animName)
{
Animation* anim = getAnimation(animName);//如果不存在就会抛异常
return mAnimationStates.createAnimationState(animName, 0, anim->getLength());
}

在SceneManager里面使用Animation时会把Animation保存在mAnimationsList里面,AnimationState保存在mAnimationStates里面,他们通过animName来保持一一对应。生命周期:Animation创建-->AnimationState创建-->AnimationState销毁-->Animation销毁。如果不是按照这种顺序会报异常。

2.动画播放:

void SceneManager::_applySceneAnimations(void)
{
// manual lock over states (extended duration required)
OGRE_LOCK_MUTEX(mAnimationStates.OGRE_AUTO_MUTEX_NAME)
// Iterate twice, once to reset, once to apply, to allow blending
ConstEnabledAnimationStateIterator stateIt = mAnimationStates.getEnabledAnimationStateIterator();
while (stateIt.hasMoreElements())
{
const AnimationState* state = stateIt.getNext();
Animation* anim = getAnimation(state->getAnimationName());
// Reset any nodes involved
Animation::NodeTrackIterator nodeTrackIt = anim->getNodeTrackIterator();
while(nodeTrackIt.hasMoreElements())
{
Node* nd = nodeTrackIt.getNext()->getAssociatedNode();
if (nd)
nd->resetToInitialState();
}
Animation::NumericTrackIterator numTrackIt = anim->getNumericTrackIterator();
while(numTrackIt.hasMoreElements())
{
const AnimableValuePtr& animPtr = numTrackIt.getNext()->getAssociatedAnimable();
if (!animPtr.isNull())
animPtr->resetToBaseValue();
}
}
// this should allow blended animations
stateIt = mAnimationStates.getEnabledAnimationStateIterator();
while (stateIt.hasMoreElements())
{
const AnimationState* state = stateIt.getNext();
Animation* anim = getAnimation(state->getAnimationName());
// Apply the animation
anim->apply(state->getTimePosition(), state->getWeight());
}
}

遍历所有Enabled为true的动画状态,调用node的resetToInitialState()和AnimableValue的resetToBaseValue()方法,分辨对节点动画和数组动画进行reset,最后调用Animation的apply方法进行播放动画。

OGRE ANIMATION相关推荐

  1. Ogre骨骼动画分析

    http://3dlearn.googlecode.com/files/ogre skeleton animation.pdf 欢迎指出文中错误 1  前言 骨骼蒙皮动画分两步骤进行:根据时间插值更新 ...

  2. 【Ogre-windows】旋转矩阵及位置解析

    前言 这篇博客主要针对三种问题 如何创建动画帧 如何获取全局位置 如何计算全局旋转矩阵 仿真环境为VS2013+Ogre1.10.9与matlab验证 创建动画帧 这里只做一个简单的实验: 将自带的人 ...

  3. OGRE 1.8 Animation ,动画部分

    本系列文章认为你懂得C++ stl的设计概念和设计模式,当然c++你要是压根不会就直接闪人吧.懂点boost最好,Ogre也用了这个.计算机图形学怎么说也得懂点吧?要不然四元数都够你郁闷的了.mesh ...

  4. Ogre 2011-11-29

    添加R按键改变绘制模式,播放动画 class Example25FrameListener : public Ogre::FrameListener { public:Example25FrameLi ...

  5. 《Pro Ogre 3D Programming》读书笔记 之 第十章 布告板与粒子 第一部分 (转)

    布告板(Billboard) Ogre中的布告板简单的来讲就是场景中的一个四边形,但它的朝向与相机有关.通常布告板随着相机的视方向旋转以便与相机的视线方向对齐.把布告板放在场景的任何地方,它将会朝向相 ...

  6. Ogre wiki 中级教程1 动画,点之间行走及四元数的基本应用

    引言 在本教程中,我们将介绍如何让一个实体以动画的方式在预先定义的点之间行走.此外还将通过展示一个如何让实体朝向它所移动的方向的例子来讲解四元数的基本应用.在创作这个demo的过程中你将慢慢把代码添加 ...

  7. Ogre共享骨骼与两种骨骼驱动方法

    前言 最近业务中用到Ogre做基于3D关键点虚拟角色骨骼驱动,但是遇到两个问题: 身体.头.眼睛.衣服等mesh的骨骼是分开的,但是骨骼结构都是一样的,需要设置共享骨骼 驱动的时候可以直接修改骨骼旋转 ...

  8. 开源3D图形渲染引擎OGRE学习笔记

    参考资料: 电子书:<Pro OGRE 3D Programming>中文翻译本 官方wiki: http://www.ogre3d.org/wiki/index.php/Main_Pag ...

  9. ogre 学习笔记 - Day 7

    ogre 学习笔记 - Day 7 [Sample_ParticleFX] 粒子效果 void setupParticles(){ParticleSystem::setDefaultNonVisibl ...

最新文章

  1. RubyGems 库发现了后门版本的网站开发工具 bootstrap-sass
  2. 总也学不会Linux命令行?这本新书大概率能拯救你!
  3. DeepMind发布最新原始音频波形深度生成模型WaveNet,将为TTS带来无数可能
  4. java中split特殊符号
  5. Pandas通过某列不是NaN来进行筛选
  6. kaggle中zillow比赛中模型融合的方法及其代码
  7. SpringCloud 阶段总结
  8. 手机锁屏js倒计时停止问题解决办法探索
  9. string的compare operator
  10. 米尔顿-艾瑞克森的催眠引导词
  11. Codeforces 964B(贪心)
  12. MathType工具栏在word里无法点击
  13. MyBatisPlus的代码生成器
  14. 最长公共子串 动态规划
  15. linux新增字符串,字符串添加字符_Linux使用sed命令添加字符串的方法
  16. PPT图片虚化效果要怎样实现?
  17. 估值3000亿,中国最神秘电商公司,征服美国年轻人
  18. 从最终用户角度来看外部结构_从不同角度来看您最喜欢的游戏
  19. 深度学习 | 误差反向传播法
  20. Java读写gif格式图片,解决ImageIO读取gif文件只读取第一帧的问题(read and write gif format pictures in Java)

热门文章

  1. 计算机冷启动和热启动的区别
  2. vue仿QQ聊天室|vue聊天实例,直播聊天室
  3. maxcompute操作_MaxCompute常用语句汇总(更新ing)
  4. 人工智能和机器学习如何影响金融服务?
  5. 蔬菜配送APP开发基本功能
  6. 操作系统实验二·生产者消费者问题
  7. 【Unity】多种方法实现第一人称角色移动(一)角色控制器
  8. 挖掘原理|k近邻法原理
  9. 解读数学问题自动求解领域的一篇论文A Goal-Driven Tree-Structured Neural Model for Math Word Problems以及论文的代码
  10. 位,字和字节的关系电脑知识