UserProperties/main.cxx

/**************************************************************************************

 Copyright (C) 2001 - 2010 Autodesk, Inc. and/or its licensors.
 All Rights Reserved.

 The coded instructions, statements, computer programs, and/or related material 
 (collectively the "Data") in these files contain unpublished information 
 proprietary to Autodesk, Inc. and/or its licensors, which is protected by 
 Canada and United States of America federal copyright law and by international 
 treaties. 
 
 The Data may not be disclosed or distributed to third parties, in whole or in
 part, without the prior written consent of Autodesk, Inc. ("Autodesk").

 THE DATA IS PROVIDED "AS IS" AND WITHOUT WARRANTY.
 ALL WARRANTIES ARE EXPRESSLY EXCLUDED AND DISCLAIMED. AUTODESK MAKES NO
 WARRANTY OF ANY KIND WITH RESPECT TO THE DATA, EXPRESS, IMPLIED OR ARISING
 BY CUSTOM OR TRADE USAGE, AND DISCLAIMS ANY IMPLIED WARRANTIES OF TITLE, 
 NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE OR USE. 
 WITHOUT LIMITING THE FOREGOING, AUTODESK DOES NOT WARRANT THAT THE OPERATION
 OF THE DATA WILL BE UNINTERRUPTED OR ERROR FREE. 
 
 IN NO EVENT SHALL AUTODESK, ITS AFFILIATES, PARENT COMPANIES, LICENSORS
 OR SUPPLIERS ("AUTODESK GROUP") BE LIABLE FOR ANY LOSSES, DAMAGES OR EXPENSES
 OF ANY KIND (INCLUDING WITHOUT LIMITATION PUNITIVE OR MULTIPLE DAMAGES OR OTHER
 SPECIAL, DIRECT, INDIRECT, EXEMPLARY, INCIDENTAL, LOSS OF PROFITS, REVENUE
 OR DATA, COST OF COVER OR CONSEQUENTIAL LOSSES OR DAMAGES OF ANY KIND),
 HOWEVER CAUSED, AND REGARDLESS OF THE THEORY OF LIABILITY, WHETHER DERIVED
 FROM CONTRACT, TORT (INCLUDING, BUT NOT LIMITED TO, NEGLIGENCE), OR OTHERWISE,
 ARISING OUT OF OR RELATING TO THE DATA OR ITS USE OR ANY OTHER PERFORMANCE,
 WHETHER OR NOT AUTODESK HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS
 OR DAMAGE. 

**************************************************************************************/

//
// In this example a scene is created containing a cube and a pyramid. 
//
// The example illustrates two things:
//  1) How to create user properties, attach them to the cube and 
//     animate them.
//  2) How to create a constraint, constraining the pyramid to the cube.
//
//

#include <fbxsdk.h>

#include "../Common/Common.h"

#define SAMPLE_FILENAME "UserProperties.fbx"
#define ANIM_STACK_ANIMATE_LIST "Animate Cube List"
#define ANIM_STACK_ANIMATE_CUBE "Animate Cube"
#define ANIM_STACK_ANIMATE_PYRAMID "Animate Pyramid"

// Function prototypes.
KFbxNode* CreateCube(KFbxScene* pScene, char* pName);
KFbxNode* CreatePyramid(KFbxScene* pScene, char* pName);
void CreateUserProperties(KFbxNode *pNode);
void AnimateList(KFbxScene* pScene, KFbxNode* pNode, KFbxProperty* pList);
KFbxConstraintPosition* CreatePositionConstraint(KFbxScene* pScene, KFbxNode* pSourceNode, KFbxNode* pConstrainedNode);
void AnimateCube(KFbxScene* pScene, KFbxNode* pNode);
void AnimatePyramid(KFbxScene* pScene, KFbxNode* pNode);
void AnimateNode(KFbxNode* pNode, KFbxAnimLayer* pAnimLayer);


int main(int argc, char** argv)
{
    KFbxSdkManager* lSdkManager = NULL;
    KFbxScene* lScene = NULL;
    bool lResult;

    // Prepare the FBX SDK.
    InitializeSdkObjects(lSdkManager, lScene);

    // Create the scene. 
    KFbxNode* lCube = CreateCube(lScene, "Cube");
    KFbxNode* lPyramid = CreatePyramid(lScene, "Pyramid");

    // Build the node tree.
    KFbxNode* lRootNode = lScene->GetRootNode();
    lRootNode->AddChild(lCube);
    lRootNode->AddChild(lPyramid);

    // Create the user properties on the Cube.
    CreateUserProperties(lCube);

    //

    // animate the list "MyList"
    KFbxProperty p6 = lCube->FindProperty("MyList", false);
    AnimateList(lScene, lCube, &p6);

    // Constraint (position constraint) the pyramid to the cube.
    KFbxConstraintPosition* lPositionConstraint = (KFbxConstraintPosition*)CreatePositionConstraint(lScene, lCube, lPyramid);
    KFbxConnectDst(lPositionConstraint,lScene);

    // Animate the cube: the pyramid will follow, because of the position constraint.
    AnimateCube(lScene, lCube);

    // Animate the pyramid: it doesn't actually move, because it is constrained to the immobile cube.
    AnimatePyramid(lScene, lPyramid);

    // Save the scene.

    // The example can take an output file name as an argument.
    if(argc > 1)
    {
        lResult = SaveScene(lSdkManager, lScene, argv[1], lSdkManager->GetIOPluginRegistry()->FindWriterIDByDescription("FBX ascii (*.fbx)") );
    }
    // A default output file name is given otherwise.
    else
    {
        lResult = SaveScene(lSdkManager, lScene, SAMPLE_FILENAME, lSdkManager->GetIOPluginRegistry()->FindWriterIDByDescription("FBX ascii (*.fbx)") );
    }

    if(lResult == false)
    {
        printf("\n\nAn error occurred while saving the scene...\n");
        DestroySdkObjects(lSdkManager);
        return 0;
    }

    // Destroy all objects created by the FBX SDK.
    DestroySdkObjects(lSdkManager);

    return 0;
}


// Create a cube.
KFbxNode* CreateCube(KFbxScene* pScene, char* pName)
{
    typedef double Vector4[4];
    typedef double Vector2[2];

    // indices of the vertices per each polygon
    static int vtxId[24] = {
        0,1,2,3, // front  face  (Z+)
        1,5,6,2, // right  side  (X+)
        5,4,7,6, // back   face  (Z-)
        4,0,3,7, // left   side  (X-)
        0,4,5,1, // bottom face  (Y-)
        3,2,6,7  // top    face  (Y+)
    };

    // control points
    static Vector4 lControlPoints[8] = {
        { -50.0,  0.0,  50.0, 1.0}, {  50.0,  0.0,  50.0, 1.0}, {  50.0,100.0,  50.0, 1.0}, { -50.0,100.0,  50.0, 1.0}, 
        { -50.0,  0.0, -50.0, 1.0}, {  50.0,  0.0, -50.0, 1.0}, {  50.0,100.0, -50.0, 1.0}, { -50.0,100.0, -50.0, 1.0} 
    };

    // normals
    static Vector4 lNormals[8] = {
        {-0.577350258827209,-0.577350258827209, 0.577350258827209, 1.0}, 
        { 0.577350258827209,-0.577350258827209, 0.577350258827209, 1.0}, 
        { 0.577350258827209, 0.577350258827209, 0.577350258827209, 1.0},
        {-0.577350258827209, 0.577350258827209, 0.577350258827209, 1.0}, 
        {-0.577350258827209,-0.577350258827209,-0.577350258827209, 1.0}, 
        { 0.577350258827209,-0.577350258827209,-0.577350258827209, 1.0},
        { 0.577350258827209, 0.577350258827209,-0.577350258827209, 1.0},
        {-0.577350258827209, 0.577350258827209,-0.577350258827209, 1.0}
    };

    // uvs
    static Vector2 lUVs[14] = {
        { 0.0, 0.0}, 
        { 1.0, 0.0}, 
        { 0.0, 1.0},
        { 1.0, 1.0}, 
        { 0.0, 2.0},
        { 1.0, 2.0},
        { 0.0, 3.0},
        { 1.0, 3.0},
        { 0.0, 4.0},
        { 1.0, 4.0},
        { 2.0, 0.0},
        { 2.0, 1.0},
        {-1.0, 0.0},
        {-1.0, 1.0}
    };

    // create the main structure.
    KFbxMesh* lMesh = KFbxMesh::Create(pScene,"");

    // Create control points.
    lMesh->InitControlPoints(8);
    KFbxVector4* vertex = lMesh->GetControlPoints();
    memcpy((void*)vertex, (void*)lControlPoints, 8*sizeof(KFbxVector4));

    // create the polygons
    int vId = 0;
    for (int f=0; f<6; f++)
    {
        lMesh->BeginPolygon();
        for (int v=0; v<4; v++)
            lMesh->AddPolygon(vtxId[vId++]);
        lMesh->EndPolygon();
    }

    // specify normals per control point.

    KFbxGeometryElementNormal* lNormlElement= lMesh->CreateElementNormal();
    lNormlElement->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);
    lNormlElement->SetReferenceMode(KFbxGeometryElement::eDIRECT);

    for (int n=0; n<8; n++)
        lNormlElement->GetDirectArray().Add(KFbxVector4(lNormals[n][0], lNormals[n][1], lNormals[n][2]));


    // Finally we create the node containing the mesh
    KFbxNode* lNode = KFbxNode::Create(pScene,pName);
    pScene->GetRootNode()->AddChild(lNode);
    lNode->SetNodeAttribute(lMesh);

    return lNode;
}


// Create a pyramid.
KFbxNode* CreatePyramid(KFbxScene* pScene, char* pName)
{
    int i, j;
    KFbxMesh* lMesh = KFbxMesh::Create(pScene,"");

    KFbxVector4 lControlPoint0(-50, 0, 50);
    KFbxVector4 lControlPoint1(50, 0, 50);
    KFbxVector4 lControlPoint2(50, 0, -50);
    KFbxVector4 lControlPoint3(-50, 0, -50);
    KFbxVector4 lControlPoint4(0, 100, 0);

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

    // Create control points.
    lMesh->InitControlPoints(16);
    KFbxVector4* lControlPoints = lMesh->GetControlPoints();

    lControlPoints[0] = lControlPoint0;
    lControlPoints[1] = lControlPoint1;
    lControlPoints[2] = lControlPoint2;
    lControlPoints[3] = lControlPoint3;
    lControlPoints[4] = lControlPoint0;
    lControlPoints[5] = lControlPoint1;
    lControlPoints[6] = lControlPoint4;
    lControlPoints[7] = lControlPoint1;
    lControlPoints[8] = lControlPoint2;
    lControlPoints[9] = lControlPoint4;
    lControlPoints[10] = lControlPoint2;
    lControlPoints[11] = lControlPoint3;
    lControlPoints[12] = lControlPoint4;
    lControlPoints[13] = lControlPoint3;
    lControlPoints[14] = lControlPoint0;
    lControlPoints[15] = lControlPoint4;

    // specify normals per control point.
    KFbxGeometryElementNormal* lNormlElement = lMesh->CreateElementNormal();
    lNormlElement->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);
    lNormlElement->SetReferenceMode(KFbxGeometryElement::eDIRECT);

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

    // Array of polygon vertices.
    int lPolygonVertices[] = { 0, 3, 2, 1,
        4, 5, 6,
        7, 8, 9,
        10, 11, 12,
        13, 14, 15 };

    // Create polygons.

    // Pyramid base.
    lMesh->BeginPolygon();
    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();

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

        lMesh->EndPolygon ();
    }

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

    lNode->SetNodeAttribute(lMesh);
    pScene->GetRootNode()->AddChild(lNode);

    // Translate the pyramid
    KFbxVector4 lTranslation(-150, 0, 0, 0);
    lNode->LclTranslation.Set(lTranslation);

    return lNode;
}


void CreateUserProperties(KFbxNode *pNode) {
    // Now we create the user properties 
    KFbxProperty p1 = KFbxProperty::Create(pNode, DTBool, "MyBooleanProperty", "My Bool");
    KFbxProperty p2 = KFbxProperty::Create(pNode, DTFloat, "MyRealProperty", "My floating point number");
    KFbxProperty p3 = KFbxProperty::Create(pNode, DTColor3, "MyColorProperty", "My Color");
    KFbxProperty p4 = KFbxProperty::Create(pNode, DTInteger, "MyInteger", "");
    KFbxProperty p5 = KFbxProperty::Create(pNode, DTDouble4, "MyVector", "");
    KFbxProperty p6 = KFbxProperty::Create(pNode, DTStringList, "MyList", "");

    /* 
    NOTE: The properties labels exists only while the property object is in memory.
    The label is not saved in the FBX file. When loading properties from the FBX file
    it will take the same value as the property name.
    */

    // we now fill the properties. All the properties are user properties so we set the
    // correct flag
    p1.ModifyFlag(KFbxProperty::eUSER, true);
    p2.ModifyFlag(KFbxProperty::eUSER, true);
    p3.ModifyFlag(KFbxProperty::eUSER, true);
    p4.ModifyFlag(KFbxProperty::eUSER, true);
    p5.ModifyFlag(KFbxProperty::eUSER, true);
    p6.ModifyFlag(KFbxProperty::eUSER, true);

    // let's make MyColorProperty, MyVector and MyList animatables
    p3.ModifyFlag(KFbxProperty::eANIMATABLE, true);
    p5.ModifyFlag(KFbxProperty::eANIMATABLE, true);
    p6.ModifyFlag(KFbxProperty::eANIMATABLE, true);

    // we set the default values
    KFbxColor lRed(1.0, 0.0, 0.0);
    p1.Set(false);
    p2.Set(3.33);
    p3.Set(lRed);
    p4.Set(11);
    p5.Set(fbxDouble3(-1.1, 2.2, -3.3));
    p6.Set(2);

    // and some limits
    p4.SetLimits(-5.0, 9.0);
    p5.SetLimits(0.0, 2.1);

    // add elements to the list
    p6.AddEnumValue("one");
    p6.AddEnumValue("two");
    p6.AddEnumValue("three");
    p6.AddEnumValue("Four");
    p6.InsertEnumValue(0, "zero");
}


// Animate the user property given by pList.
void AnimateList(KFbxScene* pScene, KFbxNode* pNode, KFbxProperty* pList)
{
    // This is represented by the AnimStack object and in order to be able to add
    // animation curves, we need to define at least one AnimLayer.
    KFbxAnimStack* lAnimStack = KFbxAnimStack::Create(pScene, ANIM_STACK_ANIMATE_LIST);
    KFbxAnimLayer* lAnimLayer = KFbxAnimLayer::Create(pScene, "Base Layer");
    lAnimStack->AddMember(lAnimLayer);

    KFbxAnimCurveNode* lFbxFCurveNode = pList->GetCurveNode(lAnimLayer, true);
    if (lFbxFCurveNode)
    {
        KTime      lKeyTime;
        KFbxAnimCurve*  lFCurve = lFbxFCurveNode->GetCurve(0U);
        if (lFCurve == NULL)
            lFCurve = lFbxFCurveNode->CreateCurve(lFbxFCurveNode->GetName());

        lFCurve->KeyModifyBegin();
        lFCurve->ResizeKeyBuffer(5);

        // One way of setting a keyframe value
        lKeyTime.SetSecondDouble(0.0);
        KFbxAnimCurveKey lKey1(lKeyTime, 0.0);
        lKey1.SetInterpolation(KFbxAnimCurveDef::eINTERPOLATION_CONSTANT);
        lKey1.SetConstantMode(KFbxAnimCurveDef::eCONSTANT_STANDARD);
        lFCurve->KeySet(0, lKey1);

        lKeyTime.SetSecondDouble(1.0);
        KFbxAnimCurveKey lKey2(lKeyTime, 1.0);
        lKey2.SetInterpolation(KFbxAnimCurveDef::eINTERPOLATION_CONSTANT);
        lKey2.SetConstantMode(KFbxAnimCurveDef::eCONSTANT_STANDARD);
        lFCurve->KeySet(1, lKey2);

        // an other way of setting a keyframe value
        lKeyTime.SetSecondDouble(2.0);

        // the cast to ETangentMode is intended
        lFCurve->KeySet(2, lKeyTime, 2.0, KFbxAnimCurveDef::eINTERPOLATION_CONSTANT, (KFbxAnimCurveDef::ETangentMode)KFbxAnimCurveDef::eCONSTANT_STANDARD);

        lKeyTime.SetSecondDouble(3.0);
        lFCurve->KeySet(3, lKeyTime, 3.0, KFbxAnimCurveDef::eINTERPOLATION_CONSTANT, (KFbxAnimCurveDef::ETangentMode)KFbxAnimCurveDef::eCONSTANT_STANDARD);

        lKeyTime.SetSecondDouble(4.0);
        lFCurve->KeySet(4, lKeyTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CONSTANT, (KFbxAnimCurveDef::ETangentMode)KFbxAnimCurveDef::eCONSTANT_STANDARD);

        lFCurve->KeyModifyEnd();
    }
}


// Create a position constraint whith pSourceNode as source node and pConstraintedNode as constrained node.
KFbxConstraintPosition* CreatePositionConstraint(KFbxScene* pScene, KFbxNode* pSourceNode, KFbxNode* pConstrainedNode)
{
    KFbxConstraintPosition *lPositionConstraint = KFbxConstraintPosition::Create(pScene,"Position");

    // set constrained object
    lPositionConstraint->SetConstrainedObject(pConstrainedNode);

    // set source
    lPositionConstraint->AddConstraintSource(pSourceNode, 100.0);

    // Constrain the position in X, Y and Z
    lPositionConstraint->SetAffectX(true);
    lPositionConstraint->SetAffectY(true);
    lPositionConstraint->SetAffectZ(true);

    // keep offset between source and constrained object
    KFbxVector4 lPositionSource;
    KFbxVector4 lPositionConstrainedObj;
    lPositionSource = pSourceNode->LclTranslation.Get();
    lPositionConstrainedObj = pConstrainedNode->LclTranslation.Get();
    KFbxVector4 lOffset = lPositionConstrainedObj - lPositionSource;
    lPositionConstraint->SetOffset(lOffset);

    // activate property
    KFbxProperty lActive = lPositionConstraint->FindProperty("Active", false);
    lActive.Set(true);


    return lPositionConstraint;
}


// Animate the cube by translating it in X, Y and Z.
void AnimateCube(KFbxScene* pScene, KFbxNode* pNode)
{
    // This is represented by the AnimStack object and in order to be able to add
    // animation curves, we need to define at least one AnimLayer.
    KFbxAnimStack* lAnimStack = KFbxAnimStack::Create(pScene, ANIM_STACK_ANIMATE_CUBE);
    KFbxAnimLayer* lAnimLayer = KFbxAnimLayer::Create(pScene, "Base Layer");
    lAnimStack->AddMember(lAnimLayer);

    AnimateNode(pNode, lAnimLayer);

}


void AnimatePyramid(KFbxScene* pScene, KFbxNode* pNode)
{
    // This is represented by the AnimStack object and in order to be able to add
    // animation curves, we need to define at least one AnimLayer.
    KFbxAnimStack* lAnimStack = KFbxAnimStack::Create(pScene, ANIM_STACK_ANIMATE_PYRAMID);
    KFbxAnimLayer* lAnimLayer = KFbxAnimLayer::Create(pScene, "Base Layer");
    lAnimStack->AddMember(lAnimLayer);

    AnimateNode(pNode, lAnimLayer);
}


// Animate a given node.
void AnimateNode(KFbxNode* pNode, KFbxAnimLayer* pAnimLayer)
{
    KFbxAnimCurve* lCurveX = NULL;
    KFbxAnimCurve* lCurveY = NULL;
    KFbxAnimCurve* lCurveZ = NULL;
    KTime lTime;
    int lKeyIndex = 0;

    // The CurveNode must be created in order to access the AnimCurves
    pNode->LclTranslation.GetCurveNode(pAnimLayer, true);
    lCurveX = pNode->LclTranslation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_T_X, true);
    lCurveY = pNode->LclTranslation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_T_Y, true);
    lCurveZ = pNode->LclTranslation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_T_Z, true);

    if (lCurveX)
    {
        lCurveX->KeyModifyBegin();

        lTime.SetSecondDouble(0.0);
        lKeyIndex = lCurveX->KeyAdd(lTime);
        lCurveX->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(1.0);
        lKeyIndex = lCurveX->KeyAdd(lTime);
        lCurveX->KeySet(lKeyIndex, lTime, 100.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(2.0);
        lKeyIndex = lCurveX->KeyAdd(lTime);
        lCurveX->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(3.0);
        lKeyIndex = lCurveX->KeyAdd(lTime);
        lCurveX->KeySet(lKeyIndex, lTime, -100.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(4.0);
        lKeyIndex = lCurveX->KeyAdd(lTime);
        lCurveX->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lCurveX->KeyModifyEnd();
    }

    if (lCurveY)
    {
        lCurveY->KeyModifyBegin();

        lTime.SetSecondDouble(2.0);
        lKeyIndex = lCurveY->KeyAdd(lTime);
        lCurveY->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(3.0);
        lKeyIndex = lCurveY->KeyAdd(lTime);
        lCurveY->KeySet(lKeyIndex, lTime, 100.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(4.0);
        lKeyIndex = lCurveY->KeyAdd(lTime);
        lCurveY->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(5.0);
        lKeyIndex = lCurveY->KeyAdd(lTime);
        lCurveY->KeySet(lKeyIndex, lTime, -100.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(6.0);
        lKeyIndex = lCurveY->KeyAdd(lTime);
        lCurveY->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lCurveY->KeyModifyEnd();
    }

    if (lCurveZ)
    {
        lCurveZ->KeyModifyBegin();

        lTime.SetSecondDouble(5.0);
        lKeyIndex = lCurveZ->KeyAdd(lTime);
        lCurveZ->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(6.0);
        lKeyIndex = lCurveZ->KeyAdd(lTime);
        lCurveZ->KeySet(lKeyIndex, lTime, 100.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(7.0);
        lKeyIndex = lCurveZ->KeyAdd(lTime);
        lCurveZ->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(8.0);
        lKeyIndex = lCurveZ->KeyAdd(lTime);
        lCurveZ->KeySet(lKeyIndex, lTime, -100.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(9.0);
        lKeyIndex = lCurveZ->KeyAdd(lTime);
        lCurveZ->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lCurveZ->KeyModifyEnd();
    }

}