Gamebryo—NiAnimation

NiAnimation

一、 简介

GB支持automatic animation playback和character-driven animation sequences。

GB动画系统基于控制器和动画对象机制。在这种机制中,动画控制器被分给一个动画对象去操作,这些控制器被放在动画对象的控制器列表中。当动画对象更新时,控制器会基于当前时间改变动画对象的某些行为。随着时间的变换,动画对象的属性也方式变化,这种改变是基于时间动画的。

二、 NiAnimation Baseis

1. Animation Time Controllers

不同的时间控制器控制着对象的不同属性,一个时间控制器不能挂载到一个不含有该类型属性(该类型时间控制器所能操作的)的对象上。如,NiMorphWeightsController不能挂载到一个NiNode,因为NiNode不含有一个NiMorphWeightsController所控制的顶点网格。

一个对象可以挂载多种类型的时间控制器,这样可以对对象的多个属性实现动画。

所有的动画控制器都是继承于NiTimeController,它提供了基本的时间功能。每个时间控制器都有个frequency和phase值,这些值再Update函数中接受时间。此外,还有两个属性—CycleType和AnimType。

CycleType决定了控制器再动画结束后的行为:loop代表重行播放,clamp代表结束动画,reverse代表ping-pong 效果。

AnimType代表了控制器如何理解传入Update的时间值。APP_TIME代表简单地使用Update的时间去修改动画状态;APP_INIT使用Update时间去计算从开始(start被调用)到现在的时间,从而使用该时间去修改动画状态。

在模型导出的使用,NiAnimation会被自动创建。这种导出会根据动画类型创建相应的控制器:使用NiTransformControllers来创建骨骼动画;蒙皮会被导出到NiMorphWeightControllers等。

大多数控制器都会包含一个NiInterpolator,该类负责接受时间,以及根据时间来决定相关的值。包括动画帧、solving constraint,以及其它算法。

在导出3D模型动画序列时,可以把这些interpolator看作是NiEvaluator。

许多NiInterpController对象支持一种或多个UpdateValue函数,该函数使控制器把一些特殊的数据关联到场景属性上。

2. Animation Keys

NiKeyBasedInterpolator对象控制的对象数据被包含在动画帧中,这些动画帧继承于NiAnimationKey类。大部分修改器(interpolator)包含一些序列帧,每一帧包含了时间和该时间的值。如,一个NiPosKey涉及到一个3D空间的位置信息,即一个时间值和一个NiPoint3。一个修改位置信息的控制器(如,NiTransformController)将包含一个访问NiPosKey的修改器。当更新时该控制器会根据frequency、phase、CycleType、AnimType来修改时间值,同时把这个时间值到修改器中。修改器会根据该时间值来决定那一帧,从而得到该帧的数据值。此过程时基于修改器的帧类型和修改器的类型。最后得到的数值会被传到控制器所控制的对象。

为了降低存储和提高性能,动画帧会被分配和存储到一组block arrarys中。因此,你想指定一个动画帧(NiAnimationKey、NiPosKey、NiRotKey、NiFloatKey、NiColorKey、NiBoolKey)你必须通过GetKeyAt(uiIndex、uiKeySize)在Key Array中定位。其中uiIndex代表帧的位置、uiKeySize代表一个动画帧的大小。

在创建和销毁时,使用NiAnimationKeypseudo-virtual 函数。

3. Animation Key Array Requirements

动画系统使用GenInterp函数在特定的时间去采样帧数组中的数据。主要有以下要求

(1)传入GenInterp的时间必须在在帧时间范围内

(2)帧数组至少包含一个帧

(3)帧时间必须时严格增长的,不能有两个帧包含同一时间。

4. Shared Animation Data Objects

多个修改器可以使用统一动画帧数据,而不用复制帧数组。该过程通过Replace来实现。

5. Transform Animation and UpdateSelected

当一个节点的局部空间进行了变换,在场景图中和变换相关的属性(如世界空间、包围盒)在场景图中必须被更新。当在游戏角色中使用GB高级“sequence-based animation system”该更新过程是自动的。但是应用程序直接使用动画变换时,如NiTransformInterpolator,必须确保场景中全局变换属性被正确更新。

该更新过程可以通过UpdateSelected标志控制,该标志可以被设置通过递归函数NiAVObject::SetSelectiveUpdateFlags。该函数的主要要求如下:

(1)所有的局部对象变换动画被处理通过NiTimeController子类。

(2)所有的控制器确保IsTransfomController返回true

6. Animation Sequences

尽管可以通过NiTimeCtroller和NiInterpolator对象去创建动画。在高级对象时使用动画序列。该序列在场景树的一个枝上驱动整个角色动画。GB通过NiSquenceData来实现该过程。

一个NiSequenceData是一组NiEvaluator对象,该这些对象描述了一个完整的角色动画,如步行、闲逛。Evaluator扮演了和Interpolator相似的角色。但对于动画序列evaluator更有效。在序列中所有的evaluator含有共同的开始和结束时间,此外所有的evaluator拥有共享一个时间信息,如frequency,cycle type.

NiSequenceData对象以KF文件的形式被美术工具导出,可以通过NiControllerManager或NiActorManager(分别到表一个 in-game和角色).为了存储优化,一个特定的NiSequenceData可以被使用同一3D模型多个角色共享.如,Ice Demo中的小鸡,使用了同一NiSequencyData对象,因为所有的小鸡都是从一个角色模板中克隆来的.和interpolator不同的是,evaluator是完全共享的,因为他们包含同一动画播放状态.

当一个NiSequence被激活时将在角色上创建一个NiControllerSeqence临时对象.NiControllerSequence代表当前活动的动画,当活动的sequence完成运行时,该对象将会被销毁.NiControllerSequence管理一个NiScatchPad,它存储了evaluators需要的角色序列的基本数据(instance-based data).

每一个NiControllerSequence拥有一个NiPoseBuffer,它代表了NiControllerSequence在特定时间写入动画数据.特别,pose buffer包含一个动画序列的基本时间信息快照(snapshot).

7. Animation Blending Overview

动画混合主要分为基于interpolator和sequence两部分。

(1) NiInterpolator

NiInterpolator主要负责通过当前的时间值来得到相应的动画数值。支持的动画数据格式有:bool、float、NiQuaternion、NiPoint3、NiColorA以及NiQuaTransform。这些数值被NiInterpController使用来修改对象的相应部分。如NiAlphaController使用一个NiFloatInterpolator去设置NiMaterialProperty中的Alpha值,NiInterpolator不要求使用动画帧去生成输出值。如NiLookAtInterpolator生成一个NiQuaTransform,它迫使对象去“look at”其它对象。

创建Interpolator需要注意以下:

clip_image002

NiBlendInterpolator对象

NiBlendInterpolator,是NiInterpolator的子类。它可以从多个interpolator去混合数据。该类存储了一个interpolatror数组和每一个interpolator的权重值。在它的update函数中更新所有的interpolator以及通过相应的权重来计算最终的值,最后把混合的值返回给NiBlendInterpolator。由于该类也是NiInterpolator的子类,所有对应的控制器也不知道该值是被混合而来的。必须注意的是这些权重在混合计算之前必须被归一化。

NiBlendInterpolator对象支持为每个子Interpolator增加优先权参数.优先权是一个在混合操作时获取特定interpolator的途径.只有拥有最高优先权的interpolator才会参与混合运算.这中方式使得一个特殊动画可以在一定时期内占据统治地位.如角色行走,想在角色的上半身触发一个射击动作,当完成后顺利返回先前的行走状态.由于行走时手臂进行轮转动作,如果我们严格地去混合行走和射击动画,将会得到一个错误的结果.此时我们可以使麽个时刻射击动画拥有更高的优先权.当混合处理时,上身的射击动作将替代手臂的行走动画.当混合结束后,将返回先前的行走动作.

(2) 序列帧连接

NiSequenceData对象代表角色不同的动作,如行走,跳跃,停止,射击… ,当角色处于某个状态时,一个NiControllerSequence将别临时性的创建,来代表当前动画.每一个NiControllerSequence拥有一个NiPoseBuffer,它代表完成特定时间的动画数据写入.特别,pose buffer包含一个动画序列的基本时间信息快照(snapshot).

三、 Higher Level NiAnimation Systems

1. NiControllerManager

简介

    一个NiControllerManager控制了角色的动画序列或一组场景。它提供了不同方法来激活、休眠、混合动画序列从而产生平滑的动画。NiSequenceData对象存储在KF文件中,被添加到控制器管理器中。一个NiControllerSequence被临时创建来注册NiSequenceData。该NiControllerSequence代表了当前角色激活的动画,随后被销毁或重新利用。

    每一个NiControllerSequence拥有一个NiPoseBuffer、它是NiControllerSequence通过时间来计算动画的依据。实际上、该pose buffer包含了一个动画序列的时间信息。通过使用辅助类、控制器管理器(controller manager)传递该时间信息到目标场景中、从而产生角色动画。当多个sequence同时作用在同一角色上时,在计算最终结果之前,控制器管理器会混合这些数据通过pose buffer。

    除了激活和休眠sequence,controller manager还会提供一些列机制来平滑sequence之间的过度。简而言之、NiControllerManager提供了一个基于sequence角色动画的上层接口。控制器管理器以及它的辅助类处理一些低级别的时间控制、帧数据、evaluator、scratch pad entries、scene graph attributes、以及其它基本动画实体。这些抽象允许应用程序在何时的时间在一个更高一个级别播放Sequence从而处理角色动画。

    综上所述、NiActorManger建立在NiControllerManager基础上提供更高级别的抽象来控制角色的sequence。尤其、NiActorManager还支持通过一个简单的状态机来控制基于in-game角色上播放sequence。在状态机中、sequence之间的平滑过度可以通过Gamebryo Animation Tool。因为NiActorManager提供了一个比NiControllerManager更高级别的接口。

   辅助类

   NiControllerManager使用一些辅助类来播放sequence,这些辅助类包括:NiPosBingding、NiPoseBlender、NiMultiTargetPosHandler。在应用程序背后,由于NiControllerManager和这些辅助类来交互从而使得应用程序可以很少和这些类进行交互。尽管如此、最好还是理解系统背后的工作机制。

   一个NiPoseBinding会在NiControllerManager的构造函数中保对其进行绑定。NiPoseBinding可以映射到一个sequence evaluator channels从而获取数据的位置(NiPoseBuffer)。每一激活的Sequence有一个NiPoseBuffer,它可以指定在特定的更新时间里来修改它的采样evaluator 数据。NiPoseBinding决定那一块数据被写入。例如,一个驱动肘部旋转的evaluator channel可以包含5个四元组的pose buffer,从而角色上活动的sequence将通过pose buffer来写肘部旋转值到这5个四元组中。当包含了一个先前未绑定evaluator channel的NiSequenceData被NiControllerManager加载时,一个新的bingings将被加入到NiPosBingding中。

    每一个角色模板只能有一个NiPoseBinding。然而,不同角色使用同一3D模型以及一组动作序列可以共享一个NiPoseBinding这样可以达到优化的目的。例如在Lenguis On Ice demo中所有的企鹅复用了同一NiPoseBinding,因为它们是从同一角色模板克隆出来的,同时,企鹅的妈妈使用了一个不同的NiPoseBinding,因为她使用了一个不同的3D模型,以及不同的动作序列。

    当一个角色有多个sequences激活时,控制器管理器会使用NiPoseBlender来混合每一个包含在sequences`s pose buffer中的数据。管理器中的NiPoseBinding提供的统一的映射机制允许基于sequences的时间时间戳(snapshots)迅速混合数据,这些混合是基于每个激活sequence的优先权和权重。NiPoseBlender会写最终的混合数据到一个单独的NiPoseBuffer,该NiPoseBuffer会返回到控制器管理器中,从而使其包含的数据能影响场景目标。该部分在Animation Basics中。

    控制器管理器使用一个NiMultiTargetPoseHandler来传送Pose buffer数据到他们的目标插值函数控制器和场景节点。由于各种实际的目的,NIMultiTargetPoseHandler已经变成了NiControllerManager的场景接口。在初始化时、NiMultiTargetPoseHandler会建立一个内部数组,该数组可以容纳pose buffer的位置信息。该位置信息包含了NiPoseBinding和他们的目标场景对象。当一个包含先前未绑定evaluator channels的sequence对象被控制器管理器加载时,NimultiTargetPoseHandler的内部数组会和NiPosebinding一个更新。作为优化,变换数组会直接绑定到目标变换节点上,其它数据会绑定他们的目标插值函数控制器上。

    除了映射pose buffer到目标对象之外,NiMultiTargetPoseHandler还为accumulation root维护了一个变换值(当角色开启了transformtion accumulation)。当角色sequence播放时,NiMultiTargetPoseHandler会负责更新accumulation变换节点。

    尽管NiControllerManager继承与NiTimeController,但是它很少使用基类的接口。一个典型的应用程序使用NiControllerManager来控制in-game角色通过如下方式:

    (1) 加载in-game角色的场景图。3D模型可以通过3ds max或maya来以nif文件导出。

    (2) 创建NiControllerManager,并在构造函数中指定角色场景图。当开启accumulation transformations时此时必须指定构造函数的第二个参数为true。

    (3) 为角色注册一个或更多的动画序列,主要通过NiControllerManager的相关加载接口如:AddSequenceDataFromFile、AddAllSequenceDatasFramFile。这些函数从KF(是通过3ds max或maya插件导出的-Controller Extractor plug-in)文件中加载NiSequenceData。除此之外、在游戏运行时也可以向NiControllerManager中加载NiSequenceDat。已注册的NiSequenceData可以被移除,以节省存储空间-RemoveSequencData。

    (4) 添加角色的场景图到游戏场景中。

    (5) 在主循环中更新和绘制游戏世界,通过调用场景根节点的Update(使用游戏的全局时间)。由于角色场景已经被添加到游戏场景中了,所以控制器管理器的Update函数会在场景图遍历时被自动调用。该过程会造成当前激活的sequence中的evaluator来写入最新的数组到pose buffer,当激活多的sequence时还会混合数据、随后传递最终混合的结果到指定的场景中,当完成时会关闭激活的sequence。本质上,角色场景的更新为了映射出最新的动画状态。

    (6) 尽管NiSequenceData对象已经被注册到角色中,但角色任然会处于静止状态,知道一个或多个sequence被应用程序激活。NiControllerManager::ActiveSequence会激活一个角色的sequence。一旦激活,sequence将会持续播放直到调用DeactivateSequence来休眠它。

    (7) 在一个sequence播放后控制器管理器会平滑地过渡到下一个sequence。该功能包括:CrossFade、BlendFromSequence、Morph以及BlendFromPose。

    (8) NiControllerManager中的激活和过渡功能会返回一个指针指向新建的NiControllerSequence(代表一个新的激活动画)。如果需要,应用程序可以获取一个智能指针,从而应用程序可以根据需要来查询和调整NicontrollerSequence。除此之外,应用程序可以根据需要来注册ActivationCallback对象。

    (9) 应用程序可以通过NiControllerSequence::AddActivationCallback和NiSequenceData::AddDefaultActivationCallBack来注册相关的事件。第一个方法是向一个已播放的sequence来添加一个回调对象。第二个方法更有用,因为它会在每次激活sequence时都会创建一个回调对象。回调对象中的ActivationChanged函数会在相应的NiControllerSequence对象激活、休眠、重新激活时被调用。当应用程序保留个NiControllerSequence对象引用时,该机制非常有用。这些引用可以在回调信号进入INACTIVE状态时被释放,意味著该sequence不再对角色产生租用。

   (10) 应用程序会使用transformation accumulation去为角色建立accumulation transform。该过程可以通过NiControllerManager::SetAccumulatedTransform来完成。

   (11) 关闭角色停止所有的激活sequence,从游戏世界中删除角色场景图。当应用程序释放最后一个引用时,角色相关的对象如:NiControllerManager、NiControllerSequence以及角色场景图将会自动销毁。NiControllerManager::RemoveAllSequence可以用来删除角色对象中所有的NiControllerSequence。相似地,RemoveAllSequenceData可以unregister所有NiSequenceData。在调用RemoveAllSequence之前可以使角色中的sequence recycling被销毁,这样可以加速角色的关闭过程。

    (12) Sequence recycling可以通过NiControllerManager::SetmRecycledSequence来控制。Sequence recling默认是开启的。

    (13) 如上所述,新的bindings可以被添加到NiPoseBinding中。下列技术是可选的,确保这些bingings可以在应用程序进入主循环前被定义。在工具中到处NiSequenceData时可以包含每一个可能的动画channel。在游戏进入主循环之前加载角色的Sequence。该操作可以迫使NiPoseBinding创建sequence中的所有channels创建bingdings。

   

2. NiActorManager-An Easier Way to Control Animations

    GB为in-game角色的sequence-based动画提供了两个类-NiControllerManager和NiActorManager。如上节所述,控制器管理器提供了激活、休眠、混合sequence的,以及产生平滑动画。NiActorManager建立在NiControllerManager之上,提供了最高级别的角色动画控制。特别,NiActorManager提供了支持通过简单的状态机来播放sequence,每一状态代表一个sequence动画。使用状态机,sequence之间的过度可以通过Gamebryo Animation Tool来定义,同时保存到KFM文件中。应用程序加载KFM文件时可以创建一个NiActorManager来代笔in-game角色。可以通过设置目标序列(Set Target Sequence)来激活sequence。NiActorManager处理相关细节-根据已定义的状态机来决定平滑地过度到目标sequence。拥有更简单的程序接口,使用NiActorManager允许动画者协调sequence之间的过度。

    NiActorManager比NiControllerManager拥有更复杂的回调系统。除了为特殊事件(发生在sequence播放的时候)注册回调功能,应用程序可以想NiActorManager查询下一个将要发生的事件。有5种类型的事件:动画激活事件、动画休眠事件、动画完成事件、sequence结束事件、以及关键字事件(text key events)。动画完成事件会在一个sequence完成播放或终止时激活,该事件不同于“sequence结束”事件,该事件暗示sequence循环中的最后一帧已经到达。其中最有用的事件类型是“关键字事件”,发生在任何关键字被查询时。如:使用一个“关键字”事件,暗示一个脚步碰撞到地板上,此时可以激活一个脚步声音。

    Usage

    (1) Animation Tool可以用来创建KFM文件,该文件代表了Actor Manager背后的资源。该资源包含了NIF文件-代表了角色的3D模型;一个或多个KF文件-包含了角色动画的sequences;以及状态机如何在两个sequences之间平滑过度细节。每一个可用的sequence会被分配一个ID,应用程序可以通过该ID来和角色管理器交互。当动画工具保存KFM文件时,会产生一个文件头,该文件头包含了一个命名空间和sequence IDs的枚举信息(enumations)。

    (2) 在应用程序中可以通过NiActorManager::Create来创建角色管理器。该静态函数通过特定的KFM文件来创建NiActorManager。该函数为角色建立了必须的动画状态机以及过度信息(Sequence)。在内部,NiActorManager创建和初始化一个NiControllerManager来处理大部分播放动画sequence的细节工作。如果需要,KFM中引用的NIF和KF文件会被加载。KF文件存储了3ds max或maya插件导出的动画序列信息-NiSequenceData。从Sequence ID到NiSequenceData对象之间的映射可以通过NiActorManager::ChanageSequenceData来修改。已注册的NiSequenceData可以通过UnloadSequeceData从NiActorManager中移除。

    (3) 向游戏时间中添加角色场景图-GetNIFRoot. 注意,该方法不同于GetActorRoot,它是返回NiControllerManager内部的目标节点。

    (4) 在游戏主循环中,调用NiActorManager::Upate可以根据游戏当前时间来更新内部的状态机。在角色被更新过后,调用角色场景图根节点的Update,从而角色内部的NiControllerManager会传递动画数据到场景中。

    (5) 尽管NiSequenceData对象已经被注册到NiActorManager中。NiActorManager可以根据状态机来过渡到相应的sequence,同时调用内部NiControllerManager。当sequence激活时,GB创建一个NiControllerSequence来维持一个状态实例,从而播放动画序列。该NiControllerSequence代表了激活的动画,在完成播放sequence后,它将会被销毁或recycled。根据需要,状态机中的激活sequence可以被访问(NiActorManager::GetCurAnimation或GetActiveSequence)。

    (6) sequence可以通过NiActorManager::ActivateSequence和DeactivateSequence独立于内部状态机被激活或休眠。该特征在激活身体部分动画,从而增加身体动画控制。如:状态机可以循环地控制角色在接到游走,当角色在街上碰到一个邻居时,ActivateSequence可以激活一个更高级别的挥手sequence,从而角色可以在游走的同事挥动手臂。

    (7) 事件可以通过NiActorManager::RegisterCallback和UnregisterCallback来被registerd和unregistered。当ActivateSquence函数接受到一个回调时状态机会激活sequence。

    (8) 在一个指定的事件即将发生时,NiActorManager::GetNextEventTime可以用于查询角色管理器。NiActorManager维持了一个基于sequence事件的时间轴,该sequence事件可以被预测。

    (9) 通过使用NiControllerManager::SetMaxRecycledSequence来控制Sequence recycling。应用程序会调用NiActorManager::GetControllerManager来方位角色的控制器管理器。sequence recycling默认是开启的,从而改善运行时效率以及街上片段的存储空间。

    (10) 在关闭角色时,通过NiActorManager::Reset来休眠所有的激活sequence,同时从游戏时间中移除角色的场景图。角色相关的对象如:NiActorManager、NiControllerManager、NiControllerSequence、角色场景图将被自动销毁,当应用程序释放它的最后引用。

  

3. Animation Sequence Data Creation

4. BSpline Copression

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值