ExportScene03/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. 

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

//
// The scene created in this example is a textured cube, a pyramid 
// with materials mapped on it's faces and triangle deformed by vertex cache. 
// An animation stack displays 6 different angles of all models.
//
// The example illustrates how to:
//        1) create a cube in mesh
//        2) map textures on diffuse, ambient and emissive channel of the cube
//        3) create an UV set for each channel with a texture for the cube
//        4) create a pyramid in mesh
//        5) create and map materials on the diffuse channel for each faces of the pyramid
//        6) create a cube with our custom mesh type
//        7) create and map materials on the diffuse channel for our custom mesh cube
//        8) create and add to our custom meshed cube a User Data Layer
//        9) create vertex cache deformer
//       10) create an animation stack
//       11) animate vertex
//       12) export a scene in a .FBX file
//
//
// 1. To test vertex (3 doubles) cache (default) :
//
// ./ExportScene03 cacheFileName
//
// 2. To test int32 cache :
//
// ./ExportScene03 cacheFileName 1
//

#include <fbxsdk.h>

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

#define SAMPLE_FILENAME_MC "ExportScene03_MC.fbx"
#define SAMPLE_FILENAME_PC2 "ExportScene03_PC2.fbx"
#define PID_MY_GEOMETRY_LEMENT 0

// Function prototypes.
bool CreateScene(KFbxScene* pScene, char* pSampleFileName);

KFbxNode* CreateCubeWithTexture(KFbxScene* pScene, char* pName);
KFbxNode* CreatePyramidWithMaterials(KFbxScene* pScene, char* pName);
KFbxNode* CreateTriangle(KFbxScene* pScene, char* pName);
KFbxNode* CreateCubeWithMaterialAndMyKFbxMesh(KFbxScene* pScene, char* pName);

void CreateTexture(KFbxScene* pScene, KFbxMesh* pMesh);
void CreateMaterials(KFbxScene* pScene, KFbxMesh* pMesh);
void CreateMaterialsWithMyKFbxMesh(KFbxScene* pScene, MyKFbxMesh* pMyKFbxMesh);

void MapShapeOnPyramid(KFbxScene* pScene, KFbxNode* pPyramid);
void MapVertexCacheOnTriangle(KFbxScene* pScene, KFbxNode* pTriangle, char* pSampleFileName);

void SetCubeDefaultPosition(KFbxNode* pCube);
void SetPyramidDefaultPosition(KFbxNode* pPyramid);
void SetTriangleDefaultPosition(KFbxNode* pTriangle);
void SetMyKFbxMeshCubeDefaultPosition(KFbxNode* pMyKFbxCube);

void Animate(KFbxNode* pNode, KFbxAnimLayer* pAnimLayer);
void AnimateVertexCacheOnTriangleDoubleVertex(KFbxNode* pNode, double pFrameRate);
void AnimateVertexCacheOnTriangleInt32(KFbxNode* pNode, double pFrameRate);
void AnimateVertexCacheOnTriangleFloat(KFbxNode* pNode, double pFrameRate);

bool gExportVertexCacheMCFormat = true;

// gCacheType == 0 (default)  - double vertex array
//            == 1            - int32 array
int  gCacheType = 2;

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

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

    //Add the new class we have created to the Sdk Manager
    //Our class MyKFbxMesh is derived from KFbxMesh
    lSdkManager->RegisterFbxClass("MyKFbxMesh", FBX_TYPE(MyKFbxMesh), FBX_TYPE(KFbxMesh));
    //Now, our class MyKFbxMesh is ready to be used

    lSdkManager->RegisterFbxClass("MyFbxObject", FBX_TYPE(MyFbxObject), FBX_TYPE(KFbxObject), "MyFbxObjectType", "MyFbxObjectSubType");

    // The example can take an output file name as an argument.
    if(argc > 1)
    {
        lSampleFileName = argv[1];
    }
    // A default output file name is given otherwise.
    else
    {
        lSampleFileName = gExportVertexCacheMCFormat ? (char *)SAMPLE_FILENAME_MC : (char *)SAMPLE_FILENAME_PC2;
    }

    if(argc > 2)
    {
        gCacheType = atoi(argv[2]);
    }

    // Create the scene.
    lResult = CreateScene(lScene, lSampleFileName);

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

    // Save the scene.
    lResult = SaveScene(lSdkManager, lScene, lSampleFileName);

    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;
}

bool CreateScene(KFbxScene* pScene, char* pSampleFileName)
{
    KFbxNode* lCube = CreateCubeWithTexture(pScene, "Cube");
    KFbxNode* lPyramid = CreatePyramidWithMaterials(pScene, "Pyramid");
    KFbxNode* lTriangle = CreateTriangle(pScene, "Triangle");
    KFbxNode* lMyKFbxMeshCube = CreateCubeWithMaterialAndMyKFbxMesh(pScene, "CubeMyKFbxMesh");
    MyFbxObject* lMyFbxObject = MyFbxObject::Create(pScene, "MyFbxObject 1");

    MapShapeOnPyramid(pScene, lPyramid);
    MapVertexCacheOnTriangle(pScene, lTriangle, pSampleFileName);

    SetCubeDefaultPosition(lCube);
    SetPyramidDefaultPosition(lPyramid);
    SetTriangleDefaultPosition(lTriangle);
    SetMyKFbxMeshCubeDefaultPosition(lMyKFbxMeshCube);

    // Build the node tree.
    KFbxNode* lRootNode = pScene->GetRootNode();
    lRootNode->AddChild(lCube);
    lRootNode->AddChild(lPyramid);
    lRootNode->AddChild(lMyKFbxMeshCube);
    lRootNode->AddChild(lTriangle);
    lRootNode->ConnectSrcObject(lMyFbxObject);

    // Create the Animation Stack
    KFbxAnimStack* lAnimStack = KFbxAnimStack::Create(pScene, "Show all faces");

    // The animation nodes can only exist on AnimLayers therefore it is mandatory to
    // add at least one AnimLayer to the AnimStack. And for the purpose of this example,
    // one layer is all we need.
    KFbxAnimLayer* lAnimLayer = KFbxAnimLayer::Create(pScene, "Base Layer");
    lAnimStack->AddMember(lAnimLayer);

    //Create a simple animated fcurve
    KFbxProperty lMyProperty = lMyFbxObject->FindProperty("MyAnimatedPropertyName");
    if( lMyProperty.IsValid() )
    {
        lMyProperty.Set(0.0);   //Default value
        lMyProperty.ModifyFlag(KFbxProperty::eANIMATABLE, true);
        lMyProperty.CreateCurveNode(lAnimLayer);
        KFbxAnimCurve* lMyFCurve = lMyProperty.GetCurve<KFbxAnimCurve>(lAnimLayer, true);
        if( lMyFCurve )
        {
            KFbxAnimCurveKey key;

            key.Set(KTIME_ZERO, -100); lMyFCurve->KeyAdd(key.GetTime(), key);
            key.Set(KTime(100), 0)   ; lMyFCurve->KeyAdd(key.GetTime(), key);
            key.Set(KTime(200), 100) ; lMyFCurve->KeyAdd(key.GetTime(), key);
        }
    }

    Animate(lCube, lAnimLayer);
    Animate(lPyramid, lAnimLayer);
    Animate(lMyKFbxMeshCube, lAnimLayer);
    KFbxGlobalSettings& lGlobalSettings = pScene->GetGlobalSettings();

    switch(gCacheType) 
    {
    case 0:
    default:
        AnimateVertexCacheOnTriangleDoubleVertex(lTriangle, KTime::GetFrameRate(lGlobalSettings.GetTimeMode()));
        break;
    case 1:
        AnimateVertexCacheOnTriangleInt32(lTriangle, KTime::GetFrameRate(lGlobalSettings.GetTimeMode()));
        break;
    case 2:
        AnimateVertexCacheOnTriangleFloat(lTriangle, KTime::GetFrameRate(lGlobalSettings.GetTimeMode()));
        break;
    }

    return true;
}

// Create a cube with a texture. 
KFbxNode* CreateCubeWithTexture(KFbxScene* pScene, char* pName)
{
    int i, j;
    KFbxMesh* lMesh = KFbxMesh::Create(pScene,pName);

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

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

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

    lControlPoints[0] = lControlPoint0;
    lControlPoints[1] = lControlPoint1;
    lControlPoints[2] = lControlPoint2;
    lControlPoints[3] = lControlPoint3;
    lControlPoints[4] = lControlPoint1;
    lControlPoints[5] = lControlPoint5;
    lControlPoints[6] = lControlPoint6;
    lControlPoints[7] = lControlPoint2;
    lControlPoints[8] = lControlPoint5;
    lControlPoints[9] = lControlPoint4;
    lControlPoints[10] = lControlPoint7;
    lControlPoints[11] = lControlPoint6;
    lControlPoints[12] = lControlPoint4;
    lControlPoints[13] = lControlPoint0;
    lControlPoints[14] = lControlPoint3;
    lControlPoints[15] = lControlPoint7;
    lControlPoints[16] = lControlPoint3;
    lControlPoints[17] = lControlPoint2;
    lControlPoints[18] = lControlPoint6;
    lControlPoints[19] = lControlPoint7;
    lControlPoints[20] = lControlPoint1;
    lControlPoints[21] = lControlPoint0;
    lControlPoints[22] = lControlPoint4;
    lControlPoints[23] = lControlPoint5;


    // We want to have one normal for each vertex (or control point),
    // so we set the mapping mode to eBY_CONTROL_POINT.
    KFbxGeometryElementNormal* lGeometryElementNormal= lMesh->CreateElementNormal();

    lGeometryElementNormal->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);

    // Here are two different ways to set the normal values.
    bool firstWayNormalCalculations=true;
    if (firstWayNormalCalculations)
    {    
        // The first method is to set the actual normal value
        // for every control point.
        lGeometryElementNormal->SetReferenceMode(KFbxGeometryElement::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
    {
        // The second method is to the possible values of the normals
        // in the direct array, and set the index of that value
        // in the index array for every control point.
        lGeometryElementNormal->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

        // Add the 6 different normals to the direct array
        lGeometryElementNormal->GetDirectArray().Add(lNormalZPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalZNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalXNeg);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYPos);
        lGeometryElementNormal->GetDirectArray().Add(lNormalYNeg);

        // Now for each control point, we need to specify which normal to use
        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(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.
    }

    // 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
    KFbxGeometryElementUV* lUVDiffuseElement = lMesh->CreateElementUV( "DiffuseUV");
    K_ASSERT( lUVDiffuseElement != NULL);
    lUVDiffuseElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON_VERTEX);
    lUVDiffuseElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

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

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


    // Create UV for Ambient channel
    KFbxGeometryElementUV* lUVAmbientElement = lMesh->CreateElementUV("AmbientUV");

    lUVAmbientElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON_VERTEX);
    lUVAmbientElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

    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
    KFbxGeometryElementUV* lUVEmissiveElement = lMesh->CreateElementUV("EmissiveUV");

    lUVEmissiveElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON_VERTEX);
    lUVEmissiveElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

    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 eINDEX_TO_DIRECT reference and in eBY_POLYGON_VERTEX  mapping mode
    //we must update the size of the index array.
    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
            lUVDiffuseElement->GetIndexArray().SetAt(i*4+j, j);
            lUVAmbientElement->GetIndexArray().SetAt(i*4+j, j);
            lUVEmissiveElement->GetIndexArray().SetAt(i*4+j, j);

        }

        lMesh->EndPolygon ();
    }

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

    lNode->SetNodeAttribute(lMesh);
    lNode->SetShadingMode(KFbxNode::eTEXTURE_SHADING);

    CreateTexture(pScene, lMesh);

    return lNode;
}

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

    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* lNormalElement= lMesh->CreateElementNormal();
    lNormalElement->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);
    lNormalElement->SetReferenceMode(KFbxGeometryElement::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);


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

    // Set material mapping.
    KFbxGeometryElementMaterial* lMaterialElement = lMesh->CreateElementMaterial();
    lMaterialElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON);
    lMaterialElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

    // Create polygons. Assign material indices.

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


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

    lNode->SetNodeAttribute(lMesh);

    CreateMaterials(pScene, lMesh);

    return lNode;
}

KFbxNode* CreateTriangle(KFbxScene* pScene, char* pName)
{
    KFbxMesh* lMesh = KFbxMesh::Create(pScene, pName);

    // The three vertices
    KFbxVector4 lControlPoint0(-50, 0, 50);
    KFbxVector4 lControlPoint1(50, 0, 50);
    KFbxVector4 lControlPoint2(0, 50, -50);

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

    lControlPoints[0] = lControlPoint0;
    lControlPoints[1] = lControlPoint1;
    lControlPoints[2] = lControlPoint2;

    // Create the triangle's polygon
    lMesh->BeginPolygon();
    lMesh->AddPolygon(0); // Control point 0
    lMesh->AddPolygon(1); // Control point 1
    lMesh->AddPolygon(2); // Control point 2
    lMesh->EndPolygon();

    KFbxNode* lNode = KFbxNode::Create(pScene,pName);
    lNode->SetNodeAttribute(lMesh);

    return lNode;
}

KFbxNode* CreateCubeWithMaterialAndMyKFbxMesh(KFbxScene* pScene, char* pName)
{
    int i, j;

    //create a cube with our newly created class
    MyKFbxMesh* lMyKFbxMesh = MyKFbxMesh::Create(pScene,pName);
    fbxDouble3 lVector3(0.1, 0.2, 0.3);
    fbxDouble4 lVector4(0.1, 0.2, 0.3, 0.4);
    fbxDouble4 lVector41(1.1, 1.2, 1.3, 1.4);
    fbxDouble4 lVector42(2.1, 2.2, 2.3, 2.4);
    fbxDouble4 lVector43(3.1, 3.2, 3.3, 3.4);
    fbxDouble44 lMatrix(lVector4,lVector41,lVector42,lVector43);

    KFbxColor lGreen(0.0, 0.0, 1.0);

    KTime lTime(333);
    //Set user-specific properties of our classes
    KString lString = "My Property 5 Value";
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY1).Set(true);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY2).Set((int) 1);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY3).Set((float)2.2);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY4).Set((double)3.3);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY5).Set(lString);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY6).Set(lVector3);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY6).Set(lGreen);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY8).Set(lVector4);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY9).Set(lMatrix);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY10).Set(3);
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY10).AddEnumValue("AAA");
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY10).AddEnumValue("BBB");
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY10).AddEnumValue("CCC");
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY10).AddEnumValue("DDD");
    lMyKFbxMesh->GetProperty((int) MyKFbxMesh::eMY_PROPERTY11).Set(lTime);

    KFbxVector4 lControlPoint0(-25, 0, 25);
    KFbxVector4 lControlPoint1(25, 0, 25);
    KFbxVector4 lControlPoint2(25, 50, 25);
    KFbxVector4 lControlPoint3(-25, 50, 25);
    KFbxVector4 lControlPoint4(-25, 0, -25);
    KFbxVector4 lControlPoint5(25, 0, -25);
    KFbxVector4 lControlPoint6(25, 50, -25);
    KFbxVector4 lControlPoint7(-25, 50, -25);

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

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

    lControlPoints[0] = lControlPoint0;
    lControlPoints[1] = lControlPoint1;
    lControlPoints[2] = lControlPoint2;
    lControlPoints[3] = lControlPoint3;
    lControlPoints[4] = lControlPoint1;
    lControlPoints[5] = lControlPoint5;
    lControlPoints[6] = lControlPoint6;
    lControlPoints[7] = lControlPoint2;
    lControlPoints[8] = lControlPoint5;
    lControlPoints[9] = lControlPoint4;
    lControlPoints[10] = lControlPoint7;
    lControlPoints[11] = lControlPoint6;
    lControlPoints[12] = lControlPoint4;
    lControlPoints[13] = lControlPoint0;
    lControlPoints[14] = lControlPoint3;
    lControlPoints[15] = lControlPoint7;
    lControlPoints[16] = lControlPoint3;
    lControlPoints[17] = lControlPoint2;
    lControlPoints[18] = lControlPoint6;
    lControlPoints[19] = lControlPoint7;
    lControlPoints[20] = lControlPoint1;
    lControlPoints[21] = lControlPoint0;
    lControlPoints[22] = lControlPoint4;
    lControlPoints[23] = lControlPoint5;


    // We want to have one normal for each vertex (or control point),
    // so we set the mapping mode to eBY_CONTROL_POINT.
    KFbxGeometryElementNormal* lGeometryElementNormal = lMyKFbxMesh->CreateElementNormal();
    lGeometryElementNormal->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);


    // The second method is to the possible values of the normals
    // in the direct array, and set the index of that value
    // in the index array for every control point.
    lGeometryElementNormal->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

    // Add the 6 different normals to the direct array
    lGeometryElementNormal->GetDirectArray().Add(lNormalZPos);
    lGeometryElementNormal->GetDirectArray().Add(lNormalXPos);
    lGeometryElementNormal->GetDirectArray().Add(lNormalZNeg);
    lGeometryElementNormal->GetDirectArray().Add(lNormalXNeg);
    lGeometryElementNormal->GetDirectArray().Add(lNormalYPos);
    lGeometryElementNormal->GetDirectArray().Add(lNormalYNeg);

    // Now for each control point, we need to specify which normal to use
    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(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.


    // 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 };

    // Set material mapping.
    KFbxGeometryElementMaterial* lMaterialElement = lMyKFbxMesh->CreateElementMaterial();
    lMaterialElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON);
    lMaterialElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

    // Create UV coordinates.
    KFbxGeometryElementUV* lUVElement = lMyKFbxMesh->CreateElementUV( "");
    K_ASSERT( lUVElement != NULL);
    lUVElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON_VERTEX);
    lUVElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

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

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

    for(i = 0; i < 6; i++)
    {
        //we created 6 lambert materials in the MyKFbxMesh
        //make each face use a different one
        lMyKFbxMesh->BeginPolygon(i);

        for(j = 0; j < 4; j++)
        {
            lMyKFbxMesh->AddPolygon(lPolygonVertices[i*4 + j], // Control point index. 
                j); // Valid texture UV index since texture UV mapping is by polygon vertex.
        }

        lMyKFbxMesh->EndPolygon ();
    }


    //Add a User Data Element
    //As of now, the types supported by a User Data Element are: DTBool, DTInteger, DTFloat and DTDouble

    //For this example, we will create a element which possess 1 float and 1 bool

    //create a template array of KFbxDataTypes
    KArrayTemplate<KFbxDataType> lArrayType;

    //Create a template array of const char*
    KArrayTemplate<const char*> lArrayNames;

    //let's add our types and the names of each of the added types
    lArrayType.Add(DTFloat);
    lArrayNames.Add("My Float");

    lArrayType.Add(DTBool);
    lArrayNames.Add("My Bool");


    //Now we are ready to create the User Data Element
    KFbxGeometryElementUserData* lKFbxGeometryElementUserData = KFbxGeometryElementUserData::Create(lMyKFbxMesh, "My Geometry Element",PID_MY_GEOMETRY_LEMENT,lArrayType, lArrayNames);
    //And UserData create function is still in implementing


    //For this example we will set the mapping mode to POLYGON_VERTEX
    lKFbxGeometryElementUserData->SetMappingMode(KFbxGeometryElement::eBY_POLYGON_VERTEX);

    //As we are using the eDirect Reference mode, and we are using polygon vertex Mapping mode
    //we have to resize the direct array to the number of polygon vertex we have in this mesh
    lKFbxGeometryElementUserData->ResizeAllDirectArrays(lMyKFbxMesh->GetPolygonVertexCount());


    //To change the values in the direct array, we simply get the array and modify what we need to
    KFbxLayerElementArrayTemplate<void*>* directArrayF = lKFbxGeometryElementUserData->GetDirectArrayVoid("My Float");
    float *lDirectArrayFloat = NULL;
    lDirectArrayFloat = directArrayF->GetLocked(lDirectArrayFloat);

    KFbxLayerElementArrayTemplate<void*>* directArrayB = lKFbxGeometryElementUserData->GetDirectArrayVoid("My Bool");
    bool *lDirectArrayBool = NULL;
    directArrayB->GetLocked(lDirectArrayBool);

    //Modify every data for each polygon vertex on our mesh with some value
    for(i=0; i<lMyKFbxMesh->GetPolygonVertexCount(); ++i)
    {
        if(lDirectArrayFloat)
            lDirectArrayFloat[i]=(float)(i+0.5);
        if(lDirectArrayBool)
            lDirectArrayBool[i]= (i%2==0);
    }

    directArrayF->Release((void**)&lDirectArrayFloat);
    directArrayB->Release((void**)&lDirectArrayBool);

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

    lNode->SetNodeAttribute(lMyKFbxMesh);
    lNode->SetShadingMode(KFbxNode::eTEXTURE_SHADING);

    //let's create the materials
    //6 materials, 1 for each face of the cube
    CreateMaterialsWithMyKFbxMesh(pScene, lMyKFbxMesh);

    return lNode;
}


// Create texture for cube.
void CreateTexture(KFbxScene* pScene, KFbxMesh* 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
    KFbxSurfacePhong* lMaterial = NULL;

    //get the node of mesh, add material for it.
    KFbxNode* lNode = pMesh->GetNode();
    if(lNode)
    {
        lMaterial = lNode->GetSrcObject<KFbxSurfacePhong>(0);
        if (lMaterial == NULL)
        {
            KString lMaterialName = "toto";
            KString 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 = KFbxSurfacePhong::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);
        }
    }

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

    // Set texture properties.
    lTexture->SetFileName("scene03.jpg"); // Resource file is in current directory.
    lTexture->SetTextureUse(KFbxTexture::eSTANDARD);
    lTexture->SetMappingType(KFbxTexture::eUV);
    lTexture->SetMaterialUse(KFbxFileTexture::eMODEL_MATERIAL);
    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 = KFbxFileTexture::Create(pScene,"Ambient Texture");

    // Set texture properties.
    lTexture->SetFileName("gradient.jpg"); // Resource file is in current directory.
    lTexture->SetTextureUse(KFbxTexture::eSTANDARD);
    lTexture->SetMappingType(KFbxTexture::eUV);
    lTexture->SetMaterialUse(KFbxFileTexture::eMODEL_MATERIAL);
    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 = KFbxFileTexture::Create(pScene,"Emissive Texture");

    // Set texture properties.
    lTexture->SetFileName("spotty.jpg"); // Resource file is in current directory.
    lTexture->SetTextureUse(KFbxTexture::eSTANDARD);
    lTexture->SetMappingType(KFbxTexture::eUV);
    lTexture->SetMaterialUse(KFbxFileTexture::eMODEL_MATERIAL);
    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);
}

// Create materials for pyramid.
void CreateMaterials(KFbxScene* pScene, KFbxMesh* pMesh)
{
    int i;

    for (i = 0; i < 5; i++ )
    {
        KString lMaterialName = "material";
        KString lShadingName = "Phong";
        lMaterialName += i;
        fbxDouble3 lBlack(0.0, 0.0, 0.0);
        fbxDouble3 lRed(1.0, 0.0, 0.0);
        fbxDouble3 lColor;
        KFbxSurfacePhong *lMaterial = KFbxSurfacePhong::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.
        KFbxNode* lNode = pMesh->GetNode();
        if(lNode)             
            lNode->AddMaterial(lMaterial);
    }  
}

void CreateMaterialsWithMyKFbxMesh(KFbxScene* pScene, MyKFbxMesh* pMyKFbxMesh)
{
    int i;
    for (i = 0; i != 6; ++i )
    {
        KString lMaterialName = "material";
        KString lShadingModelName = i%2==0 ? "Lambert" : "Phong";
        lMaterialName += i;
        fbxDouble3 lBlack(0.0, 0.0, 0.0);
        fbxDouble3 lRed(1.0, 0.0, 0.0);
        fbxDouble3 lColor;
        KFbxSurfaceLambert *lMaterial = KFbxSurfaceLambert::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(lShadingModelName);

        //get the node of mesh, add material for it. 
        KFbxNode* lNode = pMyKFbxMesh->GetNode();
        if(lNode)
            lNode->AddMaterial(lMaterial);

    }
}

// Map pyramid control points onto an upside down shape.
void MapShapeOnPyramid(KFbxScene* pScene, KFbxNode* pPyramid)
{
    KFbxShape* lShape = KFbxShape::Create(pScene,"Upside Down");

    KFbxVector4 lControlPoint0(-50, 100, 50);
    KFbxVector4 lControlPoint1(50, 100, 50);
    KFbxVector4 lControlPoint2(50, 100, -50);
    KFbxVector4 lControlPoint3(-50, 100, -50);
    KFbxVector4 lControlPoint4(0, 0, 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.
    lShape->InitControlPoints(16);
    KFbxVector4* lControlPoints = lShape->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;


    KFbxGeometryElementNormal* lNormalElement = lShape->CreateElementNormal();
    lNormalElement->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);
    lNormalElement->SetReferenceMode(KFbxGeometryElement::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);


    KFbxBlendShape* lBlendShape = KFbxBlendShape::Create(pScene,"");
    KFbxBlendShapeChannel* lBlendShapeChannel = KFbxBlendShapeChannel::Create(pScene, "");
    pPyramid->GetMesh()->AddDeformer(lBlendShape);
    lBlendShape->AddBlendShapeChannel(lBlendShapeChannel);
    lBlendShapeChannel->AddTargetShape(lShape);
}

void MapVertexCacheOnTriangle(KFbxScene* pScene, KFbxNode* pTriangle, char* pSampleFileName)
{
    // By convention, all cache files are created in a _fpc folder located at the same
    // place as the .fbx file. 
    KString lFBXAbsolutePath = KFbxFullPath(pSampleFileName);

    // Create a cache directory with the same name as the fbx file
    KString lFPCAbsoluteDirectory;

    lFPCAbsoluteDirectory  = KFbxExtractDirectory(lFBXAbsolutePath);
    lFPCAbsoluteDirectory += "/";
    lFPCAbsoluteDirectory += KFbxExtractFileName(pSampleFileName, false);
    lFPCAbsoluteDirectory += "_fpc";

    // Make this path the shortest possible
    lFPCAbsoluteDirectory = KFbxCleanPath(lFPCAbsoluteDirectory);

    // Now get the point cache absolute and relative file name
    KString lAbsolutePCFileName = lFPCAbsoluteDirectory + KString("/") + pTriangle->GetName();
    lAbsolutePCFileName += gExportVertexCacheMCFormat ? ".xml" : ".pc2";

    KString lRelativePCFileName = KFbxGetRelativeFilePath(KFbxExtractDirectory(lFBXAbsolutePath)+"/", lAbsolutePCFileName);

    // Make sure the direcotry exist.
    if (!KFbxEnsureDirectoryExistance(lAbsolutePCFileName))
    {
        // Cannot create this directory. So do not create the point cache
        return;
    }

    //
    // Create the cache file
    //
    KFbxCache* lCache = KFbxCache::Create(pScene, pTriangle->GetName());

    lCache->SetCacheFileName(lRelativePCFileName, lAbsolutePCFileName);
    lCache->SetCacheFileFormat(gExportVertexCacheMCFormat ? KFbxCache::eMC : KFbxCache::ePC2);

    //
    // Create the vertex deformer
    //
    KFbxVertexCacheDeformer* lDeformer = KFbxVertexCacheDeformer::Create(pScene, pTriangle->GetName());

    lDeformer->SetCache(lCache);
    lDeformer->SetCacheChannel(pTriangle->GetName());
    lDeformer->SetActive(true);

    // Apply the deformer on the mesh
    pTriangle->GetGeometry()->AddDeformer(lDeformer);
}

// Cube is translated to the left.
void SetCubeDefaultPosition(KFbxNode* pCube) 
{
    pCube->LclTranslation.Set(KFbxVector4(-75.0, -50.0, 0.0));
    pCube->LclRotation.Set(KFbxVector4(0.0, 0.0, 0.0));
    pCube->LclScaling.Set(KFbxVector4(1.0, 1.0, 1.0));
}

// Pyramid is translated to the right.
void SetPyramidDefaultPosition(KFbxNode* pPyramid) 
{
    pPyramid->LclTranslation.Set(KFbxVector4(75.0, -50.0, 0.0));
    pPyramid->LclRotation.Set(KFbxVector4(0.0, 0.0, 0.0));
    pPyramid->LclScaling.Set(KFbxVector4(1.0, 1.0, 1.0));
}

void SetTriangleDefaultPosition(KFbxNode* pTriangle)
{
    pTriangle->LclTranslation.Set(KFbxVector4(200.0, -50.0, 0.0));
    pTriangle->LclRotation.Set(KFbxVector4(0.0, 0.0, 0.0));
    pTriangle->LclScaling.Set(KFbxVector4(1.0, 1.0, 1.0));
}

void SetMyKFbxMeshCubeDefaultPosition(KFbxNode* pMyKFbxCube)
{
    pMyKFbxCube->LclTranslation.Set(KFbxVector4(-200.0, -25.0, 0.0));
    pMyKFbxCube->LclRotation.Set(KFbxVector4(0.0, 0.0, 0.0));
    pMyKFbxCube->LclScaling.Set(KFbxVector4(1.0, 1.0, 1.0));
}

// Displays 6 different angles.
void Animate(KFbxNode* pNode, KFbxAnimLayer* pAnimLayer)
{
    KFbxAnimCurve* lCurve = NULL;
    KTime lTime;
    int lKeyIndex = 0;

    lCurve = pNode->LclRotation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_R_Y, true);
    if (lCurve)
    {
        lCurve->KeyModifyBegin();

        lTime.SetSecondDouble(0.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 0.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(0.5);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 90.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(1.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 180.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(1.5);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 270.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(2.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 360.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lCurve->KeyModifyEnd();
    }

    lCurve = pNode->LclRotation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_R_X, true);
    if (lCurve)
    {
        lCurve->KeyModifyBegin();

        lTime.SetSecondDouble(2.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 0.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(2.5);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 90.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(3.5);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, -90.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(4.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 0.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lCurve->KeyModifyEnd();
    }


    // The upside down shape is at index 0 because it is the only one.
    // The cube has no shape so the function returns NULL is this case.
    KFbxGeometry* lGeometry = (KFbxGeometry*) pNode->GetNodeAttribute();
    lCurve = lGeometry->GetShapeChannel(0, 0, pAnimLayer, true);
    if (lCurve)
    {
        lCurve->KeyModifyBegin();

        lTime.SetSecondDouble(0.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 0.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(2.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 100.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lTime.SetSecondDouble(4.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySetValue(lKeyIndex, 0.0);
        lCurve->KeySetInterpolation(lKeyIndex, KFbxAnimCurveDef::eINTERPOLATION_CUBIC);

        lCurve->KeyModifyEnd();
    }
}

void AnimateVertexCacheOnTriangleDoubleVertex(KFbxNode* pTriangle, double pFrameRate)
{
    //
    // Move the vertices from their original position to the center.
    //
    KFbxVertexCacheDeformer* lDeformer = static_cast<KFbxVertexCacheDeformer*>(pTriangle->GetGeometry()->GetDeformer(0, KFbxDeformer::eVERTEX_CACHE));
    KFbxCache*               lCache = lDeformer->GetCache();
    bool                     lRet;

    // Write samples for 4 seconds
    KTime lTimeIncrement, lCurrentTime, lStopTime;
    lTimeIncrement.SetTime(0, 0, 0, 1); // 1 frame @ current frame rate
    lStopTime.SetTime(0, 0, 4);         // 4 seconds

    unsigned int lFrameCount = (unsigned int)(lStopTime.Get()/lTimeIncrement.Get());

    // Open the file for writing
    if (gExportVertexCacheMCFormat)
    {
        lRet = lCache->OpenFileForWrite(KFbxCache::eMC_ONE_FILE, pFrameRate, pTriangle->GetName());
    }
    else
    {
        lRet = lCache->OpenFileForWrite(0.0, pFrameRate, lFrameCount, 3);  
    }

    if (!lRet)
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
        return;
    }

    int lChannelIndex = lCache->GetChannelIndex(pTriangle->GetName());
    unsigned int lCurrentFrame = 0;

    while (lCurrentTime <= lStopTime)
    {
        double lVertices[3][3];
        double lScaleFactor = 1.0-double(lCurrentTime.GetSecondDouble()/lStopTime.GetSecondDouble());

        lVertices[0][0] = -50.0 * lScaleFactor;  // X
        lVertices[0][1] = 0.0;                   // Y
        lVertices[0][2] = 50.0  * lScaleFactor;  // Z

        lVertices[1][0] = 50.0  * lScaleFactor;  // X
        lVertices[1][1] = 0.0;                   // Y
        lVertices[1][2] = 50.0  * lScaleFactor;  // Z

        lVertices[2][0] = 0.0   * lScaleFactor;  // X
        lVertices[2][1] = 50.0  * lScaleFactor;  // Y
        lVertices[2][2] = -50.0 * lScaleFactor;  // Z

        if (gExportVertexCacheMCFormat)
        {
            lCache->Write(lChannelIndex, lCurrentTime, &lVertices[0][0], 3);
        }
        else
        {
            lCache->Write(lCurrentFrame, &lVertices[0][0]);
        }

        lCurrentTime += lTimeIncrement;
        lCurrentFrame++;
    }

    if (!lCache->CloseFile())
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
    }
}

void AnimateVertexCacheOnTriangleInt32(KFbxNode* pTriangle, double pFrameRate)
{
    //
    // Move the vertices from their original position to the center.
    //
    KFbxVertexCacheDeformer* lDeformer = static_cast<KFbxVertexCacheDeformer*>(pTriangle->GetGeometry()->GetDeformer(0, KFbxDeformer::eVERTEX_CACHE));
    KFbxCache*               lCache = lDeformer->GetCache();
    bool                     lRet = false;

    // Write samples for 4 seconds
    KTime lTimeIncrement, lCurrentTime, lStopTime;
    lTimeIncrement.SetTime(0, 0, 0, 1); // 1 frame @ current frame rate
    lStopTime.SetTime(0, 0, 4);         // 4 seconds

    unsigned int lFrameCount = (unsigned int)(lStopTime.Get()/lTimeIncrement.Get());

    // Open the file for writing int32 array
    if (gExportVertexCacheMCFormat)
    {
        lRet = lCache->OpenFileForWrite(KFbxCache::eMC_ONE_FILE, pFrameRate, pTriangle->GetName(), KFbxCache::kInt32Array);
    }

    if (!lRet)
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
        return;
    }

    int lChannelIndex = lCache->GetChannelIndex(pTriangle->GetName());
    unsigned int lCurrentFrame = 0;

    while (lCurrentTime <= lStopTime)
    {
        int v[2];

        v[0] = -10 + lCurrentFrame;
        v[1] = v[0]+1;

        if (gExportVertexCacheMCFormat)
        {
            lCache->Write(lChannelIndex, lCurrentTime, &v[0], 2);
        }

        lCurrentTime += lTimeIncrement;
        lCurrentFrame++;
    }

    if (!lCache->CloseFile())
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
    }

    // Open the file for reading int32 array
    if (gExportVertexCacheMCFormat)
    {
        lRet = lCache->OpenFileForRead();
    }

    if (!lRet)
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
        return;
    }

    KTime lCurrentTime2;
    lCurrentFrame = 0;

    printf("Testing awCache int32 array read and write\n");
    bool passTest = true;
    // printf("Should print out -10 .. 110\n");
    while (lCurrentTime2 <= lStopTime)
    {
        int v[2];
        if (gExportVertexCacheMCFormat)
        {
            lCache->Read(lChannelIndex, lCurrentTime2, &v[0], 2);
            if ((v[0] != -10 + lCurrentFrame) || (v[0]+1 != v[1]) ) 
            {
                printf("awCache int32 array read/write mismatch\n");
                passTest = false;
                break;
            }
            // printf("%d ",v[0]);
        }

        lCurrentTime2 += lTimeIncrement;
        lCurrentFrame++;
    }

    if (!lCache->CloseFile())
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
    }

    if (passTest)
    {
    ;
    printf("awCache int32 array read and write test passed\n");
    }
}


void AnimateVertexCacheOnTriangleFloat(KFbxNode* pTriangle, double pFrameRate)
{
    //
    // Move the vertices from their original position to the center.
    //
    KFbxVertexCacheDeformer* lDeformer = static_cast<KFbxVertexCacheDeformer*>(pTriangle->GetGeometry()->GetDeformer(0, KFbxDeformer::eVERTEX_CACHE));
    KFbxCache*               lCache = lDeformer->GetCache();
    bool                     lRet;

    // Write samples for 4 seconds
    KTime lTimeIncrement, lCurrentTime, lStopTime;
    lTimeIncrement.SetTime(0, 0, 0, 1); // 1 frame @ current frame rate
    lStopTime.SetTime(0, 0, 4);         // 4 seconds

    unsigned int lFrameCount = (unsigned int)(lStopTime.Get()/lTimeIncrement.Get());

    // Open the file for writing
    if (gExportVertexCacheMCFormat)
    {
        lRet = lCache->OpenFileForWrite(KFbxCache::eMC_ONE_FILE, pFrameRate, pTriangle->GetName(), KFbxCache::kFloatVectorArray);
    }
    else
    {
        lRet = false;
    }

    if (!lRet)
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
        return;
    }

    int lChannelIndex = lCache->GetChannelIndex(pTriangle->GetName());
    unsigned int lCurrentFrame = 0;

    while (lCurrentTime <= lStopTime)
    {
        float lVertices[3][3];
        float lScaleFactor = 1.0f-float(lCurrentTime.GetSecondDouble()/lStopTime.GetSecondDouble());

        lVertices[0][0] = -50.0f * lScaleFactor;  // X
        lVertices[0][1] = 0.0f;                   // Y
        lVertices[0][2] = 50.0f  * lScaleFactor;  // Z

        lVertices[1][0] = 50.0f  * lScaleFactor;  // X
        lVertices[1][1] = 0.0f;                   // Y
        lVertices[1][2] = 50.0f  * lScaleFactor;  // Z

        lVertices[2][0] = 0.0f   * lScaleFactor;  // X
        lVertices[2][1] = 50.0f  * lScaleFactor;  // Y
        lVertices[2][2] = -50.0f * lScaleFactor;  // Z

        if (gExportVertexCacheMCFormat)
        {
            lCache->Write(lChannelIndex, lCurrentTime, &lVertices[0][0], 3);
        }

        lCurrentTime += lTimeIncrement;
        lCurrentFrame++;
    }

    if (!lCache->CloseFile())
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
    } 

    // Open the file for reading int32 array
    if (gExportVertexCacheMCFormat)
    {
        lRet = lCache->OpenFileForRead();
    }

    if (!lRet)
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
        return;
    }

    KTime lCurrentTime2;
    lCurrentFrame = 0;
    #define AbsFlt(a)      (((a) < 0) ? -(a) : (a))
    #define CmpFlt(a,b)    (AbsFlt((a)-(b)) > 1e-5)

    printf("Testing awCache Float3 array read and write\n");
    bool passTest = true;
    while (lCurrentTime2 <= lStopTime)
    {
        float lVertices[3][3];
        float lScaleFactor = 1.0f-float(lCurrentTime2.GetSecondDouble()/lStopTime.GetSecondDouble());

        if (gExportVertexCacheMCFormat)
        {
            lCache->Read(lChannelIndex, lCurrentTime2, &lVertices[0][0], 3);
            
            if ((CmpFlt(lVertices[0][0], -50.0f * lScaleFactor) || CmpFlt(lVertices[0][1], 0.0f                ) || CmpFlt(lVertices[0][2], 50.0f * lScaleFactor)) ||
                (CmpFlt(lVertices[1][0],  50.0f * lScaleFactor) || CmpFlt(lVertices[1][1], 0.0f                ) || CmpFlt(lVertices[1][2], 50.0f * lScaleFactor)) ||
                (CmpFlt(lVertices[2][0],   0.0f * lScaleFactor) || CmpFlt(lVertices[2][1], 50.0f * lScaleFactor) || CmpFlt(lVertices[2][2],-50.0f * lScaleFactor)))
            {
                printf("awCache Float3 array read/write mismatch\n");
                passTest = false;
                break;
            }
        }   

        lCurrentTime2 += lTimeIncrement;
        lCurrentFrame++;
    }

    #undef AbsFlt
    #undef CmpFlt
    
    if (!lCache->CloseFile())
    {
        // print out the error
        printf("File open error: %s\n", lCache->GetError().GetLastErrorString());
    }

    if (passTest)
    {
        printf("awCache float3 array read and write test passed\n");
    }

}