#include <fbxsdk.h>
#include "../Common/Common.h"
#include "Thumbnail.h"
#define SAMPLE_FILENAME "ExportScene01.fbx"
bool CreateScene(FbxManager* pSdkManager, FbxScene* pScene);
FbxNode* CreatePatch(FbxScene* pScene, const char* pName);
FbxNode* CreateSkeleton(FbxScene* pScene, const char* pName);
void LinkPatchToSkeleton(FbxScene* pScene, FbxNode* pPatch, FbxNode* pSkeletonRoot);
void StoreBindPose(FbxScene* pScene, FbxNode* pPatch);
void StoreRestPose(FbxScene* pScene, FbxNode* pSkeletonRoot);
void AnimateSkeleton(FbxScene* pScene, FbxNode* pSkeletonRoot);
void AddThumbnailToScene(FbxScene* pScene);
void AddNodeRecursively(FbxArray<FbxNode*>& pNodeArray, FbxNode* pNode);
void SetXMatrix(FbxAMatrix& pXMatrix, const FbxMatrix& pMatrix);
int main(int argc, char** argv)
{
FbxManager* lSdkManager = NULL;
FbxScene* lScene = NULL;
bool lResult;
InitializeSdkObjects(lSdkManager, lScene);
lResult = CreateScene(lSdkManager, lScene);
if(lResult == false)
{
FBXSDK_printf("\n\nAn error occurred while creating the scene...\n");
DestroySdkObjects(lSdkManager, lResult);
return 0;
}
const char* lSampleFileName = NULL;
for( int i = 1; i < argc; ++i )
{
if( FBXSDK_stricmp(argv[i], "-test") == 0 ) continue;
else if( !lSampleFileName ) lSampleFileName = argv[i];
}
if( !lSampleFileName ) lSampleFileName = SAMPLE_FILENAME;
lResult = SaveScene(lSdkManager, lScene, lSampleFileName);
if(lResult == false)
{
FBXSDK_printf("\n\nAn error occurred while saving the scene...\n");
DestroySdkObjects(lSdkManager, lResult);
return 0;
}
DestroySdkObjects(lSdkManager, lResult);
return 0;
}
bool CreateScene(FbxManager *pSdkManager, FbxScene* pScene)
{
FbxDocumentInfo* sceneInfo = FbxDocumentInfo::Create(pSdkManager,"SceneInfo");
sceneInfo->mTitle = "Example scene";
sceneInfo->mSubject = "Illustrates the creation and animation of a deformed cylinder.";
sceneInfo->mAuthor = "ExportScene01.exe sample program.";
sceneInfo->mRevision = "rev. 1.0";
sceneInfo->mKeywords = "deformed cylinder";
sceneInfo->mComment = "no particular comments required.";
pScene->SetSceneInfo(sceneInfo);
AddThumbnailToScene(pScene);
FbxNode* lPatch = CreatePatch(pScene, "Patch");
FbxNode* lSkeletonRoot = CreateSkeleton(pScene, "Skeleton");
FbxNode* lRootNode = pScene->GetRootNode();
lRootNode->AddChild(lPatch);
lRootNode->AddChild(lSkeletonRoot);
LinkPatchToSkeleton(pScene, lPatch, lSkeletonRoot);
StoreBindPose(pScene, lPatch);
StoreRestPose(pScene, lSkeletonRoot);
AnimateSkeleton(pScene, lSkeletonRoot);
return true;
}
FbxNode* CreatePatch(FbxScene* pScene, const char* pName)
{
FbxPatch* lPatch = FbxPatch::Create(pScene,pName);
lPatch->InitControlPoints(4, FbxPatch::eBSpline, 7, FbxPatch::eBSpline);
lPatch->SetStep(4, 4);
lPatch->SetClosed(true, false);
FbxVector4* lVector4 = lPatch->GetControlPoints();
int i;
for (i = 0; i < 7; i++)
{
double lRadius = 15.0;
double lSegmentLength = 20.0;
lVector4[4*i + 0].Set(lRadius, 0.0, (i-3)*lSegmentLength);
lVector4[4*i + 1].Set(0.0, -lRadius, (i-3)*lSegmentLength);
lVector4[4*i + 2].Set(-lRadius, 0.0, (i-3)*lSegmentLength);
lVector4[4*i + 3].Set(0.0, lRadius, (i-3)*lSegmentLength);
}
FbxNode* lNode = FbxNode::Create(pScene,pName);
FbxVector4 lR(-90.0, 0.0, 0.0);
lNode->LclRotation.Set(lR);
lNode->SetNodeAttribute(lPatch);
return lNode;
}
FbxNode* CreateSkeleton(FbxScene* pScene, const char* pName)
{
FbxString lRootName(pName);
lRootName += "Root";
FbxSkeleton* lSkeletonRootAttribute = FbxSkeleton::Create(pScene, pName);
lSkeletonRootAttribute->SetSkeletonType(FbxSkeleton::eRoot);
FbxNode* lSkeletonRoot = FbxNode::Create(pScene,lRootName.Buffer());
lSkeletonRoot->SetNodeAttribute(lSkeletonRootAttribute);
lSkeletonRoot->LclTranslation.Set(FbxVector4(0.0, -40.0, 0.0));
FbxString lLimbNodeName1(pName);
lLimbNodeName1 += "LimbNode1";
FbxSkeleton* lSkeletonLimbNodeAttribute1 = FbxSkeleton::Create(pScene,lLimbNodeName1);
lSkeletonLimbNodeAttribute1->SetSkeletonType(FbxSkeleton::eLimbNode);
lSkeletonLimbNodeAttribute1->Size.Set(1.0);
FbxNode* lSkeletonLimbNode1 = FbxNode::Create(pScene,lLimbNodeName1.Buffer());
lSkeletonLimbNode1->SetNodeAttribute(lSkeletonLimbNodeAttribute1);
lSkeletonLimbNode1->LclTranslation.Set(FbxVector4(0.0, 40.0, 0.0));
FbxString lLimbNodeName2(pName);
lLimbNodeName2 += "LimbNode2";
FbxSkeleton* lSkeletonLimbNodeAttribute2 = FbxSkeleton::Create(pScene,lLimbNodeName2);
lSkeletonLimbNodeAttribute2->SetSkeletonType(FbxSkeleton::eLimbNode);
lSkeletonLimbNodeAttribute2->Size.Set(1.0);
FbxNode* lSkeletonLimbNode2 = FbxNode::Create(pScene,lLimbNodeName2.Buffer());
lSkeletonLimbNode2->SetNodeAttribute(lSkeletonLimbNodeAttribute2);
lSkeletonLimbNode2->LclTranslation.Set(FbxVector4(0.0, 40.0, 0.0));
lSkeletonRoot->AddChild(lSkeletonLimbNode1);
lSkeletonLimbNode1->AddChild(lSkeletonLimbNode2);
return lSkeletonRoot;
}
void LinkPatchToSkeleton(FbxScene* pScene, FbxNode* pPatch, FbxNode* pSkeletonRoot)
{
int i, j;
FbxAMatrix lXMatrix;
FbxNode* lRoot = pSkeletonRoot;
FbxNode* lLimbNode1 = pSkeletonRoot->GetChild(0);
FbxNode* lLimbNode2 = lLimbNode1->GetChild(0);
FbxCluster *lClusterToRoot = FbxCluster::Create(pScene,"");
lClusterToRoot->SetLink(lRoot);
lClusterToRoot->SetLinkMode(FbxCluster::eTotalOne);
for(i=0; i<4; ++i)
for(j=0; j<4; ++j)
lClusterToRoot->AddControlPointIndex(4*i + j, 1.0 - 0.25*i);
FbxCluster* lClusterToLimbNode1 = FbxCluster::Create(pScene, "");
lClusterToLimbNode1->SetLink(lLimbNode1);
lClusterToLimbNode1->SetLinkMode(FbxCluster::eTotalOne);
for (i =1; i<6; ++i)
for (j=0; j<4; ++j)
lClusterToLimbNode1->AddControlPointIndex(4*i + j, (i == 1 || i == 5 ? 0.25 : 0.50));
FbxCluster * lClusterToLimbNode2 = FbxCluster::Create(pScene,"");
lClusterToLimbNode2->SetLink(lLimbNode2);
lClusterToLimbNode2->SetLinkMode(FbxCluster::eTotalOne);
for (i=3; i<7; ++i)
for (j=0; j<4; ++j)
lClusterToLimbNode2->AddControlPointIndex(4*i + j, 0.25*(i - 2));
FbxScene* lScene = pPatch->GetScene();
if( lScene ) lXMatrix = pPatch->EvaluateGlobalTransform();
lClusterToRoot->SetTransformMatrix(lXMatrix);
lClusterToLimbNode1->SetTransformMatrix(lXMatrix);
lClusterToLimbNode2->SetTransformMatrix(lXMatrix);
if( lScene ) lXMatrix = lRoot->EvaluateGlobalTransform();
lClusterToRoot->SetTransformLinkMatrix(lXMatrix);
if( lScene ) lXMatrix = lLimbNode1->EvaluateGlobalTransform();
lClusterToLimbNode1->SetTransformLinkMatrix(lXMatrix);
if( lScene ) lXMatrix = lLimbNode2->EvaluateGlobalTransform();
lClusterToLimbNode2->SetTransformLinkMatrix(lXMatrix);
FbxGeometry* lPatchAttribute = (FbxGeometry*) pPatch->GetNodeAttribute();
FbxSkin* lSkin = FbxSkin::Create(pScene, "");
lSkin->AddCluster(lClusterToRoot);
lSkin->AddCluster(lClusterToLimbNode1);
lSkin->AddCluster(lClusterToLimbNode2);
lPatchAttribute->AddDeformer(lSkin);
}
void AnimateSkeleton(FbxScene* pScene, FbxNode* pSkeletonRoot)
{
FbxString lAnimStackName;
FbxTime lTime;
int lKeyIndex = 0;
FbxNode* lRoot = pSkeletonRoot;
FbxNode* lLimbNode1 = pSkeletonRoot->GetChild(0);
lAnimStackName = "Bend on 2 sides";
FbxAnimStack* lAnimStack = FbxAnimStack::Create(pScene, lAnimStackName);
FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(pScene, "Base Layer");
lAnimStack->AddMember(lAnimLayer);
FbxAnimCurve* lCurve = lRoot->LclRotation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true);
if (lCurve)
{
lCurve->KeyModifyBegin();
lTime.SetSecondDouble(0.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 0.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(1.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 45.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(2.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, -45.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(3.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 0.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lCurve->KeyModifyEnd();
}
lCurve = lLimbNode1->LclRotation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true);
if (lCurve)
{
lCurve->KeyModifyBegin();
lTime.SetSecondDouble(0.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 0.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(1.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, -90.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(2.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 90.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(3.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 0.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lCurve->KeyModifyEnd();
}
lAnimStackName = "Bend and turn around";
lAnimStack = FbxAnimStack::Create(pScene, lAnimStackName);
lAnimLayer = FbxAnimLayer::Create(pScene, "Base Layer");
lAnimStack->AddMember(lAnimLayer);
lCurve = lRoot->LclRotation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true);
if (lCurve)
{
lCurve->KeyModifyBegin();
lTime.SetSecondDouble(0.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 0.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(2.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 720.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lCurve->KeyModifyEnd();
}
lCurve = lLimbNode1->LclRotation.GetCurve(lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true);
if (lCurve)
{
lCurve->KeyModifyBegin();
lTime.SetSecondDouble(0.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 0.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(1.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 90.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lTime.SetSecondDouble(2.0);
lKeyIndex = lCurve->KeyAdd(lTime);
lCurve->KeySetValue(lKeyIndex, 0.0);
lCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic);
lCurve->KeyModifyEnd();
}
}
void AddThumbnailToScene(FbxScene* pScene)
{
FbxThumbnail* lThumbnail = FbxThumbnail::Create(pScene,"");
lThumbnail->SetDataFormat(FbxThumbnail::eRGB_24);
lThumbnail->SetSize(FbxThumbnail::e64x64);
lThumbnail->SetThumbnailImage(cSceneThumbnail);
if (pScene->GetSceneInfo())
{
pScene->GetSceneInfo()->SetSceneThumbnail(lThumbnail);
}
}
void StoreBindPose(FbxScene* pScene, FbxNode* pPatch)
{
FbxArray<FbxNode*> lClusteredFbxNodes;
int i, j;
if (pPatch && pPatch->GetNodeAttribute())
{
int lSkinCount=0;
int lClusterCount=0;
switch (pPatch->GetNodeAttribute()->GetAttributeType())
{
case FbxNodeAttribute::eMesh:
case FbxNodeAttribute::eNurbs:
case FbxNodeAttribute::ePatch:
lSkinCount = ((FbxGeometry*)pPatch->GetNodeAttribute())->GetDeformerCount(FbxDeformer::eSkin);
for(i=0; i<lSkinCount; ++i)
{
FbxSkin *lSkin=(FbxSkin*)((FbxGeometry*)pPatch->GetNodeAttribute())->GetDeformer(i, FbxDeformer::eSkin);
lClusterCount+=lSkin->GetClusterCount();
}
break;
}
if (lClusterCount)
{
for (i=0; i<lSkinCount; ++i)
{
FbxSkin *lSkin=(FbxSkin*)((FbxGeometry*)pPatch->GetNodeAttribute())->GetDeformer(i, FbxDeformer::eSkin);
lClusterCount=lSkin->GetClusterCount();
for (j=0; j<lClusterCount; ++j)
{
FbxNode* lClusterNode = lSkin->GetCluster(j)->GetLink();
AddNodeRecursively(lClusteredFbxNodes, lClusterNode);
}
}
lClusteredFbxNodes.Add(pPatch);
}
}
if (lClusteredFbxNodes.GetCount())
{
FbxPose* lPose = FbxPose::Create(pScene,pPatch->GetName());
lPose->SetIsBindPose(true);
for (i=0; i<lClusteredFbxNodes.GetCount(); i++)
{
FbxNode* lKFbxNode = lClusteredFbxNodes.GetAt(i);
FbxMatrix lBindMatrix = lKFbxNode->EvaluateGlobalTransform();
lPose->Add(lKFbxNode, lBindMatrix);
}
pScene->AddPose(lPose);
}
}
void StoreRestPose(FbxScene* pScene, FbxNode* pSkeletonRoot)
{
FbxString lNodeName;
FbxNode* lKFbxNode;
FbxMatrix lTransformMatrix;
FbxVector4 lT,lR,lS(1.0, 1.0, 1.0);
FbxPose* lPose = FbxPose::Create(pScene,"A Bind Pose");
lT.Set(10.0, 10.0, 10.0);
lR.Set( 0.0, 0.0, 45.0);
lTransformMatrix.SetTRS(lT, lR, lS);
lKFbxNode = pSkeletonRoot;
lPose->Add(lKFbxNode, lTransformMatrix, false );
lT.Set(0.0, 40.0, 0.0);
lR.Set(0.0, 0.0, -90.0);
lTransformMatrix.SetTRS(lT, lR, lS);
lKFbxNode = lKFbxNode->GetChild(0);
lPose->Add(lKFbxNode, lTransformMatrix, true );
lT.Set(0.0, 40.0, 0.0);
lR.Set(0.0, 0.0, 45.0);
lTransformMatrix.SetTRS(lT, lR, lS);
lKFbxNode = lKFbxNode->GetChild(0);
lNodeName = lKFbxNode->GetName();
lPose->Add(lKFbxNode, lTransformMatrix, true );
pScene->AddPose(lPose);
}
void AddNodeRecursively(FbxArray<FbxNode*>& pNodeArray, FbxNode* pNode)
{
if (pNode)
{
AddNodeRecursively(pNodeArray, pNode->GetParent());
if (pNodeArray.Find(pNode) == -1)
{
pNodeArray.Add(pNode);
}
}
}
void SetXMatrix(FbxAMatrix& pXMatrix, const FbxMatrix& pMatrix)
{
memcpy((double*)pXMatrix, &pMatrix.mData[0][0], sizeof(pMatrix.mData));
}