在FBX模型中除了几何数据外较为常用的信息可能就是Camera和Light,虽然在游戏中一般不直接从模型中得到这两部分信息,而是由引擎来提供,但是FBX中提供了对这些信息保存的支持。其实单纯加载这两部分的信息很简单,就像之前介绍的在整个Scene Graph中对每个Node遍历过程中,判断得到当前结点是Camera或Light时调用相应的ProcessCamera或ProcessLight来完成相关的处理操作即可。
如果对于当前结点判断得到其是一个Camera结点,那么可以直接调用GetCamera来得到一个KFbxCamera类型的指针,之后就可以通过该指针来完成Camera属性的获取。
void ProcessCamera(KFbxNode* pNode)
{
KFbxCamera* pCamera = pNode->GetCamera();
// 调用相应的接口获取Camera的属性即可
}
对于Light结点的处理与Camera类似。至于Camera与Light结点所具有的属性可以直接在SDK中看kfbxcamera与kfbxlight的类型定义即可。
void ProcessLight(KFbxNode* pNode)
{
KFbxLight* pLight = pNode->GetLight();
// 调用相应的接口获取Light的属性即可
}
动画信息是模型数据中非常重要的一部分,也是一个渲染或游戏引擎最基本的需求之一。FBX对Animation的良好支持也成为其与.obj等静态模型最主要区别之一,而且最新的SDK中也提供了对Animation很丰富与简便的操作接口,包括自定义写入与读出等。接下来介绍一下如何使用FBX SDK来加载FBX中存储的动画信息。
在FBX中实现对于动画数据的存储主要通过以下三个对象层来实现:Animaiton Stack、 Animation Layer、Animation Node,其层次关系为
Animation Stack -> Animation Layer -> Animation Node,图示化结构为(图片来自于FBX SDKRef):
其中的Animation Stack为FBX动画管理的最高层,其中包含着与之相关联的Animation Layer等;每个Animation Stack对应着一套动作过程。每个Stack中包含一个或多个Animation Layer(当用来做blend时就需要多个Layer,但一般是一个)。在每个Layer中又通过一个KFbxAnimCurveNode的结点使Layer与具体的动画数据发生关系。一般情况下可以根据自己的需要情况或引擎的动画实现方式来读取FBX中的动画数据,例如本人在实现时从FBX中读取数据的方法就可以抽像化为如下图所示的结构:
其中对每个Node判断其是否有对应的动画数据,若有则读取其Curve中的数据并存储以供渲染更新使用,代码如下所述:
void LoadNodeCurve(KFbxAnimLayer* pAnimationLayer , KFbxNode* pNode , StackTimeSpan& timeSpan)
{
KTime keyTimer;
unsigned long millseconds;
for(UINT i = 0 ; i < timeSpan.mKeyNums ; ++i)
{
millseconds = timeSpan.mStart + (float)i * timeSpan.mStep;
keyTimer.SetMilliSeconds(millseconds);
// 计算得到当前结点在当前时刻下所对应的空间局部和全局矩阵
// 局部矩阵对于Skeleton是必需的,因需要使用它来计算父子Skeleton之间的空间关系
KFbxXMatrix curveKeyLocalMatrix = pNode->EvaluateLocalTransform(keyTimer);
KFbxXMatrix curveKeyGlobalMatrix = pNode->EvaluateGlobalTransform(keyTimer);
}
}
代码中的timeSpan是一个自定义的结构,其中包含了整个FBX对象动画信息的相关数据,比如帧数、起始时间、帧间时差等;在读取时需将其中的信息转换为一个KTime类型的对象(keyTimer)以供FBX SDK的API使用。上述操作加载了动画数据中直接相关的空间Matrix信息,这是普通模型对象的基本动画信息。但是对于Camera或Light等对象而言,动画不仅包含着位置或空间信息的变化而且还包含着一些其它的属性变化如Camera的FOV,Light的Direction,Color等,这些信息也导出FBX时被存储到了FBX中。而这些信息的获取就是通过KFbxCurveNode来实现,其关联具体的Curve到相应的Property上,进而从中获得对应的动画信息。比如我们熟悉的Camera实现中有一个常用的属性PixelAspectRatio,用来描述视口Width与Height之间的比值,对于某些动画效果这个Ratio可能是时变的,因而在建模时就会将该信息同样以动画的信息进行存储,现在我们想要得到这一部分动画数据。通过查看kfbxcamera.h可以发现在KFbxCamera的定义中含有
KFbxTypedProperty
的一个成员变量,这即是PixelAspcetRatio动画数据所存储的位置;而在ProcessCamera时已经由当前Node的指针得到了Camera对应的指针,之后该部分读取代码基本上如下所述:
void LoadCameraCurve(KFbxAnimLayer* pAnimationLayer , KFbxCamera* pCamera , StackTimeSpan& timeSpan)
{
if(pCamera == NULL)
{
return;
}
// 通过FBX的属性对象而获取其所对应的Animation Curve
KFbxAnimCurve* pCameraAttriAnimCurve = pCamera->PixelAspectRatio.GetCurve(pAnimationLayer);
// 判断当前的属性是否含有可变的Animation值
if(pCameraAttriAnimCurve)
{
KTime keyTimer;
unsigned long millseconds;
for(UINT i = 0 ; i < timeSpan.mKeyNums ; ++i)
{
millseconds = timeSpan.mStart + (float)i * timeSpan.mStep;
keyTimer.SetMilliSeconds(millseconds);
// 计算Camera的某属性在当前时刻所对应的值
pCameraAttriAnimCurve->Evaluate(keyTimer);
}
}
}
上述代码通过PixelAspectRatio的属性对象加载了其不同时刻下的动画值,其它的属性的动画读取也可以用类似的操作实现。
加载了上述的动画数据以后,即可以使用其来驱动模型中的直接动画相关部分,如Camera、Light、Skeleton等。由之前的代码可知,在加载动画数据时我们使用了当前Node的指针,因而就可以用它在加载动画时存储其它的一些额外信息使这些动画数据与对应的Camera、Light、Skeleton等部件进行关联(比如Node的指针,或是Node的Name等),从而可以从动画管理器中随时查得到某结点在指定时刻位置上的动画数据。该部分可以根据具体的实现采取适宜的操作即可完成。
最后,带有动画驱动的Skeleton渲染效果如下列图所示(Camera,Light的动画效果木有绘出):
下一篇介绍一下Skeleton Skinning~~