FBX SDK快速简笔(基本)

FBX SDK快速简笔(基本)

FBX模型的组织结构

FBX是以scene graph的结构来存储模型的所有信息(也可以认为是一个多叉树)。

整个Fbx由Scene(场景)这个空属性的根结点开始,其中每个节点均是一个KFbxNode(根节点)的对象,提供双向索引(即父节点可以索引到子节点,反之亦然。)。每个节点都有一个标记属性enum(C++枚举类型)值,比如eMesh、eLight、eCamera或eSkeleton等,分别用来标记当前结点是Mesh、Light、Camera或Skeleton,主要通过判断节点属性在遍历时进行不同的处理操作。

通常我们在使用前会初始化以下两个变量。

KFbxskdManager:内存管理机制【管理所有的加载资源(创建资源和销毁资源(简单来讲就是由C写的动态内存管理机制))】

KFbxScene:Scene(场景)容器【在这个容器内进行模型的加载和处理】

FbxManagr管理内存布局

FbxManagr管理内存布局是fbx的基础和必须存在的。‘Create()’和‘Destroy()’成员函数来创建或销毁的。

FbxManager* lSdkManager = FbxManager::Create();//初始化一个管理内存的对象
//将FbxManager*的对象传递给FbxScene*构建一个新的场景
FbxScene* lScene = FbxScene::Create(lSdkManager, "Scene Name");
//最后的字符串是为了给对象命名使用的

//仔细阅读文章的你会发现,是一个从上至下的继承关系
//FbxManager->FbxScene->FbxNode|FbxMesh。当然为了保密官方并没有公布数据结构,我们在使用过程中只能了解大概的层次关系。

//创建一个节点对象
FbxNode* lNode = FbxNode::Create(lScene , "node");
//创建一个网格对象
FbxMesh* lMesh = FbxMesh::Create(lScene , "Mesh");

//销毁
lMesh->Destroy();      // 销毁网格
lNode->Destroy();      // 销毁节点
lScene->Destroy();     // 破坏场景和它的对象
lSDKManager->Destroy() // 销毁SDK管理器和它管理的任何剩余对象。

注意:(FbxNode和FbxMesh)可以只使用FbxManager作为引用来创建。

//可以通过调用FbxObject::GetFbxManager()来检索用于实例化FbxObject的FbxManager。
//FbxImporter继承自FbxIO, FbxIO继承自FbxObject。
//假设pImporter是FbxImporter的一个实例。
FbxManager* lSdkManager = pImporter->GetFbxManager();

复制一个对象

// 假设lScene是一个指向有效Scene对象的指针。
FbxMesh* lSourceMesh = FbxMesh::Create (lScene, "");

// 为lSourceMesh定义控制点等。
// 这个网格将被覆盖
FbxMesh* lTargetMesh = FbxMesh::Create (lScene, "");

//将数据从lSourceMesh复制到lTargetMesh。请注意,
//源对象和目标对象必须是的实例
//相同的类(本例中为FbxMesh)。
lTargetMesh->Copy(lSourceMesh);

注意:复制FbxObject还将复制它的所有关联的FbxProperty实例及其值。
注意:复制FbxObject不会复制它的任何对象间连接(例如,父-子关系)。这些连接必须在副本上显式设置。有关更多信息,请参见连接。

管理属性

FbxProperty p = FbxProperty::Create(pScene, DTDouble3, "Vector3Property");
//可以FbxProperty::GetName()来访问创建FbxProperty的名称
//通过调用根属性上的FbxProperty:: destroyrecur()来销毁FbxProperty的层次结构。

//使用以上类型传递给
FbxSet<FbxDouble3>(p, FbxDouble3(1.1, 2.2, 3.3));

属性数据

//初始化FbxNode* lNode ...
//分别调用FbxProperty::set()和FbxProperty::Get()来设置和访问的数据。
FbxDouble3 translation = lNode->LclTranslation.Get();
FbxDouble3 rotation = lNode->LclRotation.Get();
FbxDouble3 scaling = lNode->LclScaling.Get();

Flags标记

如果属性还包含一组FbxPropertyFlags::eFbxPropertyFlags,可以使用FbxProperty::GetFlag()和FbxProperty::ModifyFlag()进行操作。

运算符

标准的比较和赋值运算符可用于比较和相互分配属性(FbxProperty::operator==(), FbxProperty::operator::operator!=(), FbxProperty::operator=())。

用户定义的数据

属性可以在层次结构中组织。一个FbxProperty可以连接到一个FbxObject,或者连接到另一个FbxProperty。可以通过调用FbxProperty::GetFbxObject()来访问属性的绑定FbxObject。ExportScene05示例说明了属性层次结构的构造。

访问FBX的对象

使用FbxObject::FindProperty()来搜索这些属性。还可以通过调用FbxObject::GetFirstProperty()和FbxObject::GetNextProperty()等方法来迭代对象的属性。

在属性层次结构中导航(注释:这部分没介绍清楚)

可以通过FBX属性导航函数来遍历FBX属性的层次结构:FbxProperty::GetParent(), FbxProperty::GetChild(), FbxProperty::GetSibling(), FbxProperty::Find(),等等。

它演示了如何创建自己的FbxProperty值,并将它们绑定到一个FbxObjectMetaData实例(该实例继承自FbxObject)。

// …将pScene初始化为FbxScene*…

//在pScene中创建一个FbxObjectMetaData*对象。
FbxObjectMetaData* lFamilyMetaData = FbxObjectMetaData::Create(pScene, "Family");

//根据参数化的FbxProperty实例创建和分配数据
//数据类型(DTString、DTFloat、DTDouble)。
//
//第四个参数是一个可选的标签字符串,它可以被获取和修改
//使用FbxProperty::GetLabel()和FbxProperty::SetLabel()。这个标签只存在
//在程序的主存中,并且不会被导出到文件中如果/当属性
//导出。
//
//这些属性将包含在pFamilyMetaData对象中。
//
FbxProperty::Create(lFamilyMetaData, DTString, "Level","Level").Set(FbxString("Family")); // String
FbxProperty::Create(lFamilyMetaData, DTString, "Type", "Type").Set(FbxString("Wall"));     // String
FbxProperty::Create(lFamilyMetaData, DTFloat, "Width", "Width").Set(10.0f);              // float
FbxProperty::Create(lFamilyMetaData, DTDouble, "Weight", "Weight").Set(25.0);            // double
FbxProperty::Create(lFamilyMetaData, DTDouble, "Cost", "Cost").Set(1.25);                // double

Connections连接(尽力了没法精简)

Visualizing connections可视化的连接

连接是一个FBX SDK数据结构,它管理FBX对象和FBX属性之间的双向关系。为了保证FBX SDK内连接的一致性,实际的数据结构不会公开。相反,可以使用FbxObject和FbxProperty连接管理方法来操作连接,如:FbxObject::ConnectSrcObject()、FbxObject::ConnectDstObject()、FbxProperty::ConnectDstObject()、FbxProperty::ConnectSrcProperty()等。

可以将连接可视化为FBX对象和属性的目标和源层次结构分为。

  • 对象-属性连接:属性作为源包含在对象中。调用FbxObject::GetSrcProperty()将在给定的索引处返回对象的源属性。对称地,调用FbxProperty::GetDstObject()将返回属性的目标对象。
  • 对象-对象连接:对象之间的父子关系使用连接(例如:场景的节点层次结构)。通常,对象的子对象是源,可以使用FbxObject::GetSrcObject()访问。对象的父对象是一个目的地,使用FbxObject::GetDstObject()访问它。
  • 属性-属性连接:属性之间的父子关系也使用连接(例如:fbxiosetings的属性层次结构)。通常,属性的子元素是源,可以使用FbxProperty::GetSrcProperty()来访问。属性的父级被引用为目标,并使用FbxProperty::GetDstProperty()来访问。

下面的代码示例演示了上述关系图中的目标连接和源连接。

  • 访问obj0的源对象:

  • //……假设obj0已经被初始化,以反映上面的关系图…
    //为了便于说明,计算连接到obj0的源对象的数量。
    int numSrcObjects = obj0->GetSrcObjectCount(); 
    // numSrcObjects = 2
    
    //访问连接到obj0的两个源对象
    //注意,obj0->GetSrcObject(0)等价于调用obj0->GetSrcObject()
    FbxObject* obj1 = obj0->GetSrcObject(0);
    FbxObject* obj2 = obj0->GetSrcObject(1);
    
  • 访问obj0的源属性:

    //……假设obj0已经被初始化,以反映上面的关系图…
    FbxProperty* prop0 = obj0->GetSrcProperty();
    
  • 访问obj1的目标对象:

    //……假设obj1已经被初始化,以反映上面的关系图…
    FbxObject* obj0 = obj1->GetDstObject();
    
  • 以特别的方式从obj2开始遍历层次结构:

    //……假设obj2已经被初始化,以反映上面的关系图…
    //使用obj2访问prop2。
    FbxProperty* prop2 = obj2->GetSrcProperty();
    
    //为了说明,计算prop2的源属性的数量。
    int numSrcProperties = prop2->GetSrcPropertyCount(); // numSrcProperties = 2
    
    //访问prop3和prop4,这是关于prop2的来源,
    //分别以0和1为索引。
    FbxProperty* prop3 = prop2->GetSrcProperty(0);
    FbxProperty* prop4 = prop2->GetSrcProperty(1);
    
    //使用obj2访问obj0。
    FbxObject* obj0 = obj2->GetDstObject();
    
    //使用obj0访问prop0。
    FbxProperty* prop0 = obj0->GetSrcProperty();
    
    //使用obj0访问obj1。
    //这里,我们假设obj1的索引位置是0,而obj2的索引位置是1。
    FbxObject* obj1 = obj0->GetSrcObject(0);
    
    //使用obj1访问prop1。
    FbxProperty* prop1 = obj1->GetSrcProperty();
    

总体上来说有点像display的关系树。当设置新的display(块)需要设置其顺序例如:0、1设置完块,就往里面放东西,然后设置连接形式。

连接对象和属性

FBX SDK中的连接概念允许将FbxProperty的实例动态添加到FbxObject中。这使您可以灵活地定义自己的数据类型,通过FbxProperty::SetUserDataPtr()成员函数将其包装成FBX属性,并通过FbxObject::ConnectSrcProperty()成员函数将该属性绑定到FBX对象。

在深入研究节点(FbxNode)和节点属性(FbxNodeAttribute)之间的连接使用之前,建议您熟悉FBX SDK的场景图组织。有关更多信息,请参考{节点和场景图}部分。

“对象-对象”连接示例:场景中节点之间的父子关系

//……假设lScene初始化为FbxScene*,
//获取场景的根节点。
    FbxNode* lParentNode = lScene->GetRootNode();

    //创建一个子节点。
    FbxNode* lChildNode = FbxNode::Create(lScene, "child");

 	//将子节点添加到根节点。
    lParentNode->AddChild(lChildNode);

连接方式如下:

  • 子节点将是父节点的源对象。
  • 父节点将是子节点的目标对象。

注意:lParentNode是lScene的一个源对象。因此,lScene是lParentNode的目标对象。

注意:主题显式操作节点之间的连接,以合并两个场景的内容。

“对象-对象”连接示例:节点和节点属性

FbxNode与FbxNodeAttribute的关系通常是通过调用FbxNode::SetNodeAttribute()来创建的。因此,可以将FbxMesh的实例(它继承自FbxNodeAttribute)绑定到场景中的一个节点。在这种情况下:

  • FbxNodeAttribute是FbxNode的一个源对象。
  • FbxNode是FbxNodeAttribute的目标对象。

注意:材料(fbxsurfacemataterial)也作为源对象连接到fbxnode。一个节点可以连接到多个材料,一个材料可以连接到多个节点(以减少内存使用)。但是,请注意,fbxsurfacemataterial不是FbxNodeAttribute的子类。

“对象-属性”连接示例:节点和转换

如~{FBX properties}~小节所述,FbxNode的本地转换数据被定义为FbxPropertyT,并使用FbxDouble3数据类型进行参数化。在这种情况下:

  • FbxNode::LclTranslation返回的FbxPropertyT是该FbxNode的一个源属性。

  • FbxNode是FbxPropertyT的目标对象。

错误处理;差错处理

下面的代码出现在Common/Common.cxx中。它说明了文件导入操作(包括密码保护)的错误处理过程。一些代码被“//…”所取代,以强调错误处理功能。

bool LoadScene(FbxManager* pManager, FbxDocument* pScene, const char* pFilename)
{
  //…

  bool lStatus;
  char lPassword[1024];

  //…

  //创建一个Importer(输入)
  FbxImporter* lImporter = FbxImporter::Create(pManager,"");

 //通过提供文件名初始化导入程序。
  const bool lImportStatus = lImporter->Initialize(pFilename, -1, pManager->GetIOSettings());

  // …

  if(!lImportStatus)
    {
        FbxString error = lImporter->GetStatus().GetErrorString();
        FBXSDK_printf("Call to FbxImporter::Initialize() failed.\n");
        FBXSDK_printf("Error returned: %s\n\n", error.Buffer());

        if (lImporter->GetStatus().GetCode() == FbxStatus::eInvalidFileVersion)
        {
          FBXSDK_printf("FBX file format version for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision);
          FBXSDK_printf("FBX file format version for file '%s' is %d.%d.%d\n\n", pFilename, lFileMajor, lFileMinor, lFileRevision);
        }

        return false;
    }
    // …
    //导入场景。

    lStatus = lImporter->Import(pScene);

    if(lStatus == false && lImporter->GetStatus().GetCode() == FbxStatus::ePasswordError)
    {
        FBXSDK_printf("Please enter password: ");
        lPassword[0] = '\0';

        FBXSDK_CRT_SECURE_NO_WARNING_BEGIN
        scanf("%s", lPassword);
        FBXSDK_CRT_SECURE_NO_WARNING_END

        FbxString lString(lPassword);

        IOS_REF.SetStringProp(IMP_FBX_PASSWORD, lString);
        IOS_REF.SetBoolProp(IMP_FBX_PASSWORD_ENABLE, true);

        lStatus = lImporter->Import(pScene);

        if(lStatus == false && lImporter->GetStatus().GetCode() == FbxStatus::ePasswordError)
        {
          FBXSDK_printf("\nPassword is wrong, import aborted.\n");
        }
    }

  	//销毁Importer
    lImporter->Destroy();

    return lStatus;
}

支持字符串格式

FBX SDK内部使用UTF-8字符串。当调用操作系统的API中的函数时,FBX将其UTF-8字符串转换为所需的字符串格式。

Windows环境定义了以下转换函数:

FBXSDK_DLL void FbxWCToAnsi(const wchar_t* pInWideChar, char*& pOutANSI);
FBXSDK_DLL void FbxAnsiToWC(const char* pInANSI, wchar_t*& pOutWideChar);
FBXSDK_DLL void FbxAnsiToUTF8(const char* pInANSI, char*& pOutUTF8);
FBXSDK_DLL void FbxUTF8ToAnsi(const char* pInUTF8, char*& pOutANSI);

定制FBX SDK

自定义用户数据

FbxObject和FbxProperty的实例提供了一个(void)指针,这样你就可以将任何自定义数据关联到该实例:*

  • FbxObject用户数据:FbxObject::SetUserDataPtr(), FbxObject::GetUserDataPtr()。
  • FbxProperty用户数据:FbxProperty::SetUserDataPtr(), FbxProperty::GetUserDataPtr()。

注意:您负责创建、销毁和以其他方式管理自定义数据。

自定义属性

可以将FbxProperty的自定义实例动态添加到FbxObject中。在~{FBX properties}~中提供的示例代码说明了如何创建和添加此类自定义属性。

ExportScene05、UserProperties和Tutorial: ImportExport示例程序还提供了关于如何获取和设置自定义属性数据的详细信息。

定制类

可以将FbxProperty的自定义实例动态添加到FbxObject中。在~{FBX properties}~中提供的示例代码说明了如何创建和添加此类自定义属性。

ExportScene05、UserProperties和Tutorial: ImportExport示例程序还提供了关于如何获取和设置自定义属性数据的详细信息。

必须通过调用FbxManager::RegisterFbxClass()向FbxManager注册自定义类。

在以下来自" [ExportScene03] ()"/main的示例中。自定义类MyFbxMesh和MyFbxObject是在ExportScene03/MyFbxMesh.h中定义的。这些类分别继承自FbxMesh和FbxObject。

#include <fbxsdk.h>
#include "../Common/Common.h"
#include "MyFbxMesh.h"

// ...

int main(int argc, char** argv)
{
    FbxManager* lSdkManager = NULL;
    FbxScene* lScene = NULL;
    // ...

	//编写FBX SDK。
    InitializeSdkObjects(lSdkManager, lScene);

    //将我们创建的新类添加到Sdk管理器中
	//我们的类MyFbxMesh派生自FbxMesh
    lSdkManager->RegisterFbxClass("MyFbxMesh", FBX_TYPE(MyFbxMesh), FBX_TYPE(FbxMesh));
    //现在,我们的类MyFbxMesh已经可以使用了

    lSdkManager->RegisterFbxClass("MyFbxObject", FBX_TYPE(MyFbxObject), FBX_TYPE(FbxObject), "MyFbxObjectType", "MyFbxObjectSubType");
    // ...

}
为层元素定制用户数据

要创建具有自定义类型的layer元素,请使用FbxLayerElementUserData类。像任何其他层元素一样,它可以被多边形顶点映射,一个顶点,一个多边形,等等。

请参考ExportScene03中的CreateCubeWithMaterialAndMyFbxMesh()函数。这个函数创建一个基于浮点型和布尔型数据类型的自定义复合,并为每个顶点添加数据。

自定义文件格式

FBX SDK使用几种文件格式导入和导出场景数据。每一种文件格式都有自己的writer类(来自FbxWriter)和reader类(来自FbxReader)。要使用定制的FbxWriter和FbxReader,它们必须通过FBX SDK I/O插件加载。有关更多信息,请参见使用FBX SDK I/O插件自定义文件格式。

FBX扩展SDK

FBX Extensions SDK是一组回调函数,可以在.dll文件中实现。这些功能自定义的导入和导出功能的3ds Max,玛雅和MotionBuilder。有关更多信息,请参见FBX Extensions SDK。

以上基本的介绍完了,从这里开始介绍重点

FBX SDK(相信各位看到他就烦)

FBX SDK的配置,需要下载FBX SDK。

FBX场景

Scene组织总结

FBX SDK场景图是由FbxScene类抽象出来的。Scene被组织为节点的层次结构(FbxNode)。通过FbxScene::GetRootNode()访问场景的根节点。一个场景元素,例如,一个网格,一盏灯,或者一个相机,是通过结合FbxNode和FbxNodeAttribute的子类来定义的。有关更多信息,请参见FBX节点和FBX节点属性。

注意:在导出操作期间,场景的根节点不会导出到文件中。只有根节点的子节点被导出到文件中。因此,不建议将任何内容与应该保存到文件中的根节点相关联。

创建一个Scene(场景)

正如在使用FBX SDK管理器管理内存中所演示的,FbxScene是通过调用FbxScene::Create()函数来创建的。一个新的FbxScene包含一个根FbxNode,以及一个带有默认配置的FbxGlobalSettings(全局设置)对象。

//创建
FbxManager* lSdkManager = FbxManager::Create();
FbxScene* lScene = FbxScene::Create(lSdkManager, "Scene Name");

使用Scene创建子节点

//获取场景的根节点。
FbxNode* lRootNode = lScene->GetRootNode();

//创建一个子节点。
FbxNode* lChild = FbxNode::Create(lScene, "child");

//将子节点添加到根节点。
lRootNode->AddChild(lChild);

Sence轴和单元转换

FBX SDK中的对象总是在右向上的轴系统中创建。场景的轴系统可能需要进行转换以适应您的应用程序的需要。有关更多信息,请参阅FbxAxisSystem类文档。

FBX SDK系统单元重点:

•如果读取应用程序需要确定创建数据的系统单元,则使用原始系统单元作为参考。

•编写应用程序必须明确设置系统单元。

•隐式坐标系以厘米为单位。这意味着坐标(0,0,0),(1,0,0),(1,1,0),(0,1,0)和所有比例/单位因子等于1的平面定义了“1x1cm”平面。

Sence和单元系统分别可以通过以下功能进行改变:

  • FbxAxisSystem::ConvertScene(), which acts on the node transforms (pre-rotation) and animations.

  • FbxSystemUnit::ConvertScene(), which acts on the node transforms (scale) and animations.

  • FbxAxisSystem::ConvertScene(),它作用于节点转换(预旋转)和动画。

  • FbxSystemUnit::ConvertScene(),它作用于节点转换(缩放)和动画。

注意,调用ConvertScene()不会改变网格的顶点值,只会影响节点转换和动画。如果场景已经在required axis系统或required unit系统中,调用ConvertScene()对场景没有影响。例如:

  • 如果场景已经在MayaZUp轴系统中,代码FbxAxisSystem::MayaZUp. convertscene(scene);不会改变任何事。
  • 如果场景的单位已经是厘米,代码FbxSystemUnit::cm.ConvertScene(scene);不会改变任何事。

注意:如果你的场景在单元转换后缩放不当,这可能是由不同的节点继承类型(ETransformInheritType)造成的,特别是继承类型为eINHERIT_Rrs的节点。要避免这个问题,请确保使用以下代码片段中的转换选项避免在这些节点上进行单元转换。这段代码还演示了如何将场景的单位从厘米(cm)转换为米(m):

   if(lScene->GetGlobalSettings().GetSystemUnit() == FbxSystemUnit::cm)
    {
      const FbxSystemUnit::ConversionOptions lConversionOptions = {
        false, /* mConvertRrsNodes */
        true, /* mConvertLimits */
        true, /* mConvertClusters */
        true, /* mConvertLightIntensity */
        true, /* mConvertPhotometricLProperties */
        true  /* mConvertCameraClipPlanes */
      };

      //使用定义的选项将场景转换为米。
      FbxSystemUnit::m.ConvertScene(lScene, lConversionOptions);
    }

全部场景设置

场景的轴系统、系统单元、环境照明和时间设置在其FbxGlobalSettings对象中定义。这个对象是通过FbxScene::GetGlobalSettings()来访问的。

Animation evaluation

场景的FbxAnimEvaluator在特定时间计算场景中每个节点的动画几何变换。它还计算场景的任何其他可设置动画的属性,例如,特定材质的颜色更改。通过FbxScene::GetEvaluator()访问场景的FbxAnimEvaluator。有关详细信息,请参见动画。

纹理和材质管理

可以使用成员函数(如FbxScene::GetMaterial()和FbxScene::getexture())访问和修改在场景中创建的材质(FbxSurfaceMaterial)和纹理(FbxTexture)。有关将材质和纹理与网格一起使用的详细信息,请参见网格、材质和纹理。

角色与角色姿势管理

可以使用FbxScene::GetCharacter()和FbxScene::GetCharacterPose()访问场景中的角色(FbxCharacter)和角色姿势(FbxCharacterPose)。有关如何在场景中定义角色的详细信息,请参考FbxCharacter和FbxCharacterPose的类文档。

数据结构子节点类型

FBX模型的数据

支持场景元素 Supported scene elements

FBX SDK允许您访问、创建或修改场景的以下元素(FbxScene):

网格-FbxMesh

详细级别(LOD)组-FbxlodGroup

摄像头(包括3D立体摄像头)-FbxCamera

灯和聚光灯FbxLight, FbxGobo

非均匀有理样条(Non-Uniform Rational B-Splines) FbxNurbs, FbxNurbsCurve, FbxNurbsSurface, FbxTrimNurbsSurface

几何图形上的纹理映射 - FbxTexture

几何图形上的材质映射-FbxSurfaceMaterial

约束条件 - FbxConstraint

顶点缓存动画对一个几何图形的控制点 - FbxDeformer

场景设置提供上轴(X/Y/Z)和场景缩放(单位)-FbxGlobalSettings, FbxAxisSystem

转换数据包括位置、旋转、比例、父元素- - FbxNode

标记- FbxMarker

Lines - FbxLine

骨架片段(根、肢和肢节)- FbxSkeleton

动画曲线- FbxAnimCurve

est和bind为节点列表(骨骼和几何图形)-- FbxPose

注意:当然你可以可以在扩展里定义更多特殊的属性

以下则是一些常用的标记属性

/**
*根据节点属性的不同,返回字符串。就是返回节点属性的名字
**/
FbxString GetAttributeTypeName(FbxNodeAttribute::EType type)
{
	switch (type)
	{
	case FbxNodeAttribute::eUnknown: return "UnknownAttribute";//未知属性
	case FbxNodeAttribute::eNull: return "Null";
	case FbxNodeAttribute::eMarker: return "marker";  //马克……
	case FbxNodeAttribute::eSkeleton: return "Skeleton"; //骨骼
	case FbxNodeAttribute::eMesh: return "Mesh"; //网格
	case FbxNodeAttribute::eNurbs: return "Nurbs"; //曲线
	case FbxNodeAttribute::ePatch: return "Patch"; //Patch面片
	case FbxNodeAttribute::eCamera: return "Camera"; //摄像机
	case FbxNodeAttribute::eCameraStereo: return "CameraStereo"; //立体
	case FbxNodeAttribute::eCameraSwitcher: return "CameraSwitcher"; //切换器
	case FbxNodeAttribute::eLight: return "Light"; //灯光
	case FbxNodeAttribute::eOpticalReference: return "OpticalReference"; //光学基准,光学参考
	case FbxNodeAttribute::eOpticalMarker: return "OpticalMarker";//光学标记,光学类型(镜面、全局等等)
	case FbxNodeAttribute::eNurbsCurve: return "Nurbs Curve";//NURBS曲线
	case FbxNodeAttribute::eTrimNurbsSurface: return "Trim Nurbs Surface"; //修剪nurbs曲面
	case FbxNodeAttribute::eBoundary: return "Boundary"; //分界线、边界
	case FbxNodeAttribute::eNurbsSurface: return "Nurbs Surface"; //Nurbs曲面
	case FbxNodeAttribute::eShape: return "Shape"; //形状
	case FbxNodeAttribute::eLODGroup: return "LODGroup"; //细节等级群组
	case FbxNodeAttribute::eSubDiv: return "SubDiv";//细分表面细分建模
	default: return "UnknownAttribute";
	}
}

节点层次结构

使用FbxNode::GetChild()和FbxNode::GetParent()等方法遍历节点层次结构。GetChildCount()返回该节点的子节点数。

转换数据

节点的转换数据包括其相对于其父节点的平移、旋转和缩放向量。该数据表示为一组FbxPropertyT对象,可以通过FbxNode::LclTranslation、FbxNode::LclRotation、FbxNode::LclScaling来访问这些对象。注意,“Lcl”前缀表示“local(局部)”。

FbxDouble3 translation = lNode->LclTranslation.Get();
FbxDouble3 rotation = lNode->LclRotation.Get();
FbxDouble3 scaling = lNode->LclScaling.Get();

节点的转换数据可以由FbxLimits对象进行限制,可以通过FbxNode::GetTranslationLimits()、FbxNode::GetRotationLimits()和FbxNode::GetScalingLimits()进行访问。还可以使用FbxConstraint对象约束节点。有关详细信息,请参阅FbxLimits和FbxConstraint的类文档。

一个节点相对于场景全局坐标系的平移、旋转和缩放特性可以表示为一个变换矩阵。这个转换矩阵是通过FbxNode::EvaluateGlobalTransform()获得的。

分组的节点

FbxNode的实例可以在没有绑定FbxNodeAttribute的情况下存在。在这种情况下,可以使用这样的FbxNode在场景中对其子节点进行分组或定位。

创建节点属性

FbxNodeAttribute与FbxNode配对,以定义具有特定位置、旋转和比例的场景元素。如果没有为该节点设置节点属性,则调用FbxNode::GetNodeAttribute()将返回NULL。

下面的代码示例(改编自ExportScene04/main。说明了如何在一个场景中创建一个简单的聚光灯。这里,FbxLight light是FbxNode lightNode的节点属性。有关灯光的更多信息,请参见{灯光}。**

//制造聚光灯。
FbxNode* CreateLight(FbxScene* pScene, char* pName)
{
    FbxLight* light = FbxLight::Create(pScene,pName);

    light->LightType.Set(FbxLight::eSpot);
    light->CastLight.Set(true);

    FbxNode* lightNode = FbxNode::Create(pScene,pName);

    lightNode->SetNodeAttribute(light);

    return lightNode;
}

节点属性

下表展示了一组基本的场景元素及其相关的FbxNodeAttribute子类。可以使用FbxNode::SetNodeAttribute()将这些节点属性与FbxNode配对。有关完整的类层次结构,请参考c++参考指南。

Scene Element FbxNodeAttribute subclass
Camera FbxCamera, FbxCameraStereo
Camera Switcher (custom camera definition in Autodesk MotionBuilder) FbxCameraSwitcher
Light FbxLight
Mesh FbxMesh
Nurb FbxNurbs, FbxNurbsCurve, FbxNurbsSurface, FbxTrimNurbsSurface
Patch / Parametric Surface FbxPatch
Level of Detail Group FbxLodGroup
Marker FbxMarker
Skeleton FbxSkeleton

注意:一些应用程序需要场景图中的空节点类型。FbxNull节点属性用于定义这样的节点类型。注意,FbxNull的实例与NULL值不同。

节点属性类型

可以通过调用FbxNodeAttribute::GetAttributeType()来获得FbxNodeAttribute的类型(FbxNodeAttribute::EAttributeType)。EAttributeType对于将节点属性对象向下强制转换为相应的子类非常有用。

下面的代码示例(改编自ImportScene/main。cxx和ImportScene/DisplayLight.cxx)演示了如何使用开关来显示包含在FbxNode中的FbxLight。

//
// Adapted from ImportScene/DisplayLight.cxx ...
// Display the various properties of the FbxLight contained within the FbxNode.
//
//
//改编自ImportScene/DisplayLight。cxx……
//显示FbxNode中包含的FbxLight的各种属性。
//
void DisplayLight(FbxNode* pNode)
{
    FbxLight* lLight = (FbxLight*) pNode->GetNodeAttribute();

    DisplayString("Light Name: ", (char *) pNode->GetName());
    // ...

    char* lLightTypes[] = { "Point", "Directional", "Spot" };

    DisplayString("    Type: ", lLightTypes[lLight->LightType.Get()]);
    DisplayBool("    Cast Light: ", lLight->CastLight.Get());

    if (!(lLight->FileName.Get().IsEmpty()))
    {
        DisplayString("    Gobo");

        DisplayString("        File Name: \"", lLight->FileName.Get().Buffer(), "\"");
        DisplayBool("        Ground Projection: ", lLight->DrawGroundProjection.Get());
        DisplayBool("        Volumetric Projection: ", lLight->DrawVolumetricLight.Get());
        DisplayBool("        Front Volumetric Projection: ", lLight->DrawFrontFacingVolumetricLight.Get());
    }

    // ...
}

//
// Adapted from ImportScene/main.cxx ...
// Display the contents of a node. Here, we are only interested in
// looking  at the eLight attribute type, which coincides with the
// FbxLight node attribute.
//
//
//改编自ImportScene/main。cxx……
//显示一个节点的内容。在这里,我们只感兴趣
//查看eLight属性类型,它与
// FbxLight节点属性。
//
void DisplayContent(FbxNode* pNode)
{
    FbxNodeAttribute::EAttributeType lAttributeType;
    int i;

    if(pNode->GetNodeAttribute() == NULL)
    {
        printf("NULL Node Attribute\n\n");
    }
    else
    {
        lAttributeType = (pNode->GetNodeAttribute()->GetAttributeType());

        switch (lAttributeType)
        {
            // ...

        case FbxNodeAttribute::eLight:     
            DisplayLight(pNode);
            break;

            // ...
        }
    }

    // ...

    for(i = 0; i < pNode->GetChildCount(); i++)
    {
        DisplayContent(pNode->GetChild(i));
    }
}

这部分的页面

•灯光

•相机

•几何

灯光

创建一个光

FBX SDK中的灯是由FbxLight类抽象的。FbxLight的创建与场景中的任何其他对象一样。

默认情况下,FbxLight沿着节点的负Y轴指向。

//在场景中为我们的灯光创建一个节点。
FbxNode* lLightNode = FbxNode::Create(pScene, "lightNode");

//创造一盏灯。
FbxLight* lLight = FbxLight::Create(pScene,“light”);

//设置灯光节点的节点属性。
lLightNode - > SetNodeAttribute (lLight);

//将场景中的光节点添加到根节点。
FbxNode* lRootNode = pScene->GetRootNode();
lRootNode - > AddChild (lLightNode);

注意:场景的环境照明是在全局设置中定义的,可以通过FbxScene::GetGlobalSettings()进行访问。有关更多信息,请参阅FbxGlobalSettings类文档。

光类型

可以通过设置它的FbxLight::LightType属性来定义灯光的行为。

//将灯光类型设置为聚光灯。
lLight->LightType.Set(FbxLight::eSpot);

下表总结了每种轻型类型的行为。

Light Type (FbxLight::ELightType) Description
FbxLight::eSpot Light spreads in a conical shape from its origin, like a spotlight. The FbxLight::InnerAngle and FbxLight::OuterAngle properties determine the parameters of the cone in degrees.
FbxLight::ePoint Light spreads uniformly in all directions from its origin.
FbxLight::eDirectional Light spreads in a cylindrical shape from its origin.

指向一个光

聚光灯或定向光可以被强制一致地指向场景中的特定目标。为此,光的节点必须使用FbxNode::SetTarget()设置它的目标。目标节点中使用FbxMarker节点属性。

//创建一个节点来包含标记。这将是我们的目标节点。
FbxNode* lTargetNode = FbxNode::创建(pScene,“targetNode”);
//创建一个标记节点属性。
FbxMarker* lMarker = FbxMarker::Create(pScene,“lightMarker”);
//将该标记设置为目标节点的属性。
lTargetNode - > SetNodeAttribute (lMarker);
//设置我们的光节点的目标。
lLightNode - > SetTarget (lTargetNode);

默认情况下,FbxNode使用其正X轴作为瞄准约束。回想一下,在默认情况下,新创建的光点沿着节点的负Y轴。要使光点沿着节点的正X轴,必须使用FbxNode::SetPostTargetRotation()将旋转偏移90度应用到光源的节点。有关更多信息,请参阅FbxNode类文档中的“节点目标管理”一节。

颜色和强度

灯光的颜色是在它的FbxLight:: color属性中定义的。灯光的默认RGB值是(1.0,1.0,1.0),表示为FbxDouble3。

//将灯光的颜色设置为(0,1,0.5)。
lLight->Color.Set(FbxDouble3(0.0, 1.0, 0.5));

光的强度定义在它的FbxLight:: intensity属性中。强度的默认值是100.0,表示为FbxDouble1。

//设置光的强度为50.0
lLight - > Intensity.Set (50.0)

光衰减

光的衰减类型在其FbxLight::DecayType属性中定义。

//将光的衰减类型设置为二次衰减。
lLight->DecayType.Set(FbxLight::eQuadratic);

下表总结了可用的衰减类型。

Decay Type (FbxLight::EDecayType) Description
FbxLight::eNone No decay. The light’s intensity will not diminish with distance.
FbxLight::eLinear Linear decay. The light’s intensity will diminish linearly with the distance from the light.
FbxLight::eQuadratic Quadratic decay. The light’s intensity will diminish with the squared distance from the light. This is the most physically accurate decay rate.
FbxLight::eCubic Cubic decay. The light’s intensity will diminish with the cubed distance from the light.

注意:其他基于距离的衰减特性也可用,如FbxLight:: enablenear和FbxLight:: enablefar。有关更多信息,请参阅FbxLight类文档。

阴影

阴影使用FbxLight::CastShadows布尔属性启用。光的阴影颜色在FbxLight::ShadowColor属性中定义。光的阴影的默认RGB值是(0.0,0.0,0.0),表示为FbxDouble3。阴影纹理也可以使用FbxLight::SetShadowTexture()来应用。

摄像机

创建一个相机

FBX SDK中的摄像机是由FbxCamera类抽象的。用于三维成像的立体照相机是由FbxCameraStereo类抽象出来的。在本主题中,我们将只讨论创建和操作FbxCamera的基本方法。

默认情况下,FbxCamera指向节点的正X轴方向。

//在场景中为我们的相机创建一个节点。
FbxNode* lCameraNode = FbxNode::Create(pScene, "cameraNode");

//创造一盏灯。
FbxCamera* lCamera = FbxCamera::Create(pScene, "camera");

//设置相机节点的节点属性。
lCameraNode->SetNodeAttribute(lCamera);

//将摄像机节点添加到场景的根节点。
FbxNode* lRootNode = pScene->GetRootNode();
lRootNode->AddChild(lCameraNode);

一旦相机被创建,它可以被设置为场景的默认相机。一个场景必须显式地设置它的默认相机,即使场景中只有一个相机。

//设置场景的默认相机。
pScene - > GetGlobalSettings ()。SetDefaultCamera ((char *) lCamera - > GetName ());

指向一个相机

在场景中,摄像机可以被强制一致地指向一个特定的目标。为此,相机节点必须使用FbxNode::SetTarget()设置其目标。目标节点中使用FbxMarker节点属性。

//创建一个节点来包含标记。这将是我们的目标节点。
FbxNode* lTargetNode = FbxNode::创建(pScene,“targetNode”);
//创建一个标记节点属性。
FbxMarker* lMarker = FbxMarker::Create(pScene, "cameraMarker");
//将该标记设置为目标节点的属性。
lTargetNode - > SetNodeAttribute (lMarker);
//设置相机节点的目标。
lCameraNode - > SetTarget (lTargetNode);

注意:有关更多信息,请参阅FbxNode类文档中的“节点目标管理”一节。

默认情况下,FbxCamera::FocusSource属性被设置为FbxCamera::eFocusSrcCameraInterest来保持对相机目标的聚焦。聚焦源也可以设置到相机的特定距离。

//设置相机的聚焦源到一个特定的距离。
lCamera - > FocusSource.Set (FbxCamera:: eFocusSpecificDistance);

//设置到相机的距离为100.0个单位。
//这个距离的默认值是200.0个单位。
lCamera - > FocusDistance.Set (100.0);

相机属性

有关在场景中配置相机的更多信息,请参阅FbxCamera类文档。

几何结构

几何结构

FbxGeometry是支持控制点变形的几何对象的基类。FbxGeometry的实例可以通过FbxNode::SetNodeAttribute()作为节点属性绑定到FbxNode对象。继承自FbxGeometry的类包括FbxMesh、FbxNurb、FbxPatch和FbxLine。要获得从FbxGeometry继承的类的完整列表,请参考c++参考中的类层次结构。

更多信息,请参考网格,材质和纹理部分。

Meshes, Materials and Textures

网格,材料和纹理

本节介绍FBX SDK中的网格、纹理和材质。

Meshes 网格

网格由FbxMesh类抽象。FbxMesh定义了一个控制点列表,在一般文献中也称为顶点。可以将FbxMesh的单个实例绑定到FbxNode的多个实例上,以减少内存消耗。这就是所谓的实例化。场景几何使用层和层元素(FbxLayerElement)的概念来定义法线贴图、材质贴图、纹理贴图等。

Materials 材质

Materials (FbxSurfaceLambert, FbxSurfacePhong)通过FbxNode::AddMaterial()绑定到FbxNode的实例。材质定义了场景中几何体的基本渲染特性,比如它的漫反射、环境和发射色彩特性。每个材质在FbxNode中占有一个特定的索引,FbxMesh::BeginPolygon()可以引用这个索引来定义一个新创建的多边形的材质。

Textures 纹理

纹理(FbxFileTexture, FbxLayeredTexture, FbxProceduralTexture)被连接到材质通道,以定义如何渲染几何图形。FbxFileTexture类使用文件中包含的数据(例如。jpg)来定义纹理的值。

本节中的页面

•网格

•材料

•纹理

网格Meshes

Creating a mesh

FBX SDK中的网格是由FbxMesh类抽象的。网格由一组每面顶点或“控制点”定义,并由一组层定义网格的法线、纹理和材质。下面的代码示例演示如何实例化FbxMesh。

// Create a node for our mesh in the scene.
FbxNode* lMeshNode = FbxNode::Create(pScene, "meshNode");

// Create a mesh.
FbxMesh* lMesh = FbxMesh::Create(pScene, "mesh");

// Set the node attribute of the mesh node.
//设置网格节点的节点属性。
lMeshNode->SetNodeAttribute(lMesh);

// Add the mesh node to the root node in the scene.
FbxNode *lRootNode = pScene->GetRootNode();
lRootNode->AddChild(lMeshNode);

Defining control points定义控制点

FbxMesh的每个面顶点称为控制点。由于FBX SDK中的对象默认是在右手向上的轴系统中创建的,所以网格的顶点应该相应地定义。FbxMesh的一个实例包含一个可以初始化为特定大小的控制点数组。例如,一个立方体的网格需要24个控制点:每个面有4个控制点,而一个立方体有6个面。下面的代码示例初始化多维数据集的控制点数组。

// Define the eight corners of the cube.
// The cube spans from
//    -50 to  50 along the X axis
//      0 to 100 along the Y axis
//    -50 to  50 along the Z axis
//定义立方体的八个角。
//这个立方体跨越
// -50到50,沿着X轴
//沿着Y轴从0到100
// -50到50,沿着Z轴
FbxVector4 vertex0(-50, 0, 50);
FbxVector4 vertex1(50, 0, 50);
FbxVector4 vertex2(50, 100, 50);
FbxVector4 vertex3(-50, 100, 50);
FbxVector4 vertex4(-50, 0, -50);
FbxVector4 vertex5(50, 0, -50);
FbxVector4 vertex6(50, 100, -50);
FbxVector4 vertex7(-50, 100, -50);

// Initialize the control point array of the mesh.
//初始化网格的控制点数组。
lMesh->InitControlPoints(24);
FbxVector4* lControlPoints = lMesh->GetControlPoints();

// Define each face of the cube.
//定义立方体的每个面。
// Face 1
lControlPoints[0] = vertex0;
lControlPoints[1] = vertex1;
lControlPoints[2] = vertex2;
lControlPoints[3] = vertex3;
// Face 2
lControlPoints[4] = vertex1;
lControlPoints[5] = vertex5;
lControlPoints[6] = vertex6;
lControlPoints[7] = vertex2;
// Face 3
lControlPoints[8] = vertex5;
lControlPoints[9] = vertex4;
lControlPoints[10] = vertex7;
lControlPoints[11] = vertex6;
// Face 4
lControlPoints[12] = vertex4;
lControlPoints[13] = vertex0;
lControlPoints[14] = vertex3;
lControlPoints[15] = vertex7;
// Face 5
lControlPoints[16] = vertex3;
lControlPoints[17] = vertex2;
lControlPoints[18] = vertex6;
lControlPoints[19] = vertex7;
// Face 6
lControlPoints[20] = vertex1;
lControlPoints[21] = vertex0;
lControlPoints[22] = vertex4;
lControlPoints[23] = vertex5;

有关控制点管理的更多信息,请参阅FbxGeometry类文档。FbxGeometry是用于定义场景几何的FbxMesh、FbxLine、FbxNurb等类的父类。

注意:请注意,场景轴转换不影响网格的顶点值。有关更多信息,请参见场景轴和单元转换。

Assigning normals指定法线

网格的法向量是在FbxLayerElementNormal实例中定义的。层元素如法线可以映射到网格的表面以多种方式,如通过控制点(FbxLayerElement:: eByControlPoint),由多边形顶点(FbxLayerElement:: eByPolygonVertex),由多边形(FbxLayerElement:: eByPolygon),由边缘(FbxLayerElement:: eByEdge),或一个映射协调整个表面(FbxLayerElement:: eAllSame)。有关更多信息,请参见FbxLayerElement::EMappingMode。

给定一个法向量数组和一个控制点数组,我们可以指定控制点数组如何引用法向量数组。这是由FbxLayerElement::EReferenceMode中指定的FbxLayerElement::SetReferenceMode()定义的。eDirect模式将法向量数组中的第n个元素映射到控制点数组中的第n个元素。

Reference Mode (FbxLayerElement::EReferenceMode) Description
FbxLayerElement::eDirect This indicates that the mapping information for the n’th element is found in the n’th place of FbxLayerElementTemplate::mDirectArray.
FbxLayerElement::eIndex This symbol is kept for backward compatibility with FBX v5.0 files. In FBX v6.0 and higher, this symbol is replaced with FbxLayerElement::eIndexToDirect.
FbxLayerElement::eIndexToDirect This indicates that each element of FbxLayerElementTemplate::mIndexArray contains an index referring to an element in FbxLayerElementTemplate::mDirectArray.

在下面的代码示例中,我们将研究如何使用EReferenceMode::eDirect引用模式将法线的每个控制点映射(FbxLayerElement::eByControlPoint)应用到网格上。注意,调用FbxLayerElementArrayTemplate::Add()的顺序与上面定义的控制点的顺序一致。

//定义沿每个轴的法向量。
FbxVector4 lNormalXPos( 1,  0,  0);
FbxVector4 lNormalXNeg(-1,  0,  0);
FbxVector4 lNormalYPos( 0,  1,  0);
FbxVector4 lNormalYNeg( 0, -1,  0);
FbxVector4 lNormalZPos( 0,  0,  1);
FbxVector4 lNormalZNeg( 0,  0, -1);

//为网格创建0层,如果它还不存在的话。
//这是我们定义法线的地方。
FbxLayer lLayer = lMesh->GetLayer(0);
if(lLayer == NULL) {
    lMesh->CreateLayer();
    lLayer = lMesh->GetLayer(0);
}

//创建一个普通的图层。
FbxLayerElementNormal* lLayerElementNormal= FbxLayerElementNormal::Create(lMesh, "");

//设置其映射模式,将每个法向量映射到每个控制点。
lLayerElementNormal->SetMappingMode(FbxLayerElement::eByControlPoint);

// Set the reference mode of so that the n'th element of the normal array maps to the n'th
// element of the control point array.
//设置参考模式,使正常数组的第n个元素映射到第n个元素
//元素的控制点数组。
lLayerElementNormal->SetReferenceMode(FbxLayerElement::eDirect);

// Assign the normal vectors in the same order the control points were defined for the mesh.
//以相同的顺序分配法向量,为网格定义控制点。
// Face 1
lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
// Face 2
lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
// Face 3
lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
// Face 4
lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
// Face 5
lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
// Face 6
lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);
lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);

// Finally, we set layer 0 of the mesh to the normal layer element.
//最后,我们将网格的第0层设置为正常的图层元素。
lLayer->SetNormals(lLayerElementNormal);

本节中的页面•实例化——共享一个网格

材质Materials

注意:本主题中的示例代码也出现在ExportScene03/main中。cxx示例程序。

Creating a material创建一个材料

fbxsurfacemataterial class is The base class for Lambertian (FbxSurfaceLambert) and Phong (FbxSurfacePhong) materials。在下面的代码示例中,我们创建了5个材质,并通过FbxNode::AddMaterial()将它们添加到FbxMesh的FbxNode中。一旦材料被绑定到一个节点上,它们就可以被映射到网格的多边形上。

// Create materials for pyramid.
//为锥体创造材质
void CreateMaterials(FbxScene* pScene, FbxMesh* pMesh)
{
    int i;

    for (i = 0; i < 5; i++ )
    {
        FbxString lMaterialName = "material";
        FbxString lShadingName = "Phong";
        lMaterialName += i;
        FbxDouble3 lBlack(0.0, 0.0, 0.0);
        FbxDouble3 lRed(1.0, 0.0, 0.0);
        FbxDouble3 lColor;
        FbxSurfacePhong *lMaterial = FbxSurfacePhong::Create(pScene, lMaterialName.Buffer());


        // Generate primary and secondary colors.
        //原色和二次色
        lMaterial->Emissive.Set(lBlack);
        lMaterial->Ambient.Set(lRed);
        lColor = FbxDouble3(i > 2   ? 1.0 : 0.0,
            i > 0 && i < 4 ? 1.0 : 0.0,
            i % 2   ? 0.0 : 1.0);
        lMaterial->Diffuse.Set(lColor);
        lMaterial->TransparencyFactor.Set(0.0);
        lMaterial->ShadingModel.Set(lShadingName);
        lMaterial->Shininess.Set(0.5);

        //get the node of mesh, add material for it.
        //获取网格节点,添加材质。
        FbxNode* lNode = pMesh->GetNode();
        if(lNode)             
            lNode->AddMaterial(lMaterial);
    }  
}

Example: Creating a square pyramid with materials示例:使用材质创建方形棱锥体

下面的代码示例使用上面定义的CreateMaterials()函数将五种材质绑定到正方形棱锥体的五个面。我们首先定义锥体的控制点和法线。

//使用材质创建棱锥体。

FbxNode* CreatePyramidWithMaterials(FbxScene* pScene, char* pName)
{
    int i, j;
    FbxMesh* lMesh = FbxMesh::Create(pScene, pName);

    FbxVector4 vertex0(-50, 0, 50);
    FbxVector4 vertex1(50, 0, 50);
    FbxVector4 vertex2(50, 0, -50);
    FbxVector4 vertex3(-50, 0, -50);
    FbxVector4 vertex4(0, 100, 0);

    FbxVector4 lNormalP0(0, 1, 0);
    FbxVector4 lNormalP1(0, 0.447, 0.894);
    FbxVector4 lNormalP2(0.894, 0.447, 0);
    FbxVector4 lNormalP3(0, 0.447, -0.894);
    FbxVector4 lNormalP4(-0.894, 0.447, 0);

    //创建控制点。
    lMesh->InitControlPoints(16);
    FbxVector4* lControlPoints = lMesh->GetControlPoints();

    lControlPoints[0] = vertex0;
    lControlPoints[1] = vertex1;
    lControlPoints[2] = vertex2;
    lControlPoints[3] = vertex3;
    lControlPoints[4] = vertex0;
    lControlPoints[5] = vertex1;
    lControlPoints[6] = vertex4;
    lControlPoints[7] = vertex1;
    lControlPoints[8] = vertex2;
    lControlPoints[9] = vertex4;
    lControlPoints[10] = vertex2;
    lControlPoints[11] = vertex3;
    lControlPoints[12] = vertex4;
    lControlPoints[13] = vertex3;
    lControlPoints[14] = vertex0;
    lControlPoints[15] = vertex4;

    //为每个控制点指定法线。

    FbxGeometryElementNormal* lNormalElement= lMesh->CreateElementNormal();
    lNormalElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
    lNormalElement->SetReferenceMode(FbxGeometryElement::eDirect);

    lNormalElement->GetDirectArray().Add(lNormalP0);
    lNormalElement->GetDirectArray().Add(lNormalP0);
    lNormalElement->GetDirectArray().Add(lNormalP0);
    lNormalElement->GetDirectArray().Add(lNormalP0);
    lNormalElement->GetDirectArray().Add(lNormalP1);
    lNormalElement->GetDirectArray().Add(lNormalP1);
    lNormalElement->GetDirectArray().Add(lNormalP1);
    lNormalElement->GetDirectArray().Add(lNormalP2);
    lNormalElement->GetDirectArray().Add(lNormalP2);
    lNormalElement->GetDirectArray().Add(lNormalP2);
    lNormalElement->GetDirectArray().Add(lNormalP3);
    lNormalElement->GetDirectArray().Add(lNormalP3);
    lNormalElement->GetDirectArray().Add(lNormalP3);
    lNormalElement->GetDirectArray().Add(lNormalP4);
    lNormalElement->GetDirectArray().Add(lNormalP4);
    lNormalElement->GetDirectArray().Add(lNormalP4);

然后在网格中创建一个新的材质层元素(fbxgeometry yelementmaterial),以指定材质被绑定到网格中多边形的控制点上。函数FbxMesh::BeginPolygon()使用一个索引来确定哪些材质将被绑定到新的多边形。此索引是指存储在FbxNode中的材料的位置。

// 多边形顶点的数组。
    int lPolygonVertices[] = { 0, 3, 2, 1,
        4, 5, 6,
        7, 8, 9,
        10, 11, 12,
        13, 14, 15 };

    // 设定材料的映射。
    FbxGeometryElementMaterial* lMaterialElement = lMesh->CreateElementMaterial();
    lMaterialElement->SetMappingMode(FbxGeometryElement::eByPolygon);
    lMaterialElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect);

    // 创建多边形。分配材料指标。

    // 长方锥体的基础。
    lMesh->BeginPolygon(0); // Material index.

    for(j = 0; j < 4; j++)
    {
        lMesh->AddPolygon(lPolygonVertices[j]); // Control point index.
    }

    lMesh->EndPolygon ();

    // Pyramid sides.面
    for(i = 1; i < 5; i++)
    {
        lMesh->BeginPolygon(i); // Material index.

        for(j = 0; j < 3; j++)
        {
            lMesh->AddPolygon(lPolygonVertices[4 + 3*(i - 1) + j]); // Control point index.控制点指数。
        }

        lMesh->EndPolygon ();
    }


    FbxNode* lNode = FbxNode::Create(pScene,pName);

    lNode->SetNodeAttribute(lMesh);

    CreateMaterials(pScene, lMesh);

    return lNode;
}

使用硬件着色器创建材质Using hardware shaders to create materials

CGFX和DirectX硬件着色器是由FBX SDK从2010版开始支持的。您可以使用CGFX或DirectX实现,使用类FbxImplementation和类FbxBindingTable来设置素材。

纹理Textures

注意:本主题中的示例代码也出现在ExportScene03/main中。cxx示例程序。

从文件创建纹理

FbxTexture是FBX SDK中纹理的基类。纹理依赖于底层材质来影响几何图形的渲染方式。在下面的代码示例中,我们将一个Phong材质的扩散、环境和发射通道与三个不同的FbxFileTexture实例相关联。FbxFileTexture表示从文件中加载的任何纹理。有关在文件中引用嵌入式或非嵌入式媒体的更多信息,请参见引用媒体。

// Create texture for cube.
//为立方体创建纹理。
void CreateTexture(FbxScene* pScene, FbxMesh* pMesh)
{
    // A texture need to be connected to a property on the material,
    // so let's use the material (if it exists) or create a new one
    //纹理需要连接到材质上的属性,
	//因此,让我们使用这个材料(如果它存在的话)或者创建一个新的
    FbxSurfacePhong* lMaterial = NULL;

    //get the node of mesh, add material for it.
    //获取网格节点,添加材质。
    FbxNode* lNode = pMesh->GetNode();
    if(lNode)
    {
        lMaterial = lNode->GetSrcObject<FbxSurfacePhong>(0);
        if (lMaterial == NULL)
        {
            FbxString lMaterialName = "toto";
            FbxString lShadingName  = "Phong";
            FbxDouble3 lBlack(0.0, 0.0, 0.0);
            FbxDouble3 lRed(1.0, 0.0, 0.0);
            FbxDouble3 lDiffuseColor(0.75, 0.75, 0.0);
            lMaterial = FbxSurfacePhong::Create(pScene, lMaterialName.Buffer());

            // Generate primary and secondary colors.
            //生成原色和二次色。
            lMaterial->Emissive           .Set(lBlack);
            lMaterial->Ambient            .Set(lRed);
            lMaterial->AmbientFactor      .Set(1.);
            // Add texture for diffuse channel
            lMaterial->Diffuse           .Set(lDiffuseColor);
            lMaterial->DiffuseFactor     .Set(1.);
            lMaterial->TransparencyFactor.Set(0.4);
            lMaterial->ShadingModel      .Set(lShadingName);
            lMaterial->Shininess         .Set(0.5);
            lMaterial->Specular          .Set(lBlack);
            lMaterial->SpecularFactor    .Set(0.3);

            lNode->AddMaterial(lMaterial);
        }
    }

    FbxFileTexture* lTexture = FbxFileTexture::Create(pScene,"Diffuse Texture");

    // Set texture properties.纹理属性
    lTexture->SetFileName("scene03.jpg"); // Resource file is in current directory.资源文件在当前目录中。
    lTexture->SetTextureUse(FbxTexture::eStandard);
    lTexture->SetMappingType(FbxTexture::eUV);
    lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
    lTexture->SetSwapUV(false);
    lTexture->SetTranslation(0.0, 0.0);
    lTexture->SetScale(1.0, 1.0);
    lTexture->SetRotation(0.0, 0.0);

    // don't forget to connect the texture to the corresponding property of the material
    //别忘了把纹理和材质的相应属性联系起来
    if (lMaterial)
        lMaterial->Diffuse.ConnectSrcObject(lTexture);

    lTexture = FbxFileTexture::Create(pScene,"Ambient Texture");

    // Set texture properties.
    //设置纹理属性。
    lTexture->SetFileName("gradient.jpg"); // Resource file is in current directory.
    lTexture->SetTextureUse(FbxTexture::eStandard);
    lTexture->SetMappingType(FbxTexture::eUV);
    lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
    lTexture->SetSwapUV(false);
    lTexture->SetTranslation(0.0, 0.0);
    lTexture->SetScale(1.0, 1.0);
    lTexture->SetRotation(0.0, 0.0);

    // don't forget to connect the texture to the corresponding property of the material
    if (lMaterial)
        lMaterial->Ambient.ConnectSrcObject(lTexture);

    lTexture = FbxFileTexture::Create(pScene,"Emissive Texture");

    // Set texture properties.
    lTexture->SetFileName("spotty.jpg"); // Resource file is in current directory.
    lTexture->SetTextureUse(FbxTexture::eStandard);
    lTexture->SetMappingType(FbxTexture::eUV);
    lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
    lTexture->SetSwapUV(false);
    lTexture->SetTranslation(0.0, 0.0);
    lTexture->SetScale(1.0, 1.0);
    lTexture->SetRotation(0.0, 0.0);

    // don't forget to connect the texture to the corresponding property of the material
    if (lMaterial)
        lMaterial->Emissive.ConnectSrcObject(lTexture);
}

Example: Creating a textured cube示例:创建一个纹理化的多维数据集

The following code sample makes use of the CreateTexture() function we defined above. We first begin by defining the control points and normals of the cube.

下面的代码示例使用了我们在上面定义的“CreateTexture()”函数。我们首先定义立方体的控制点和法线。

// Create a cube with a texture.//创建一个有纹理的立方体。
FbxNode* CreateCubeWithTexture(FbxScene* pScene, char* pName)
{
    int i, j;
    FbxMesh* lMesh = FbxMesh::Create(pScene,pName);

    FbxVector4 vertex0(-50, 0, 50);
    FbxVector4 vertex1(50, 0, 50);
    FbxVector4 vertex2(50, 100, 50);
    FbxVector4 vertex3(-50, 100, 50);
    FbxVector4 vertex4(-50, 0, -50);
    FbxVector4 vertex5(50, 0, -50);
    FbxVector4 vertex6(50, 100, -50);
    FbxVector4 vertex7(-50, 100, -50);

    FbxVector4 lNormalXPos(1, 0, 0);
    FbxVector4 lNormalXNeg(-1, 0, 0);
    FbxVector4 lNormalYPos(0, 1, 0);
    FbxVector4 lNormalYNeg(0, -1, 0);
    FbxVector4 lNormalZPos(0, 0, 1);
    FbxVector4 lNormalZNeg(0, 0, -1);

    // Create control points.创建控制点。
    lMesh->InitControlPoints(24);
    FbxVector4* lControlPoints = lMesh->GetControlPoints();

    lControlPoints[0] = vertex0;
    lControlPoints[1] = vertex1;
    lControlPoints[2] = vertex2;
    lControlPoints[3] = vertex3;
    lControlPoints[4] = vertex1;
    lControlPoints[5] = vertex5;
    lControlPoints[6] = vertex6;
    lControlPoints[7] = vertex2;
    lControlPoints[8] = vertex5;
    lControlPoints[9] = vertex4;
    lControlPoints[10] = vertex7;
    lControlPoints[11] = vertex6;
    lControlPoints[12] = vertex4;
    lControlPoints[13] = vertex0;
    lControlPoints[14] = vertex3;
    lControlPoints[15] = vertex7;
    lControlPoints[16] = vertex3;
    lControlPoints[17] = vertex2;
    lControlPoints[18] = vertex6;
    lControlPoints[19] = vertex7;
    lControlPoints[20] = vertex1;
    lControlPoints[21] = vertex0;
    lControlPoints[22] = vertex4;
    lControlPoints[23] = vertex5;


    // We want to have one normal for each vertex (or control point),
    // so we set the mapping mode to eByControlPoint.
    //我们希望每个顶点(或控制点)都有一条法线,因此我们将映射模式设置为eByControlPoint。
    FbxGeometryElementNormal* lGeometryElementNormal= lMesh->CreateElementNormal();

    lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint);

    // Here are two different ways to set the normal values.
    //这里有两种不同的设置正常值的方法。
    bool firstWayNormalCalculations=true;
    if (firstWayNormalCalculations)
    {    
        //第一种方法是设置实际的正常值
		//每个管制站。
        lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect);

        lGeometryElementNormal->GetDirectArray().Add(lNormalZPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYNeg);
    }
    else
    {
        //第二种方法是求法线的可能值
		//在直接数组中,并设置该值的索引
		//在每个控制点的索引数组中。
        lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eIndexToDirect);

        //将6个不同的法线添加到直接数组中
        lGeometryElementNormal->GetDirectArray().Add(lNormalZPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYNeg);

       //现在对于每个控制点,我们需要指定使用哪个法线
        lGeometryElementNormal->GetIndexArray().Add(0); // index of lNormalZPos in the direct array.//直接数组中lNormalZPos的索引。
        lGeometryElementNormal->GetIndexArray().Add(0); // index of lNormalZPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(0); // index of lNormalZPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(0); // index of lNormalZPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(1); // index of lNormalXPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(1); // index of lNormalXPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(1); // index of lNormalXPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(1); // index of lNormalXPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(2); // index of lNormalZNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(2); // index of lNormalZNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(2); // index of lNormalZNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(2); // index of lNormalZNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(3); // index of lNormalXNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(3); // index of lNormalXNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(3); // index of lNormalXNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(3); // index of lNormalXNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(4); // index of lNormalYPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(4); // index of lNormalYPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(4); // index of lNormalYPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(4); // index of lNormalYPos in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(5); // index of lNormalYNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(5); // index of lNormalYNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(5); // index of lNormalYNeg in the direct array.
        lGeometryElementNormal->GetIndexArray().Add(5); // index of lNormalYNeg in the direct array.
    }

We then call FbxMesh::CreateElementUV() to create three new texture layers in the mesh - one for each of the diffuse, ambient, and emissive material channels. The resulting texture layer elements (FbxGeometryElementUV) define how to map the texture’s UV coordinates to each of the polygon’s vertices. Lastly, we call FbxNode::SetShadingMode() with the value FbxNode::eTextureShading to allow our textures to be rendered in the scene.

然后我们调用“FbxMesh::CreateElementUV()”来在网格中创建三个新的纹理层——分别用于漫反射环境发射材质通道。生成的纹理层元素(’ fbxgeometry yelementuv ‘)定义如何将纹理的UV坐标映射到多边形的每个顶点。最后,我们调用’ FbxNode::SetShadingMode() ‘的值’ FbxNode:: etextuding '来允许我们的纹理在场景中渲染。

    // Array of polygon vertices.多边形顶点的数组。
    int lPolygonVertices[] = { 0, 1, 2, 3,
        4, 5, 6, 7,
        8, 9, 10, 11,
        12, 13, 14, 15,
        16, 17, 18, 19,
        20, 21, 22, 23 };

    // Create UV for Diffuse channel为漫反射通道创建UV
    FbxGeometryElementUV* lUVDiffuseElement = lMesh->CreateElementUV( "DiffuseUV");
    K_ASSERT( lUVDiffuseElement != NULL);
    lUVDiffuseElement->SetMappingMode(FbxGeometryElement::eByPolygonVertex);
    lUVDiffuseElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect);

    FbxVector2 lVectors0(0, 0);
    FbxVector2 lVectors1(1, 0);
    FbxVector2 lVectors2(1, 1);
    FbxVector2 lVectors3(0, 1);

    lUVDiffuseElement->GetDirectArray().Add(lVectors0);
    lUVDiffuseElement->GetDirectArray().Add(lVectors1);
    lUVDiffuseElement->GetDirectArray().Add(lVectors2);
    lUVDiffuseElement->GetDirectArray().Add(lVectors3);


    // Create UV for Ambient channel//创建环境通道的UV
    FbxGeometryElementUV* lUVAmbientElement = lMesh->CreateElementUV("AmbientUV");

    lUVAmbientElement->SetMappingMode(FbxGeometryElement::eByPolygonVertex);
    lUVAmbientElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect);

    lVectors0.Set(0, 0);
    lVectors1.Set(1, 0);
    lVectors2.Set(0, 0.418586879968643);
    lVectors3.Set(1, 0.418586879968643);

    lUVAmbientElement->GetDirectArray().Add(lVectors0);
    lUVAmbientElement->GetDirectArray().Add(lVectors1);
    lUVAmbientElement->GetDirectArray().Add(lVectors2);
    lUVAmbientElement->GetDirectArray().Add(lVectors3);

    // Create UV for Emissive channel//为发射通道创建UV
    FbxGeometryElementUV* lUVEmissiveElement = lMesh->CreateElementUV("EmissiveUV");

    lUVEmissiveElement->SetMappingMode(FbxGeometryElement::eByPolygonVertex);
    lUVEmissiveElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect);

    lVectors0.Set(0.2343, 0);
    lVectors1.Set(1, 0.555);
    lVectors2.Set(0.333, 0.999);
    lVectors3.Set(0.555, 0.666);

    lUVEmissiveElement->GetDirectArray().Add(lVectors0);
    lUVEmissiveElement->GetDirectArray().Add(lVectors1);
    lUVEmissiveElement->GetDirectArray().Add(lVectors2);
    lUVEmissiveElement->GetDirectArray().Add(lVectors3);

    //Now we have set the UVs as eIndexToDirect reference and in eByPolygonVertex mapping mode
    //we must update the size of the index array.
	//现在我们已经将UVs设置为eIndexToDirect reference和eByPolygonVertex映射模式
	//我们必须更新索引数组的大小。
    lUVDiffuseElement->GetIndexArray().SetCount(24);
    lUVAmbientElement->GetIndexArray().SetCount(24);
    lUVEmissiveElement->GetIndexArray().SetCount(24);



    // Create polygons. Assign texture and texture UV indices.//创建多边形。分配纹理和纹理紫外线指数。
    for(i = 0; i < 6; i++)
    {
        //we won't use the default way of assigning textures, as we have
        //textures on more than just the default (diffuse) channel.
        //我们不会使用默认的纹理分配方式
		//纹理不只是默认的(漫反射)通道。
        lMesh->BeginPolygon(-1, -1, false);



        for(j = 0; j < 4; j++)
        {
            //this function points
            lMesh->AddPolygon(lPolygonVertices[i*4 + j] // Control point index.
            );
            //Now we have to update the index array of the UVs for diffuse, ambient and emissive//现在我们必须更新uv的索引数组,包括漫射、环境和发射
            lUVDiffuseElement->GetIndexArray().SetAt(i*4+j, j);
            lUVAmbientElement->GetIndexArray().SetAt(i*4+j, j);
            lUVEmissiveElement->GetIndexArray().SetAt(i*4+j, j);

        }

        lMesh->EndPolygon ();
    }

    FbxNode* lNode = FbxNode::Create(pScene,pName);

    lNode->SetNodeAttribute(lMesh);
    lNode->SetShadingMode(FbxNode::eTextureShading);

    CreateTexture(pScene, lMesh);

    return lNode;
}

Pages in this section

全局和局部变换矩阵

调用FbxNode::EvaluateGlobalTransform()和FbxNode::EvaluateLocalTransform()分别得到节点的全局和局部变换矩阵:

//获取节点的默认全局变换矩阵。
FbxAMatrix& lGlobalTransform = lNode->EvaluateGlobalTransform();

//获取节点的默认局部转换矩阵。
FbxAMatrix& lLocalTransform = lNode->EvaluateLocalTransform();

这些成员函数相当于使用FbxScene的动画求值器FbxAnimEvaluator::GetNodeGlobalTransform()和FbxAnimEvaluator::GetNodeLocalTransform()函数:

//获取场景的动画评估器。
FbxAnimEvaluator* lEvaluator = lScene->getEvaluator();

//获取节点的默认全局变换矩阵。
FbxAMatrix& lGlobalTransform = lEvaluator->GetNodeGlobalTransform(lNode);

//获取节点的默认局部转换矩阵。
FbxAMatrix& lLocalTransform = lEvaluator->GetNodeLocalTransform(lNode);

通过传递FbxTime对象,可以获得特定时间点的动画节点的转换矩阵。

FbxTime lTime;

//把时间定在两秒钟。
lTime.SetSecondDouble((float) 2);

//在2秒内得到节点的全局变换矩阵。
FbxAMatrix& lGlobalTransform = lNode->EvaluateGlobalTransform(lTime);

注意:无法在FBX SDK中显式设置节点的全局变换矩阵。

几何变换性质

FbxNode的几何变换属性(FbxNode::GeometricTranslation, FbxNode::GeometricRotation, FbxNode::GeometricScaling)描述了FbxNodeAttribute如何从FbxNode的本地参考框架中偏移。在计算FbxNode的局部转换之后,这些几何变换应用于FbxNodeAttribute,而不是在节点层次结构中继承。

注意:几何变换性质与3ds Max如何表示主元信息有关。它可以与3ds Max的对象偏移转换相媲美。

将转换组件烘焙为标准TRS转换

FbxNode::ConvertPivotAnimationRecursive()函数允许烘焙变换组件:旋转前和旋转后、旋转和缩放轴和偏移-在标准变换内部-平移、旋转和缩放。这是一段代码。

// Do this setup for each node (FbxNode).
// We set up what we want to bake via ConvertPivotAnimationRecursive.
// When the destination is set to 0, baking will occur.
// When the destination value is set to the source’s value, the source values will be retained and not baked.

//为每个节点(FbxNode)执行这个设置。
//我们通过ConvertPivotAnimationRecursive设置要烘焙的东西。
//当目标设置为0时,就会发生烘烤。
//当目标值设置为源值时,将保留源值而不进行烘焙。
{
    FbxVector4 lZero(0,0,0);

    // Activate pivot converting//激活枢轴转换
    pNode->SetPivotState(FbxNode::eSourcePivot, FbxNode::ePivotActive);
    pNode->SetPivotState(FbxNode::eDestinationPivot, FbxNode::ePivotActive);

    // We want to set all these to 0 and bake them into the transforms.
    //我们想把所有这些设置为0,然后把它们放入变换中。
    pNode->SetPostRotation(FbxNode::eDestinationPivot, lZero);
    pNode->SetPreRotation(FbxNode::eDestinationPivot, lZero);
    pNode->SetRotationOffset(FbxNode::eDestinationPivot, lZero);
    pNode->SetScalingOffset(FbxNode::eDestinationPivot, lZero);
    pNode->SetRotationPivot(FbxNode::eDestinationPivot, lZero);
    pNode->SetScalingPivot(FbxNode::eDestinationPivot, lZero);

    // This is to import in a system that supports rotation order.
    // If rotation order is not supported, do this instead:
    //这将导入一个支持旋转顺序的系统。
	//如果不支持旋转顺序,可以这样做:
    // pNode->SetRotationOrder(FbxNode::eDestinationPivot, FbxNode::eEulerXYZ);
    ERotationOrder lRotationOrder;
    pNode->GetRotationOrder(FbxNode::eSourcePivot, lRotationOrder);
    pNode->SetRotationOrder(FbxNode::eDestinationPivot, lRotationOrder);

    // Similarly, this is the case where geometric transforms are supported by the system.
    // If geometric transforms are not supported, set them to zero instead of
    // the source’s geometric transforms.
    // Geometric transform = local transform, not inherited by children.
    //类似地,这是系统支持几何变换的情况。
	//如果不支持几何变换,将它们设置为0而不是
	//源的几何变换。
	//几何变换=局部变换,不被子类继承。
    pNode->SetGeometricTranslation(FbxNode::eDestinationPivot, pNode->GetGeometricTranslation(FbxNode::eSourcePivot));
    pNode->SetGeometricRotation(FbxNode::eDestinationPivot, pNode->GetGeometricRotation(FbxNode::eSourcePivot));
    pNode->SetGeometricScaling(FbxNode::eDestinationPivot, pNode->GetGeometricScaling(FbxNode::eSourcePivot));

    //四元数的Idem。
    pNode->SetUseQuaternionForInterpolation(FbxNode::eDestinationPivot, pNode->GetUseQuaternionForInterpolation(FbxNode::eSourcePivot));
}

//设置完成后,调用ConvertPivotAnimationRecursive到场景的根节点。
//抽样率例如30.0。
mScene->GetRootNode()->ConvertPivotAnimationRecursive(FbxNode::eDestinationPivot, lSamplingRate );

注意:为了避免重新计算数据透视矩阵和在每次改变一个参数时更新动画曲线,FBX SDK只在对ResetPivotSetAndConvertAnimation()或ConvertPivotAnimationRecursive()进行显式调用时应用新设置。

计算变换矩阵

FBX SDK和Maya使用相同的公式来计算转换矩阵。然而,3ds Max使用了一个不同的公式。

注意:FBXmporters and exporters为3ds Max自动转换转换矩阵到和从3ds Max。

FBX and Maya

下式表示FBX SDK和Maya如何计算一个节点的变换矩阵:

任何给定的向量都是这样变换的

注释:

  • 这个公式的作用是:对任意给定的向量先缩放,然后旋转,最后平移。
  • R矩阵考虑了旋转顺序。由于矩阵的数学性质,R是Rx、Ry和Rz的一种可能组合的结果(每一种都是矩阵)。例如,对于XYZ的默认旋转顺序,R = Rz * Ry * Rx。

3ds Max

下式表示3ds Max如何计算节点的转换矩阵。公式中的所有项与FBX和Maya中的相同,除了表示几何变换的三项;

Where the term: Is a 4 x 4 matrix that contains:
WorldTransform Transformation matrix of the node
ParentWorldTransform Transformation matrix of the parent node
T Translation
R Rotation
S Scaling
OT Geometric transform translation
OR Geometric transform rotation
OS Geometric transform scaling

注:

  • 几何平移、几何旋转和几何缩放与3ds Max中的对象偏移概念相关。这些几何变换将在节点变换之后应用于节点属性。
  • 几何变换不是继承的:ParentWorldTransform不包含WorldTransform的父节点的OT、OR和OS。
  • 在FBX SDK中实现的几何变换是FbxNode对象的三个属性:FbxNode::GeometricTranslation、FbxNode::GeometricRotation和FbxNode::GeometricScaling。

Animation动画

本节介绍FBX SDK动画系统,动画数据结构之间的相互关系,以及当前的数据结构与FBX SDK 2010中用于动画的数据结构之间的区别。此外,本节还解释了混合、提取和评估动画数据的过程。

本节的页面

•动画数据结构

•混合动画

•从FBX文件中提取动画数据

•评估场景中的动画

•例子

动画数据结构 Animation data structures

FBX SDK将这些数据结构用于动画:动画堆栈(FbxAnimStack)、动画层(FbxAnimLayer)、动画曲线节点(FbxAnimCurveNode)、动画曲线(FbxAnimCurve)和动画曲线键(FbxAnimCurveKey)。在FBX场景(FbxScene)中,数据结构通过对象到对象(OO)连接和对象到属性(OP)连接彼此连接。有关这些数据结构的更多信息,请参见动画类及其相互关系。

FBX SDK支持混合动画,这是Autodesk MotionBuilder、Autodesk Maya和其他应用程序的一个功能。混合创建一个平滑的过渡从一个动画到另一个。例如,如果您有一组动画曲线使角色能够行走,而另一组动画曲线使角色能够运行,那么您可以通过将行走动画与运行动画混合在一起来创建从行走到运行的平稳过渡。当混合动画,你会得到最好的结果,如果你混合动画与类似的运动。有关使用动画数据结构进行混合的说明,请参阅混合动画。

有关使用这些数据结构创建动画(非混合)的示例,请参见动画化节点。

在FBX SDK 2011之前,用于动画的数据结构有take节点、take节点容器、take info等。请参阅迁移到新的动画数据结构,以了解当前数据结构与FBX SDK 2010中用于动画的数据结构之间的区别。

本节的页面

•动画类及其相互关系

•为动画迁移到新的数据结构

•编写并使用自己的评价者

•在顶点缓存中存储动画

父页面:动画

动画类及其相互关系Animation classes and their interrelationships

以下是在场景中存储动画相关数据的类:

Class Description
FbxScene A scene can contain one or more animation stacks. You do not need to use animation stacks or other animation classes if there is no animation in the scene.
FbxAnimStack The animation stack is the highest-level container for the animation data and contains one or more animation layers.
FbxAnimLayer An animation layer contains one or more animation curve nodes that are connected to the animation curves. The animation stack must have at least one animation layer known as the base layer. For blended animation, more than one animation layer is required.
FbxAnimCurve An animation curve, also known as function curve or FCurve defines how a FBX property (FbxProperty) of a FBX object is animated or different from the default value in the animation layer. The animation curves are connected to the animation curve nodes. The same animation curve can be connected to multiple animation curve nodes regardless of the referred FBX property. Accordingly, one animation curve can animate many FBX properties of many FBX objects. The values in the animation curves are dependent on the FBX property type and is validated by the application. Animation curves are not mandatory. To save the memory and processing time, FBX properties that are not animated can be left without using the animation curves.
FbxAnimCurveNode An animation curve node (FbxAnimCurveNode) is the connection point between the animation curves and the FBX properties. To connect an animation curve to a FBX property, you can connect the animation curve and the FBX property to one animation curve node. An animation curve node can be connected to only one FBX property of one FBX object. FBX properties such as FbxNode::LclTranslation contain more than one value (X, Y, Z). If you create an animation curve node by calling FbxAnimCurveNode::CreateTypedCurveNode, you must specify a FBX property of the data type you need, for example, the function CreateTypedCurveNode() in LclTranslation creates an animation curve node with the necessary animation channels. In this example, channels X, Y, and Z. However, the animation curve node is not actually connected to the specified FBX property. See the animation sample program in \samples\Animation\. To allow the same FBX property to have different values on each animation layer, an animation curve node must be connected to a single animation layer. If the same animation curve node is connected to multiple animation layers, the same value is used for the FBX property in each of these animation layers.
FbxAnimCurveKey A key or keyframe marks the beginning and the end of an animation curve.
FbxObject A FBX object can contain zero or more FBX properties. Class FbxNode contains the properties that deal with the location of an object as a point in space.
FbxProperty A FBX property is strongly typed data that belongs to a FBX object. To animate a scene, you can set the appropriate FBX properties (LclTranslation is the most common) contained in the FBX objects. For more information, see FBX Properties

下面的UML类图显示了FBX场景中类之间的相互关系。

显示数据结构之间相互关系的场景

父页面:动画数据结构

显示数据结构之间相互关系的场景Scene showing interrelationships among data structures

这个例子有助于理解数据结构在典型场景中是如何相互关联的。

图中的示例场景有三个FBX对象(FBXObject0、FBXObject1和FBXObject2),每个对象都有一个可以动画化的FBX属性(Obj0Property、Obj1Property和Obj2Property)。这三个FBX属性的默认值分别是a、b和c。数据结构通过对象到对象(OO)连接和对象到属性(OP)连接彼此连接,如图所示。

示例场景有一个包含三个动画层的动画堆栈。下表解释了动画层和其他数据结构如何影响FBX属性的值。

Animation Layer Description Value of FBX Property
AnimationLayer0 AnimationLayer0 is the base layer, which is connected to one animation curve node (CurveNode0). This animation curve node is not connected to any animation curves, so it is not used to apply animation to a FBX object. However, this animation curve node is connected to one FBX property (Obj0Property). In AnimationLayer0, the Obj0Property’s value of a is overridden by the animation curve node’s value of aa0. Generally, in the absence of an animation curve or when ignoring the animation curve, the value in the animation curve node overrides the value of the FBX property.
AnimationLayer1 AnimationLayer1 is connected to two animation curve nodes: CurveNode1 and CurveNode2. - CurveNode1 is connected to one FBX property (Obj0Property) and to one animation curve (AnimationCurve0). - CurveNode2 is connected to one FBX property (Obj1Property) and one animation curve (AnimationCurve1). In AnimationLayer1, the value of Obj0Property is neither a nor aa1, but determined by the values over time in AnimationCurve0 In AnimationLayer1, the value of Obj1Property is neither b nor bb1, but determined by the values over time in AnimationCurve1.
AnimationLayer2 AnimationLayer2 is connected to one curve node (CurveNode3), which connects one animation curve (AnimationCurve0) and one FBX property (Obj1Property). In AnimationLayer2, the value of Obj1Property is neither b nor bb2, but determined by the values over time in AnimationCurve0.

注意:

AnimationCurve0连接到CurveNode1和CurveNode3。因此,当场景被求值时,AnimationCurve0会在两个不同的动画层(AnimationLayer1和AnimationLayer2)中影响两个不同的FBX对象(FBXObject0和FBXObject1)的两个不同的FBX属性(Obj0Property和Obj2Property)。此外,如何评估动画数据不仅取决于动画曲线,还取决于混合模式和每个动画层的混合权重。有关混合的更多信息,请参见混合动画。

FBX SDK动画系统提供了很多灵活性,但是您有责任确保数据结构之间的相互关系是有效的。

为动画迁移到新的数据结构Migrating to the new Data structures for animation

目前的FBX SDK动画系统已完全重新设计。FBX SDK 2010中的take node、take node container、take info等数据结构已经被新的数据结构所取代。下表解释了当前数据结构与FBX SDK 2010中的数据结构之间的区别。

Deprecated class and Data Structure Current Data Structure and Class Difference
KFbxTakeclass, Take, Current Take Animation Stack (FbxAnimStack) The animation stack replaces the concept of take as the highest-level container for animation. A scene can contain one or more animation stacks. Instead of multiple takes in a scene, now you can use multiple animation stacks. You do not need to use animation stacks or other animation classes if there is no animation in the scene. The current FbxAnimStack class manages the same data as the deprecated KFbxTake class. An animation stack can contain one or more animation layers.
KFbxTakeNode No equivalent data structure in current SDK
No equivalent class or data structure Animation Layer (FbxAnimLayer) An animation layer contains one or more animation curve nodes that are connected to the animation curves. The animation stack must have at least one animation layer known as the base layer. For blended animation, more than one animation layer is required.
KFCurve Animation Curves (FbxAnimCurve) An animation curve, also known as function curve or FCurve defines how a FBX property (FbxProperty) of a FBX object is animated or different from the default value in the animation layer. The animation curves are connected to the animation curve nodes. The FbxAnimCurve class has similar interface as the KFCurve class.
KFCurveNode Animation Curve Node (FbxAnimCurveNode) An animation curve node is the connection point between the animation curves and the FBX properties. To connect an animation curve to an FBX property, you can connect the animation curve and the FBX property to one animation curve node. An animation curve node can be connected to only one FBX property of one FBX object. The FbxAnimCurveNode class is simpler than KFCurveNode class because hierarchies are not needed.
FbxNode::GetGlobalFromCurrentTakeNode function and similar functions that FbxNode and other classes inherited from KFbxTakeNodeContainer Evaluator (FbxAnimEvaluator) The current evaluation system provides the same behaviour as the previous one, but is better encapsulated. You can write your own evaluator by deriving from the FbxAnimEvaluator class. See Writing and using your own evaluator.

注意:不推荐使用不推荐的类和API元素。当使用不赞成使用的类和API元素时,编译器可能会显示警告。

异常

FbxTakeInfo对象在当前的FBX SDK版本中没有被弃用。

你仍然可以从FBX文件(场景)中获取动画数据到FbxTakeInfo对象中,而不需要加载整个FBX文件。参见从FBX文件中提取动画aata。

•您可以使用存储在FbxTakeInfo对象中的数据来初始化FbxAnimStack对象。看到FbxAnimStack:重置()。

相关主题:

•动画类及其相互关系

•显示数据结构之间相互关系的场景

父页面:动画数据结构

编写和使用自己的评估工具Writing and using your own evaluator

可以使用FbxAnimEvaluator类计算场景中的动画。

//假设已经创建了myScene
FbxAnimEvaluator* mySceneEvaluator = MyScene->GetEvaluator();

“FbxAnimEvaluator”是一个“抽象类”。当您调用“FbxScene::GetEvaluator()”时,该函数将创建一个“FbxAnimEvalClassic”对象的实例。“FbxAnimEvalClassic”对象是FBX SDK当前版本中“FbxAnimEvaluator”类的唯一实现。

但是,您可以实现自己的从’ FbxAnimEvaluator '类派生的求值器类。

执行以下步骤来实现您的评估类:

  1. 调用“FbxScene::SetEvaluator()”将求值程序设置为您自己的求值对象。

  2. Make the following call:

    FbxAnimEvaluator* mySceneEvaluator = MyScene->GetEvaluator();
    

现在,’ mySceneEvaluator '将指向您自己的evaluator类的一个对象。

See Evaluating the animation in a scene for information on evaluating the animation in a scene.

Parent page: Animation data structures

在顶点缓存中存储动画Storing animation in a vertex cache

顶点动画发生在子对象(顶点)级别,例如,在一个球体中移动四个顶点。顶点缓存是一种将顶点动画存储在缓存文件或点缓存文件中的方法,这样顶点动画在从程序导出时就不会丢失。要从像3ds Max这样的程序中导出子对象动画,必须计算每个顶点的位置并将其存储在缓存文件中。

对于使用顶点缓存创建和动画顶点的示例代码,请参见示例程序ExportScene03中的MapVertexCacheOnTriangle()和AnimateVertexCacheOnTriangle()函数(参见无标签)。

对于在顶点缓存中读取数据的样例代码,请参见样例程序ViewScene中的PreparePointCacheData()和ReadVertexCacheData()函数(参见无标签)。

示例程序可以在\samples\中找到。

父页面:动画数据结构

混合的动画Blending animation

混合创建了从一个动画到另一个动画的平滑过渡。你可以混合两个动画,有共同的属性,是同一个字符集的一部分。当混合动画,你会得到最好的结果,如果你混合动画有类似的运动。例如,创建行走动画和运行动画之间的混合。

在一个场景中混合动画需要多个动画层。例如,要将行走动画与运行动画混合,必须使用两个动画层。基础层(Layer0)包含行走动画的动画曲线节点,另一层(Layer1)包含运行动画的动画曲线节点。

动画层按它们被添加到动画堆栈的顺序计算:Layer0、Layer1、Layer2,等等。

你可以设置动画层的权值(混合权值)属性。权重属性决定了混合期间每个动画的影响量。例如,在步行-跑步转换期间,您可以用一秒钟的时间将第0层(步行)的权重从0.99更改为0.01,将第1层(跑步)的权重从0.01更改为0.99。

动画曲线节点通常将一条动画曲线连接到一个FBX属性,但是您也可以使用动画曲线节点来简单地覆盖FBX属性的值。参见显示数据结构之间相互关系的场景。

注意:动画曲线节点也可以用作FBX属性值的容器。参见评估场景中的动画

使用混合模式

绕过特定数据类型的混合模式

父页面:动画

使用混合模式Using the Blend Modes

混合模式指定动画层中的动画如何与动画堆栈的前一动画层中的动画混合。下表解释了混合模式:

Mode Description Example
Additive The animation layer adds the animation to the preceding animation layers in the animation stack. The preceding animation layers must affect the same FBX properties. If AnimLayerA and AnimLayerB both contain animation curve nodes that control the X translation of a FBX object, the resulting X translation is the sum of the X translation values in both the animation layers.
Override The animation layer overrides the animation in the preceding animation layers of the animation stack. The preceding animation layers must control the same FBX properties. If AnimLayerA and AnimLayerB are in override mode, and the X translation of a FBX object on AnimLayerA is 10, and on AnimLayerB the X translation is 15, then the resulting X translation of the FBX object is 15.
Override-Passthrough Unlike, the override mode that completely blocks the animation in the preceding animation layers, in the override-passthrough mode, you can control the opacity of the animation layer. The preceding animation layers must affect the same FBX properties. If AnimLayerC is in the override mode, then it is completely opaque and blocks the animation in the preceding animation layers. However, if AnimLayerC is in the override-passthrough mode, you can control the opacity of AnimLayerC by setting the weight value.

参见:

enum FbxAnimLayer:: EBlendMode

•enum FbxAnimLayer:: ERotationAccumulationMode

•enum FbxAnimLayer:: EScaleAccumulationMode

父页面:混合动画

绕过特定数据类型的混合模式Bypassing the Blend mode for specific data types

每个动画层可以包含动画曲线节点,这些节点指定不同数据类型的许多FBX属性。对于特定数据类型的FBX属性,您可能不希望一个层中的动画与另一个层中的动画混合,例如对于布尔数据类型。

对于头文件fbxtype.h中定义的每个数据类型,每个动画层都有一个混合模式旁路标志。要绕过混合模式,你可以获取并设置混合模式旁通标志的值为以下其中之一:

Value of Blend Mode Bypass Flag Description
true The blend mode in the animation layer is ignored. During evaluation (by using the FbxAnimEvaluator class), the value of the properties in the animation layer override the values in any previous layers.
false The blend mode for the animation layer is used during evaluation.

See also:

  • FbxAnimLayer::GetBlendModeBypass()
  • FbxAnimLayer::SetBlendModeBypass()

Parent page: Blending animation

从FBX文件中提取动画数据Extracting the animation data from a FBX file

您可以从场景(FBX文件)中提取和查询动画数据,而不需要加载整个FBX文件。

一个场景(FbxScene)可以包含一个或多个动画堆栈(FbxAnimStack)。动画堆栈是动画数据的最高级容器,包含一个或多个动画层(FbxAnimLayer)。一个动画层包含一个或多个动画曲线节点(FbxAnimCurveNode)。动画曲线节点是动画曲线(FbxAnimCurve)与FBX对象(FbxObject)的FBX属性(FbxProperty)之间的连接点。

执行以下步骤从场景中提取动画数据:

  1. Extract the animation stacks using a pointer to an instance of the FbxScene (pScene).

    使用指向FbxScene (pScene)实例的指针来提取动画堆栈。

    int numStacks = pScene->GetSrcObjectCount(FBX_TYPE(FbxAnimStack)); i++)
    
  2. Retrieve the nth animation stack.

    获取第n个动画堆栈。

    FbxAnimStack* pAnimStack = 
        FbxCast<FbxAnimStack>(pScene->GetSrcObject(FBX_TYPE(FbxAnimStack), n)
    
  3. Retrieve the number of animation layers in an animation stack.

    检索动画堆栈中的动画层数。

    int numAnimLayers = pAnimStack->GetMemberCount(FBX_TYPE(FbxAnimLayer));
    
  4. Retrieve the nth animation layer from the animation stack.

    从动画堆栈中检索第n个动画层。

    FbxAnimLayer* lAnimLayer = lAnimStack->GetMember(FBX_TYPE(FbxAnimLayer), n);
    

    After retrieving an animation layer, you can access the animation curves for the properties of nodes and node attribute.

    获取一个动画层后,可以访问节点属性和节点属性的动画曲线。

  5. Retrieve the animation curves.

    检索动画曲线。

    • Given a node (lNode) and an animation layer (lAnimLayer), you can retrieve the animation curves for node properties such as the local translation as shown in the following example.

      给定一个节点(lNode)和一个动画层(lAnimLayer),您可以检索节点属性的动画曲线,例如如下例所示的本地转换。

      FbxAnimCurve* lAnimCurve = 
          lNode->LclTranslation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X);
      
    • Given a node (lNode) and a layer (lAnimLayer), you can retrieve the animation curves for node attribute properties such as the red color component as shown in the following example.

      给定一个节点(lNode)和一个层(lAnimLayer),您可以检索节点属性属性的动画曲线,例如如下例所示的红色组件。

      FbxNodeAttribute* lNodeAttribute = lNode->GetNodeAttribute();
      FbxAnimCurve* lAnimCurve = lNodeAttribute->Color.GetCurve<FbxAnimCurve>(pAnimLayer, FBXSDK_CURVENODE_COLOR_RED);
      

You can also use the animation curve nodes to access the animation data for the properties. The following steps show how to access the animation curves for a specific property on the first (0th) channel.

你也可以使用动画曲线节点来访问属性的动画数据。下面的步骤演示了如何访问第一个(第0个)通道上的特定属性的动画曲线。

  1. Check whether the property is valid.

    检查property 是否有效。

    if (lProperty.IsValid) ...
    
  2. Retrieve the curve node using the animation layer.

    使用动画层获取曲线节点。

    FbxAnimCurveNode* lCurveNode = lProperty.GetCurveNode(pAnimLayer);
    

    If the property is not animated the curve node is NULL.

    如果该属性没有被激活,则曲线节点为空

    if (lCurveNode != NULL) ...
    
  3. Step through the curves in the curve node.

    步进通过曲线节点中的曲线。

    for( int c = 0; c < lCurveNode->GetCurveCount(0U); c++ ) {          
      FbxAnimCurve* lAnimCurve = lCurveNode->GetCurve(0U, c);
      if (lAnimCurve != NULL) {
                                                    // ...
                                    }
                    }
    

See the FbxAnimCurveNode class reference for more information on animation curve nodes and channels.

有关动画曲线节点和通道的更多信息,请参阅“FbxAnimCurveNode”类参考。

For a more detailed example of importing animation data using the FBX SDK, see the example ImportScene/DisplayAnimation.cxx.

有关使用FBX SDK导入动画数据的更详细示例,请参见示例“ImportScene/DisplayAnimation.cxx”.

Related topics:

Parent page: Animation

评估一个场景中的动画Evaluating the animation in a scene

To render an animated scene (FbxScene), you must evaluate the animated properties of objects (FbxObject) in a scene at different points in time. For example, to display a moving race car that is represented by a mesh, you must animate the local translation property of the node whose node attribute is the mesh. See FbxNode::LclTranslation.

要渲染一个动画场景(FbxScene),你必须在不同的时间点评估一个场景中物体的动画属性(FbxObject)。例如,要显示一个由网格表示的移动赛车,您必须使其节点属性为网格的节点的本地转换属性具有动画效果。看到FbxNode:: LclTranslation。

执行以下步骤来评估一个场景中的动画:

  1. Get an evaluator object from the scene object that contains the node.

    从包含节点的场景对象中获取一个求值对象。

    // Let us assume that myScene is already created
    //假设已经创建了myScene
    FbxAnimEvaluator* mySceneEvaluator = MyScene->GetAnimationEvaluator();
    
  2. Create a time object.

    创建一个时间对象

    FbxTime myTime;       // The time for each key in the animation curve(s)
    					  //动画曲线中每个键的时间
    myTime.SetSecondDouble(0.0);   // Starting time//开始时间
    
  3. Create a node object.

    创建一个节点对象

    // Create a node object//创建一个节点对象
    FbxNode* myMeshNode = FbxNode::Create (myScene, "");
    // ... Code to connect myMeshNode to a mesh object
    //……连接myMeshNode到一个mesh对象的代码
    
  4. Get the global transformation matrix or local transformation matrix of the node at a specific time.

    得到节点在特定时刻的全局变换矩阵或局部变换矩阵

    • To get the global transformation matrix:

      得到全局变换矩阵:

      // Get a reference to node’s global transform.
      //获取对node的全局转换的引用。
      FbxMatrix& GlobalMatrix = mySceneEvaluator->GetNodeGlobalTransform(myMeshNode, myTime);
      
    • To get the local transformation matrix:

      得到局部变换矩阵:

      // Get a reference to node’s local transform.
      //获取对node的本地转换的引用。
      FbxMatrix& GlobalMatrix = mySceneEvaluator->GetNodeLocalTransform(myMeshNode, myTime);
      
  5. Evaluate the camera’s position expressed as a vector at a specific time. Note A transformation matrix is not needed to evaluate a camera’s position.

    计算相机在特定时间以矢量表示的位置。注意,不需要转换矩阵来计算相机的位置

    // Given a scene, we can create a camera//给定一个场景,我们可以创建一个相机
    FbxCamera* myCamera = FbxCamera::Create (myScene, "");
    
    // Store the value of the property in an animation curve node
    //将属性值存储在动画曲线节点中
    FbxAnimCurveNode&  myCameraPositionVector;
    
    // Get and store the value of the camera's local translation
    //获取并存储摄像机的本地翻译值
    myCameraPositionVector = mySceneEvaluator->GetPropertyValue (myCamera->Position, myTime);
    
  6. Evaluate a material property at a specific time.

    在特定的时间评估材料的性能

    FbxTime time;
    time.SetSecondDouble(3.5);
    FbxColor color = MyMaterial->GetDiffuseColor()->EvaluateValue(time);
    

See Animation/main.cxx for an example that illustrates the use of animation stacks, animation layers, animation curve nodes, and animation curves.

看的动画/主要。举例说明动画堆栈、动画层、动画曲线节点和动画曲线的使用。

使用动画曲线节点来存储FBX属性的值Using the Animation Curve node to store the value of a FBX property

In the code snippet above, the FbxAnimCurveNode object is not used as a connection point between an animation curve and a FBX property. Instead, FbxAnimCurveNode is used as a convenient place to store the vector of the property returned by the evaluator. The animatable properties of FBX objects come in many data types. Some data types are scalars, for example, FbxDouble1, FbxInteger1, and FbxBool1. Other data types have triplet (X, Y, Z) values that are stored as vectors.

在上面的代码片段中,’ FbxAnimCurveNode ‘对象没有用作动画曲线和FBX属性之间的连接点。相反,’ FbxAnimCurveNode ‘被用作存储求值程序返回的属性向量的方便位置。FBX对象的可动画属性有多种数据类型。有些数据类型是标量,例如’ FbxDouble1 ‘、’ FbxInteger1 ‘和’ FbxBool1 '。其他数据类型有三重(X、Y、Z)值,这些值存储为向量。

FbxNode::LclTranslation is of type FbxDouble3, which is a vector with three elements. FbxAnimCurveNode is a container that can store the value of any FBX property irrespective of the data type. The member functions of FbxAnimCurveNode enables you to access the value of each element in a vector, for example, to get the value of channel X, the value of channel Y, and the value of channel Z.

’ FbxNode::LclTranslation ‘的类型是’ FbxDouble3 ‘,这是一个有三个元素的向量。’ FbxAnimCurveNode ‘是一个容器,可以存储任何FBX属性的值,而不管数据类型。’ FbxAnimCurveNode '的成员函数允许您访问向量中的每个元素的值,例如,获得通道X的值、通道Y的值和通道Z.的值

See also:

  • FbxNode::LclTranslation
  • FbxAnimCurveNode::GetChannelCount(),FbxAnimCurveNode::GetChannelValue(), and other related functions
  • fbxdatatypes.h, for a full list of FBX data types

动画一个节点Animating a node

如果不使用混合动画,则设置动画的数据结构非常简单。对于每个FBX属性,至少需要一个动画堆栈、一个动画层和一个动画曲线节点。

这个例子演示了如何动画摄像机的FbxNode对象的本地翻译属性。

    FbxScene* myScene;        // An initialized and valid scene object
							  //一个初始化的有效的场景对象
    FbxNode* myCameraNode;    // An initialized and valid node object
							  //一个初始化且有效的节点对象
    // Set the default local translation values (X, Y, Z) for the camera
	//设置相机的默认本地翻译值(X, Y, Z)
    myCameraNode.LclTranslation.Set(fbxDouble3(1.1, 2.2, 3.3))

尽管本例演示了如何对摄像机的平移属性进行动画处理,但如果FBX属性是可动画的,则可以对任何FBX对象的任何FBX属性进行动画处理。有关更多信息,请参见FbxPropertyFlags::eAnimatable。

按照这些部分中的说明来设置摄像机的本地翻译属性的动画。

首先创建动画堆栈和动画层Start by creating the Animation stack and Animation Layer

执行以下步骤:

1.创建一个动画堆栈。

//创建一个动画堆栈
FbxAnimStack* myAnimStack = FbxAnimStack::Create(pScene,“My stack”);

2.创建一个动画层。

//创建基础层(这是必须的)
FbxAnimLayer* myAnimBaseLayer = FbxAnimLayer::Create(pScene, "Layer0");

3.将基础层添加到动画堆栈中。

myAnimStack - > AddMember (myAnimBaseLayer);

将基础层添加到动画堆栈之后,您可以创建动画曲线节点来将动画数据连接到FBX属性。

创建动画曲线节点Create the Animation Curve Nodes

CubeCreator教程程序为摄像机移动的每个轴创建一个动画曲线节点。

创建一个动画曲线节点,将平移数据连接到相机节点的LclTranslation属性:

// Get the camera’s curve node for local translation.
// The second parameter to GetCurveNode() is "true" to ensure
// that the curve node is automatically created, if it does not exist.
//获取相机的曲线节点进行局部平移。
// GetCurveNode()的第二个参数是“true”以确保
//如果曲线节点不存在,则自动创建它。
FbxAnimCurveNode* myAnimCurveNode = pCamera->LclTranslation.GetCurveNode(pAnimLayer, true);

说明:

  • myAnimCurveNode是通过三个double类型的动画通道自动创建的,因为lclTranslation的类型是fbxDouble3。看到FbxNode:: LclTranslation。
  • myAnimCurveNode自动连接到myCameraNode。LclTranslation是相机的局部翻译属性。
  • myAnimCurveNode被自动添加为myAnimBaseLayer的成员曲线节点。动画层是动画曲线节点的容器。

有关创建动画曲线节点和动画曲线的另一种方法,请参见\samples\ animation \中的动画示例程序。

在将动画曲线节点(myAnimCurveNode)设置为将动画曲线连接到摄像机(myCameraNode)的本地翻译属性之后,您可以创建动画曲线。

创建动画曲线Create the animation curves

执行以下步骤:

1.创建摄像机在x轴上平移的动画曲线。

FbxAnimCurve* myTranXCurve = NULL;//曲线用于x轴上的局部平移// Curve for local translation on X-axis
FbxTime myTime;//用于启动和停止键。// For the start and stop keys.
myKeyIndex = 0;//定义曲线的键的索引 // Index for the keys that define the curve
// Get the animation curve for local translation of the camera.
// true: If the curve does not exist yet, create it.
//获取摄像机本地平移的动画曲线。
// true:如果曲线还不存在,那么创建它。
myTranXCurve = myCameraNode - > LclTranslation。GetCurve (myAnimLayer KFCURVENODE_T_X,true);

2.定义动画曲线的起始和结束关键帧。

// First the start keyframe//首先是开始关键帧
myAnimCurve->KeyModifyBegin();
myTime.SetSecondDouble(0.0);             // Starting time/ /开始时间
myKeyIndex = myAnimCurve->KeyAdd(lTime); // Add the start key; returns 0//添加开始键;返回0

myAnimCurve->KeySet(lKeyIndex,           // Set the zero’th key//设置第0个键
                    lTime,               // Starting time/ /开始时间
                    0.0,                 // Starting X value开始的X值
FbxAnimCurveDef::eInterpolationLinear);// Straight line between 2 points//两点之间的直线

// Then the stop keyframe然后是停止关键帧
lTime.SetSecondDouble(20.0);
lKeyIndex = myAnimCurve->KeyAdd(lTime);
myAnimCurve->KeySet(lKeyIndex, lTime, 500.0, FbxAnimCurveDef::eInterpolationLinear);
myAnimCurve->KeyModifyEnd();

如果你运行这个动画,摄像机需要20秒沿x轴移动500个单位。

你可以参考这些演示动画的示例程序:

•\samples\Animation\中的动画示例程序。

•CubeCreator教程程序在\samples\UI Examples\CubeCreator\。

相关主题:

•动画数据结构

•相机

合并两个场景

本主题介绍如何合并两个导入的场景。它还提供了操作场景和其中节点的基本见解,并将向读者介绍连接和连接管理的概念。

合并两个场景的步骤如下:

1.加载一个场景。

2.修改加载场景中的节点。

3.将加载的场景的内容移动到参考场景中。

4.加载一个不同的场景。

5.用参考场景的内容更新新加载的场景。

6.销毁参考场景。

加载和修改场景

我们创建的第一个对象是FbxManager;创建后续的FBX SDK对象需要这个步骤。然后,将新创建的FbxScene对象(称为“当前场景”)传递给我们的自定义LoadScene()函数,这样它就可以由导入的“file1”内容填充。fbx"(它应该和可执行程序在同一个文件夹中)。一旦场景的内容被填充,我们只需将根节点的第一个子节点的名称(在0处索引)更改为“Test name”。

  //创建一个SDK管理器。
    FbxManager* lSdkManager = FbxManager::Create();

   //创建一个新场景,这样它就可以被导入的文件填充。
    FbxScene* lCurrentScene = FbxScene::Create(lSdkManager,"My Scene");

    //加载场景。
    LoadScene(lSdkManager, lCurrentScene, "file1.fbx");

    //修改场景。在本例中,只更改了一个节点名。
    lCurrentScene->GetRootNode()->GetChild(0)->SetName("Test Name");

将加载的场景的内容移动到参考场景中

创建另一个FbxScene对象,我们将其称为“参考场景”。请注意,在创建FbxScene对象时,它的根节点及其全局设置也会随之创建。因此,这一步的目标是从当前场景的根节点中删除子节点,这样它们就可以成为参考场景的根节点的子节点。

   //创建一个参考场景来存储当前加载的场景的内容。
    FbxScene *lMyRefScene = FbxScene::Create(lSdkManager, "My Reference Scene");

   //将当前加载场景的节点树移动到参考场景中。
    vector<FbxNode*> lChildren;
    int lNumChildren = lCurrentScene->GetRootNode()->GetChildCount();
    for(int i = 0; i < lNumChildren; i++) {

       //从当前加载的场景中获取一个子节点。
        lChildren.push_back(lCurrentScene->GetRootNode()->GetChild(i));

       //将子节点附加到参考场景的根节点。
       for( int c = 0; c < lChildren.size(); c )
           lMyRefScene->GetRootNode()->AddChild(lChildren[c]);
    }

    //从根节点删除子节点。
    lCurrentScene->GetRootNode()->DisconnectAllSrcObject();

当前场景中包含的其他FbxObjects可能需要移动到引用场景中。这些对象可能包括:FbxCharacter、FbxCharacterPose和FbxDocumentInfo等实例。注意,这些对象不是当前场景的根节点的子节点,需要以不同的方式移动。为了实现这一点,我们将需要一些关于FBX SDK上下文中连接的基本知识。

连接是两个FbxObject、一个FbxObject和一个FbxProperty或两个FbxProperty 集之间的定向“源到目标”关系。有关连接的更多信息,请参见连接。在本教程的范围内,我们希望从当前场景中断开所有相关的源对象,并将它们连接到参考场景。我们使用FbxObject::GetSrcObjectCount()、FbxObject::DisconnectAllSrcObject()和FbxObject::ConnectDstObject()来实现这一点。

//将其他对象移动到参考场景。
    int lNumSceneObjects = lCurrentScene->GetSrcObjectCount();
    for(int i = 0; i < lNumSceneObjects; i++) {
        FbxObject* lObj = lCurrentScene->GetSrcObject(i);
        if(lObj == lCurrentScene->GetRootNode() || *lObj == lCurrentScene->GetGlobalSettings()){
            //不要移动根节点或场景的全局设置;这些
            //为每个场景创建对象。
            continue;
        }
       	//将对象附加到参考场景。
        lObj->ConnectDstObject(lMyRefScene);
    }

    //断开所有场景对象。
    lCurrentScene->DisconnectAllSrcObject();

注意:使用连接的概念,您可以在场景中获取某个类型的所有对象。

在下面的代码片段中,我们通过调用lScene->GetSrcObjectCount(FbxMesh::ClassId)来遍历连接到场景的网格。

for(int i = 0; i < lScene->GetSrcObjectCount(FbxMesh::ClassId); i++) {
    FbxMesh* lMesh = (FbxMesh*)lScene->GetSrcObject(FbxMesh::ClassId, i);
    //...
}

加载和更新一个不同的场景

此时,我们已经将当前场景的元素移动到参考场景中,当前场景变为空的。现在,我们获取当前场景,并用“file2.fbx”的内容重新填充它。最后,我们将当前场景中根元素的第一个子元素的名称更改为引用场景中根元素的第一个子元素的名称。最后,当前场景中根的第一个子元素的名字应该是“Test name”。

//将第二个文件导入到lCurrentScene中。
    LoadScene(lSdkManager, lCurrentScene, "file2.fbx");

   //获取加载第二个文件后的名称。
    FbxString lNameBeforeUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();
    FbxString lReferenceName = lMyRefScene->GetRootNode()->GetChild(0)->GetName();

    //更新根节点的子节点0名。
    lCurrentScene->GetRootNode()->GetChild(0)->SetName(lReferenceName);
    FbxString lNameAfterUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();

    //销毁参考场景。
    lMyRefScene->Destroy();
    lMyRefScene = NULL;

    //验证步骤
    printf("Verification (0 for success): %d\n", lNameAfterUpdate.Compare("Test Name"));

场景合并教程

下面是上面讨论的场景合并教程程序。

#include <fbxsdk.h>

/* *
*加载一个场景给定一个FbxManager,一个FbxScene,和一个有效的文件名。
*/
int LoadScene(FbxManager* pSdkManager, FbxScene* pScene, char* filename) {
   //创建io设置对象。
    FbxIOSettings *ios = FbxIOSettings::Create(pSdkManager, IOSROOT);
    pSdkManager->SetIOSettings(ios);

    //使用我们的sdk管理器创建一个导入器。
    FbxImporter* lImporter = FbxImporter::Create(pSdkManager,"");

   //使用第一个参数作为导入程序的文件名。
    if(!lImporter->Initialize(filename, -1, pSdkManager->GetIOSettings())) {
        printf("Call to FbxImporter::Initialize() failed.\n");
        printf("Error returned: %s\n\n", lImporter->GetStatus().GetErrorString());
        lImporter->Destroy();
        return -1;
    }

   //将文件的内容导入到场景中。
    lImporter->Import(pScene);

    //文件已经导入;我们可以销毁importer.。
    lImporter->Destroy();
    return 0;
}

/* *
*合并两个场景示例程序的入口点。
*/
int main(int argc, char** argv) {
    //创建一个SDK管理器。
    FbxManager* lSdkManager = FbxManager::Create();

    //创建一个新场景,这样它就可以被导入的文件填充。
    FbxScene* lCurrentScene = FbxScene::Create(lSdkManager,"My Scene");

    //加载场景。
    LoadScene(lSdkManager, lCurrentScene, "file1.fbx");

    //修改场景。在本例中,只更改了一个节点名。
    lCurrentScene->GetRootNode()->GetChild(0)->SetName("Test Name");

    //创建一个参考场景来存储当前加载的场景的内容。
    FbxScene *lMyRefScene = FbxScene::Create(lSdkManager, "My Reference Scene");

    //将当前加载场景的节点树移动到参考场景中。
    vector<FbxNode*> lChildren;
    int lNumChildren = lCurrentScene->GetRootNode()->GetChildCount();
    for(int i = 0; i < lNumChildren; i++) {

        //从当前加载的场景中获取一个子节点。
        lChildren.push_back(lCurrentScene->GetRootNode()->GetChild(i));

        //将子节点附加到参考场景的根节点。
        for( int c = 0; c < lChildren.size(); c )
           lMyRefScene->GetRootNode()->AddChild(lChildren[c]);
    }

    //从根节点删除子节点。
    lCurrentScene->GetRootNode()->DisconnectAllSrcObject();

    //将其他对象移动到参考场景。
    int lNumSceneObjects = lCurrentScene->GetSrcObjectCount();
    for(int i = 0; i < lNumSceneObjects; i++) {
        FbxObject* lObj = lCurrentScene->GetSrcObject(i);
        if(lObj == lCurrentScene->GetRootNode() || *lObj == lCurrentScene->GetGlobalSettings()){
            //不要移动根节点或场景的全局设置;这些
			//为每个场景创建对象。
            continue;
        }
        // 将对象附加到参考场景。
        lObj->ConnectDstObject(lMyRefScene);
    }

    //断开所有场景对象。
    lCurrentScene->DisconnectAllSrcObject();

    //将第二个文件导入到lCurrentScene中。
    LoadScene(lSdkManager, lCurrentScene, "file1.fbx");

    //获取加载第二个文件后的名称。
    FbxString lNameBeforeUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();
    FbxString lReferenceName = lMyRefScene->GetRootNode()->GetChild(0)->GetName();

    // Update the root's child 0 name.
    lCurrentScene->GetRootNode()->GetChild(0)->SetName(lReferenceName);
    FbxString lNameAfterUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();

    // Destroy the reference scene.
    lMyRefScene->Destroy();
    lMyRefScene = NULL;

    // Verification step验证步骤
    printf("Verification (0 for success): %d\n", lNameAfterUpdate.Compare("Test Name"));

    // Destroy the sdk manager.
    lSdkManager->Destroy();
    exit(0);
}
发布了1 篇原创文章 · 获赞 0 · 访问量 4

猜你喜欢

转载自blog.csdn.net/guizhidaoni/article/details/104940957
今日推荐