UIExamples/CubeCreator/SDK_Utility.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. 

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

#include "SDK_Utility.h"

// declare global
KFbxSdkManager*   gSdkManager = NULL;
KFbxScene*        gScene      = NULL;
KFbxFileTexture*  gTexture    = NULL;
KFbxSurfacePhong* gMaterial   = NULL;

int    gCubeNumber          = 1;     // Cube Number
int    gCubeRotationAxis    = 1;     // Cube Rotation Axis 0==X, 1==Y, 2==Z
double gCubeXPos            = 0.0;   // initial CubXPos
double gCubeYPos            = 20.0;  // initial CubeYPos
double gCubeZPos            = 0.0;   // initial CubeZPos

KFbxAnimLayer* gAnimLayer = NULL;  // holder of animation curves
KString gAppPath;     // path where the application started


#ifdef IOS_REF
    #undef  IOS_REF
    #define IOS_REF (*(pSdkManager->GetIOSettings()))
#endif


// to create an instance of the SDK manager
bool InitializeSdkObjects(
                          KFbxSdkManager*& pSdkManager, 
                          KFbxScene*& pScene
                          )
{
    // The first thing to do is to create the FBX SDK manager which is the 
    // object allocator for almost all the classes in the SDK.
    pSdkManager = KFbxSdkManager::Create();

    if (!pSdkManager)
    {
        return false;
    }

    // create an IOSettings object
    KFbxIOSettings * ios = KFbxIOSettings::Create(pSdkManager, IOSROOT );
    pSdkManager->SetIOSettings(ios);

    // Create the entity that will hold the scene.
    pScene = KFbxScene::Create(pSdkManager,"");

    if(pScene == NULL) return false;

    return true;
}

// to destroy an instance of the SDK manager
void DestroySdkObjects(
                       KFbxSdkManager* pSdkManager
                       )
{
    // Delete the FBX SDK manager. All the objects that have been allocated 
    // using the FBX SDK manager and that haven't been explicitly destroyed 
    // are automatically destroyed at the same time.

    if (pSdkManager) pSdkManager->Destroy();
}


// to create a basic scene
bool CreateScene()
{
    // Initialize the KFbxSdkManager and the KFbxScene
    if(InitializeSdkObjects(gSdkManager, gScene) == false)
    {
        return false;
    }

    // set the animation stack and use the unique AnimLayer to support all the animation
    KFbxAnimStack* lAnimStack = KFbxAnimStack::Create(gScene, "Animation stack camera animation");
    gAnimLayer = KFbxAnimLayer::Create(gScene, "Base Layer");
    lAnimStack->AddMember(gAnimLayer);

    // create a marker
    KFbxNode* lMarker  = CreateMarker(gScene, "Marker");

    // create a camera
    KFbxNode* lCamera  = CreateCamera(gScene, "Camera");

    // create a single texture shared by all cubes
    CreateTexture(gScene);

    // create a material shared by all faces of all cubes
    CreateMaterial(gScene);

    // set the camera point of interest on the marker
    SetCameraPointOfInterest(lCamera, lMarker);

    // set the marker position
    SetMarkerDefaultPosition(lMarker);

    // set the camera position
    SetCameraDefaultPosition(lCamera);

    // animate the camera
    AnimateCamera(lCamera, gAnimLayer);

    // build a minimum scene graph
    KFbxNode* lRootNode = gScene->GetRootNode();
    lRootNode->AddChild(lMarker);
    lRootNode->AddChild(lCamera);

    // set camera switcher as the default camera
    gScene->GetGlobalSettings().SetDefaultCamera((char *)lCamera->GetName());

    return true;
}

// create a new cube
void CreateCube(bool pWithMaterial, bool pWithTexture, bool pAnimate)
{
    // make a new cube name
    KString lCubeName = "Cube number ";
    lCubeName += KString(gCubeNumber);

    // create a new cube
    CreateCubeDetailed( lCubeName.Buffer(), 
        gCubeXPos, 
        gCubeYPos, 
        gCubeZPos, 
        gCubeRotationAxis, 
        pWithMaterial, 
        pWithTexture, 
        pAnimate
        );

    // compute for next cube creation    
    gCubeNumber++; // cube number

    // set next pos
    if(gCubeXPos >= 0.0)
    {
        gCubeXPos += 50.0;
        gCubeXPos *= -1.0;
        gCubeRotationAxis++; // change rotation axis
    }
    else
    {
        gCubeXPos *= -1.0;
    }

    // go up
    gCubeYPos += 30.0;

    if(gCubeRotationAxis > 2) gCubeRotationAxis = 0; // cube rotation
}

// to remove cubes only
void RemoveCubes()
{
    if(gSdkManager == NULL) return;

    // get the node count
    int nc = gScene->GetNodeCount();

    // we want to keep the root node, the marker node and the camera node
    if(nc <= 3) return;

    // remove other nodes (cube nodes)
    // start from the end
    for(int i=nc-1; i >= 3; i--)
    {
        KFbxNode *node = gScene->GetNode(i);
        gScene->RemoveNode(node);

        // remove animation
        KFbxAnimCurveNode* lCurveNode = node->LclRotation.GetCurveNode(gAnimLayer);
        if(lCurveNode != NULL)
        {
            lCurveNode->Destroy(true, true);
        }

        // remove from memory
        node->Destroy(true, true);
    }

    // reset cube data
    SetInitialCubeData();
}

// create a new cube
void CreateCubeDetailed(    char* pCubeName, 
                        double pX, 
                        double pY, 
                        double pZ, 
                        int pRotateAxe, 
                        bool pWithMaterial, 
                        bool pWithTexture, 
                        bool pAnimate
                        )
{
    KFbxNode* lCube = CreateCubeMesh(gScene, pCubeName);

    // set the cube position
    lCube->LclTranslation.Set(KFbxVector4(pX, pY, pZ));

    if(pAnimate)
    {
        AnimateCube(lCube, gAnimLayer, pRotateAxe);
    }

    if(pWithTexture)
    {
        // if we asked to create the cube with a texture, we need 
        // a material present because the texture connects to the
        // material DiffuseColor property
        if (!pWithMaterial)
            AddMaterials(lCube->GetMesh());
    }

    if(pWithMaterial)
    {
        // use already created material
        AddMaterials(lCube->GetMesh());
    }

    gScene->GetRootNode()->AddChild(lCube);
}

// to save a scene to a FBX file
bool Export(    
            const char* pFilename, 
            int pFileFormat 
            )
{
    return SaveScene(gSdkManager, gScene, pFilename, pFileFormat, true); // true -> embed texture file
}

// to save a scene to a FBX file
bool SaveScene(KFbxSdkManager* pSdkManager, KFbxDocument* pScene, const char* pFilename, int pFileFormat, bool pEmbedMedia)
{
    if(pSdkManager == NULL) return false;
    if(pScene      == NULL) return false;
    if(pFilename   == NULL) return false;

    bool lStatus = true;

    // Create an exporter.
    KFbxExporter* lExporter = KFbxExporter::Create(pSdkManager, "");

    if( pFileFormat < 0 || pFileFormat >= pSdkManager->GetIOPluginRegistry()->GetWriterFormatCount() )
    {
        // Write in fall back format if pEmbedMedia is true
        pFileFormat = pSdkManager->GetIOPluginRegistry()->GetNativeWriterFormat();

        if (!pEmbedMedia)
        {
            //Try to export in ASCII if possible
            int lFormatIndex, lFormatCount = pSdkManager->GetIOPluginRegistry()->GetWriterFormatCount();

            for (lFormatIndex=0; lFormatIndex<lFormatCount; lFormatIndex++)
            {
                if (pSdkManager->GetIOPluginRegistry()->WriterIsFBX(lFormatIndex))
                {
                    KString lDesc =pSdkManager->GetIOPluginRegistry()->GetWriterFormatDescription(lFormatIndex);
                    char *lASCII = "ascii";
                    if (lDesc.Find(lASCII)>=0)
                    {
                        pFileFormat = lFormatIndex;
                        break;
                    }
                }
            }
        }
    }

    // Initialize the exporter by providing a filename.
    if(lExporter->Initialize(pFilename, pFileFormat, pSdkManager->GetIOSettings() ) == false)
    {
        return false;
    }

    // Set the export states. By default, the export states are always set to 
    // true except for the option eEXPORT_TEXTURE_AS_EMBEDDED. The code below 
    // shows how to change these states.
    IOS_REF.SetBoolProp(EXP_FBX_MATERIAL,        true);
    IOS_REF.SetBoolProp(EXP_FBX_TEXTURE,         true);
    IOS_REF.SetBoolProp(EXP_FBX_EMBEDDED,        pEmbedMedia);
    IOS_REF.SetBoolProp(EXP_FBX_SHAPE,           true);
    IOS_REF.SetBoolProp(EXP_FBX_GOBO,            true);
    IOS_REF.SetBoolProp(EXP_FBX_ANIMATION,       true);
    IOS_REF.SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true);

    // Export the scene.
    lStatus = lExporter->Export(pScene);

    // Destroy the exporter.
    lExporter->Destroy();

    return lStatus;
}

// to get the filters for the <Save file> dialog (description + file extention)
const char *GetWriterSFNFilters()
{
    int nbWriters = gSdkManager->GetIOPluginRegistry()->GetWriterFormatCount();

    KString s;
    int i=0;

    for(i=0; i < nbWriters; i++)
    {
        s += gSdkManager->GetIOPluginRegistry()->
            GetWriterFormatDescription(i);
        s += "|*.";
        s += gSdkManager->GetIOPluginRegistry()->
            GetWriterFormatExtension(i);
        s += "|";
    }

    // replace | by \0
    int nbChar   = int(strlen(s.Buffer())) + 1;
    char *filter = new char[ nbChar ];
    memset(filter, 0, nbChar);

    strcpy(filter, s.Buffer());

    for(i=0; i < int(strlen(s.Buffer())); i++)
    {
        if(filter[i] == '|')
        {
            filter[i] = 0;
        }
    }

    // the caller must delete this allocated memory
    return filter;
}

// to get a file extention for a WriteFileFormat
const char *GetFileFormatExt(
                             const int pWriteFileFormat
                             )
{
    char *buf = new char[10];
    memset(buf, 0, 10);

    // add a starting point .
    buf[0] = '.';
    const char * ext = gSdkManager->GetIOPluginRegistry()->
        GetWriterFormatExtension(pWriteFileFormat);
    strcat(buf, ext);

    // the caller must delete this allocated memory
    return buf;
}

// to get the root node
const KFbxNode* GetRootNode()
{
    return gScene->GetRootNode();
}

// to get the root node name
const char * GetRootNodeName()
{
    return GetRootNode()->GetName();
}

// to get a string from the node name and attribute type
KString GetNodeNameAndAttributeTypeName(const KFbxNode* pNode)
{
    KString s = pNode->GetName();

    KFbxNodeAttribute::EAttributeType lAttributeType;

    if(pNode->GetNodeAttribute() == NULL)
    {
        s += " (No node attribute type)";
    }
    else
    {
        lAttributeType = (pNode->GetNodeAttribute()->GetAttributeType());

        switch (lAttributeType)
        {
        case KFbxNodeAttribute::eMARKER:                s += " (Marker)";               break;
        case KFbxNodeAttribute::eSKELETON:              s += " (Skeleton)";             break;
        case KFbxNodeAttribute::eMESH:                  s += " (Mesh)";                 break;
        case KFbxNodeAttribute::eCAMERA:                s += " (Camera)";               break;
        case KFbxNodeAttribute::eLIGHT:                 s += " (Light)";                break;
        case KFbxNodeAttribute::eBOUNDARY:              s += " (Boundary)";             break;
        case KFbxNodeAttribute::eOPTICAL_MARKER:        s += " (Optical marker)";       break;
        case KFbxNodeAttribute::eOPTICAL_REFERENCE:     s += " (Optical reference)";    break;
        case KFbxNodeAttribute::eCAMERA_SWITCHER:       s += " (Camera switcher)";      break;
        case KFbxNodeAttribute::eNULL:                  s += " (Null)";                 break;
        case KFbxNodeAttribute::ePATCH:                 s += " (Patch)";                break;
        case KFbxNodeAttribute::eNURB:                  s += " (NURB)";                 break;
        case KFbxNodeAttribute::eNURBS_SURFACE:         s += " (Nurbs surface)";        break;
        case KFbxNodeAttribute::eNURBS_CURVE:           s += " (NURBS curve)";          break;
        case KFbxNodeAttribute::eTRIM_NURBS_SURFACE:    s += " (Trim nurbs surface)";   break;
        case KFbxNodeAttribute::eUNIDENTIFIED:          s += " (Unidentified)";         break;
        }   
    }

    return s;
}

// to get a string from the node default translation values
KString GetDefaultTranslationInfo(
                                  const KFbxNode* pNode
                                  )
{
    KFbxVector4 v4;
    v4 = ((KFbxNode*)pNode)->LclTranslation.Get();

    return KString("Translation (X,Y,Z): ") + KString(v4.GetAt(0)) + ", " + KString(v4.GetAt(1)) + ", " + KString(v4.GetAt(2));
}

// to get a string with info about material, texture, animation
KString GetNodeInfo(
                    const KFbxNode* pNode
                    )
{
    KString s;

    // check for material and texture
    int                     lMaterialCount = pNode->GetMaterialCount();
    KFbxSurfacePhong*       lMaterial = NULL;
    bool                    lTextureExist = false;
    if( lMaterialCount > 0)
    {
        s+= "[Material: Yes] ";
        for( int i = 0; i < lMaterialCount; ++i)
        {
            lMaterial = (KFbxSurfacePhong*)pNode->GetMaterial( i);
            if( lMaterial->Diffuse.GetSrcObjectCount( KFbxFileTexture::ClassId) > 0)
            {
                lTextureExist = true;
                break;
            }
        }

    }
    else
    {
        s+= "[Material: No] ";
        s+= "[Texture: No] ";
    }

    if( lTextureExist)              s+= "[Texture: Yes] ";
    else                            s+= "[Texture: No] ";


    // check for animation
    bool anim = false;
    KFbxAnimCurveNode* lCurveNode = NULL;

    // check rotation FCurve node
    lCurveNode =((KFbxNode* )pNode)->LclRotation.GetCurveNode(gAnimLayer);
    if(lCurveNode != NULL) anim = true;

    // check Translation FCurve node
    lCurveNode = ((KFbxNode* )pNode)->LclTranslation.GetCurveNode(gAnimLayer);
    if(lCurveNode != NULL) anim = true;

    if(anim == true)
    {
        s+= "[Animation: Yes] ";
    }
    else
    {
        s+= "[Animation: No] ";
    }

    return s;
}

// Create a marker to use a point of interest for the camera. 
KFbxNode* CreateMarker(KFbxScene* pScene, char* pName)
{
    KFbxMarker* lMarker = KFbxMarker::Create(pScene,pName);

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

    lNode->SetNodeAttribute(lMarker);

    return lNode;
}

// Create a camera.
KFbxNode* CreateCamera(KFbxScene* pScene, char* pName)
{
    KFbxCamera* lCamera = KFbxCamera::Create(pScene,pName);

    // Set camera property for a classic TV projection with aspect ratio 4:3
    lCamera->SetFormat(KFbxCamera::eNTSC);

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

    lNode->SetNodeAttribute(lCamera);

    return lNode;
}

// Set target of the camera.
void SetCameraPointOfInterest(KFbxNode* pCamera, KFbxNode* pPointOfInterest)
{
    // Set the camera to always point at this node.
    pCamera->SetTarget(pPointOfInterest);
}

// Set marker default position.
void SetMarkerDefaultPosition(KFbxNode* pMarker)
{
    //The marker is positioned above the origin. There is no rotation and no scaling.
    pMarker->LclTranslation.Set(KFbxVector4(0.0, 40.0, 0.0));
    pMarker->LclRotation.Set(KFbxVector4(0.0, 0.0, 0.0));
    pMarker->LclScaling.Set(KFbxVector4(1.0, 1.0, 1.0));
}

// Compute the camera position.
void SetCameraDefaultPosition(KFbxNode* pCamera)
{
    // set the initial camera position
    KFbxVector4 lCameraLocation(0.0, 200.0, -100.0);
    pCamera->LclTranslation.Set(lCameraLocation);
}

// The camera move on X and Y axis.
void AnimateCamera(KFbxNode* pCamera, KFbxAnimLayer* pAnimLayer)
{
    KFbxAnimCurve* lCurve = NULL;
    KTime lTime;
    int lKeyIndex = 0;

    pCamera->LclTranslation.GetCurveNode(pAnimLayer, true);

    // X translation.
    lCurve = pCamera->LclTranslation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_T_X, true);
    if (lCurve)
    {
        lCurve->KeyModifyBegin();

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

        lTime.SetSecondDouble(20.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySet(lKeyIndex, lTime, 500.0, KFbxAnimCurveDef::eINTERPOLATION_LINEAR);

        lCurve->KeyModifyEnd();
    }

    // Y translation.
    lCurve = pCamera->LclTranslation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_T_Y, true);
    if (lCurve)
    {
        lCurve->KeyModifyBegin();

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

        lTime.SetSecondDouble(20.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySet(lKeyIndex, lTime, 800.0, KFbxAnimCurveDef::eINTERPOLATION_LINEAR);

        lCurve->KeyModifyEnd();
    }
}

// The cube rotate on X or Y or Z.
void AnimateCube(KFbxNode* pCube, KFbxAnimLayer* pAnimLayer, int pRotAxe)
{
    KFbxAnimCurve* lCurve = NULL;
    KTime lTime;
    int lKeyIndex = 0;

    pCube->LclRotation.GetCurveNode(pAnimLayer, true);
         if(pRotAxe == 0) lCurve = pCube->LclRotation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_R_X, true);
    else if(pRotAxe == 1) lCurve = pCube->LclRotation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_R_Y, true);
    else if(pRotAxe == 2) lCurve = pCube->LclRotation.GetCurve<KFbxAnimCurve>(pAnimLayer, KFCURVENODE_R_Z, true);

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

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

        lTime.SetSecondDouble(20.0);
        lKeyIndex = lCurve->KeyAdd(lTime);
        lCurve->KeySet(lKeyIndex, lTime, -3500, KFbxAnimCurveDef::eINTERPOLATION_LINEAR);
        lCurve->KeyModifyEnd();
    }
}

// Create a cube mesh. 
KFbxNode* CreateCubeMesh(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);

    // Set the normal values 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);


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

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

    // Create polygons. Assign texture and texture UV indices.
    for(i = 0; i < 6; i++)
    {
        // all faces of the cube have the same texture
        lMesh->BeginPolygon(-1, -1, -1, false);

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

            // update the index array of the UVs that map the texture to the face
            lUVDiffuseElement->GetIndexArray().SetAt(i*4+j, j);
        }

        lMesh->EndPolygon ();
    }

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

    // set the node attribute
    lNode->SetNodeAttribute(lMesh);

    // set the shading mode to view texture
    lNode->SetShadingMode(KFbxNode::eTEXTURE_SHADING);

    // rescale the cube
    lNode->LclScaling.Set(KFbxVector4(0.3, 0.3, 0.3));

    // return the KFbxNode
    return lNode;
}

// Create a global texture for cube.
void CreateTexture(KFbxScene* pScene)
{
    gTexture = KFbxFileTexture::Create(pScene,"Diffuse Texture");

    // Resource file must be in the application's directory.
    KString lTexPath = gAppPath + "\\Crate.jpg";

    // Set texture properties.
    gTexture->SetFileName(lTexPath.Buffer()); 
    gTexture->SetTextureUse(KFbxTexture::eSTANDARD);
    gTexture->SetMappingType(KFbxTexture::eUV);
    gTexture->SetMaterialUse(KFbxFileTexture::eMODEL_MATERIAL);
    gTexture->SetSwapUV(false);
    gTexture->SetTranslation(0.0, 0.0);
    gTexture->SetScale(1.0, 1.0);
    gTexture->SetRotation(0.0, 0.0);
}


// Create global material for cube.
void CreateMaterial(KFbxScene* pScene)
{
    KString lMaterialName = "material";
    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);
    gMaterial = KFbxSurfacePhong::Create(pScene, lMaterialName.Buffer());

    // Generate primary and secondary colors.
    gMaterial->Emissive            .Set(lBlack);
    gMaterial->Ambient             .Set(lRed);
    gMaterial->Diffuse             .Set(lDiffuseColor);
    gMaterial->TransparencyFactor  .Set(40.5);
    gMaterial->ShadingModel        .Set(lShadingName);
    gMaterial->Shininess           .Set(0.5);

    // the texture need to be connected to the material on the corresponding property
    if (gTexture)
        gMaterial->Diffuse.ConnectSrcObject(gTexture);
}

void AddMaterials(KFbxMesh* pMesh)
{

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

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

    // We are in eBY_POLYGON, so there's only need for 6 index (a cube has 6 polygons).
    lMaterialElement->GetIndexArray().SetCount(6);

    // Set the Index 0 to 6 to the material in position 0 of the direct array.
    for(int i=0; i<6; ++i)
        lMaterialElement->GetIndexArray().SetAt(i,0);


}

// Reset camera values
void SetInitialCubeData()
{
    gCubeNumber          = 1;     // Cube Number
    gCubeRotationAxis    = 1;     // Cube Rotation Axis 0==X, 1==Y, 2==Z
    gCubeXPos            = 0.0;   // initial CubXPos
    gCubeYPos            = 20.0;  // initial CubeYPos
    gCubeZPos            = 0.0;   // initial CubeZPos
}