- 浏览: 213409 次
- 性别:
- 来自: 上海
文章分类
最新评论
在说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.播放动画:
//播放所有动画 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的动画的底部执行过程。
发表评论
-
场景的管理 :Entity
2012-12-26 23:19 654Entity 的定位:在ogre里面Entity和诸多类似En ... -
场景的管理 :MovableObject
2012-12-26 12:56 483MovableObject :是可移动的对象,对象是挂接到节点 ... -
动画框架:SceneManager
2012-12-24 13:14 852SceneManager: 1.动画对象的管理: Anim ... -
场景的管理 :SceneManagerEnumerator
2012-12-21 23:36 740如果说root是需要关注的第一个类,那么Scene必须是 ... -
场景的管理 :RenderQueue
2012-12-21 17:45 972RenderQueue渲染队列是送给渲染系统渲染的最终结果集, ... -
场景的管理 :SceneManager
2012-12-20 03:08 790SceneManager是场景管理里面内容最多的一个类,不止是 ... -
场景的查询框架 :SceneQuery
2012-12-19 16:33 802构建一个场景一般都是比较复杂的,像这样一个复杂的场景我们是不会 ... -
场景的数据结构 :Node&Entity
2012-12-19 14:53 1070想了解Scene,了解其数据结构是必不可少的。Scene中有种 ... -
Root简述
2012-12-17 00:40 819ogre::root是ogre的入口类,关注它其实就是关注og ...
相关推荐
XAnimator一个动画框架,可根据ScrollView或HorizontalScrollView中的滚动距离设置相应子视图的动画效果。使用在存储库末尾将其添加到您的root build.gradle中: allprojects { repositories { //... maven { ...
短跑 JavaFX的动画框架,使动画变得异常简单。推荐指南我做了一个更好的指南 我强烈建议您改为去那里。 您也可以在查看安装和演示只需从此页面的发行部分添加.jar即可安装Sprint。 您还可以运行.jar来查看使用Sprint...
国外一个非常好用的开源IOS动画框架,可以自定义force、duration等属性。基于swift语言,应用方便简单。APP中VIEW常用到的动画效果这个就基本全了。对学习swift也很有帮助。
Swift 中的矢量动画框架,基于iOS 8 的SwiftGraphics 。 使用 ShapeAnimation,您可以使用基于 Swift 的良好语法轻松创建各种动画。 当前的开发发生在开发分支上。代码很少合并回主分支。 使用SVGKit进行 SVG 动画...
swift写一个简单的基于关键帧的动画框架.zip,A simple keyframe-based animation framework for iOS, written in Swift. Perfect for scrolling app intros.
动画游乐场 探索动画框架的游乐场
android的四种基本动画,缩放,平移,渐隐,旋转
FrameAnimation 用TextureView或SurfaceView 高性能播放帧动画,避免在很多... implementation 'com.yuyashuai.frameanimation:frameanimation:2.3.6' usage xml <com.yuyashuai.frameanimation.FrameAnimationView
本文实例讲述了Android编程之Animation动画用法。分享给大家供大家参考,具体如下: Animations 一、Animations介绍 Animations是一个实现android UI界面动画效果的API,Animations提供了一系列的动画效果,可以进行...
)如何使用: 它易于使用,让我们一步一步看:第1步,在头中包含必要的文件,以便框架正确运行: 下载所有动画:和<link rel="stylesheet" type="text/css" href="yourpath/all-animation.css" /><script ...
1、概述 Android提供了几种动画类型:View Animation 、Drawable Animation 、Property Animation 。View Animation相当简单,不过只能支持简单的缩放、平移、旋转、...property 动画系统是相当健壮的框架,它几乎可以
iOS动画主要是指CoreAnimation框架。官方使用文档地址为:CoreAnimationGuide。 CoreAnimation是iOS和macOS平台上负责图形渲染与动画的基础框架。CoreAnimation可以作用与动画视图或者其他可视元素,为你完成了...
苹果官网文档,Core Animation 是一种能让您制作高级动画和视觉效果的技术。UIKit 提供的动画,是建立在 Core Animation 技术之上的。如果您需要超出 UIKit 功能的高级动画,可以直接使用 Core Animation。
animation工程演示了App开发用到的常见动画技术,包括帧动画的用法(帧动画、GIF动画、淡入淡出动画)、补间动画的用法(补间动画的种类与用法、集合动画、在飞掠横幅中使用补间动画)、属性动画的用法(属性动画、...
ShapeAnimation-ObjC 基于iOS和OS X的CoreAnimation的Obj-C中的矢量动画框架。借助它,您可以轻松创建各种动画。特征CALayer和CAShapeLayer的动画扩展功能。 使用块功能对动画和级联动画进行分组。 支持带有动画的...
基于 iOS10 UIViewPropertyAnimator 创建的 iOS 动画框架.zip,A radical & elegant animation library for iOS.
此主题由 (基于CSS的响应式动画框架)提供支持,该框架用于创建独特的滑块,演示文稿,横幅和其他基于步骤的应用程序。 主题网址: : 作者: - | |入门要将Sequence.js主题添加到您的网页,请完成以下步骤: 下载...
用Swift实现的快速动画框架,包括常用UIView动画以及CALayer动画 用法 将BSQuickAnimation.framework添加到工程,在Targets-> General-> Embedded Binaries中添加该framework,在文件中import BSQuickAnimation即可 ...
一、简介 IOS 动画主要是指Core Animation框架。官方使用文档地址为:Core Animation Guide。 Core Animation是IOS和OS X平台上负责图形渲染与动画的基础框架。Core Animation可以作用与动画视图或者其他可视元素,...