Instances/main.cxx

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

 Copyright (C) 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. 

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

/****************************************************************************/
/* This example shows how to instanciate meshes and curves in FBX.          */
/*  - Create a cube                                                         */
/*  - Create instances of this cube (new nodes that point to the same mesh) */
/*  - Create an animation curve                                             */
/*  - Animate all cubes using the same animation curve.                     */
/****************************************************************************/

#include <fbxsdk.h>

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

#define SAMPLE_FILENAME "Instances.fbx"


// Globals
int gNbCubes = 10;

// Function prototypes.
bool CreateScene(KFbxSdkManager* pSdkManager, KFbxScene* pScene);
KFbxNode* CreateCube(KFbxScene* pScene, const char* pName);
KFbxNode* CreateCubeInstance(KFbxScene* pScene, const char* pName, KFbxMesh* pFirstCube);
KFbxAnimCurve* CreateAnimCurve(KFbxScene* pScene);
void AnimateCube(KFbxNode* pCube, KFbxAnimLayer* pAnimLayer, KFbxAnimCurve* pAnimCurve, int pRotAxis);


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

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

    // Update cube number
    if (argc > 2)
    {
        gNbCubes = atoi(argv[2]);
        if (gNbCubes < 1)
        {
            gNbCubes = 1;
        }
    }

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

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

    // Save the scene.

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

    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(KFbxSdkManager *pSdkManager, KFbxScene* pScene)
{
    // Initial cube position
    double lX = 0.0;
    double lY = 20.0;
    double lZ = 0.0;

    KFbxMesh* lCubeMesh = NULL;
    KFbxNode* lCubeNode = NULL;

    KFbxAnimStack* lAnimStack = KFbxAnimStack::Create(pScene, "Cube Animation Stack");
    KFbxAnimLayer* lAnimLayer = KFbxAnimLayer::Create(pScene, "Base Layer");
    lAnimStack->AddMember(lAnimLayer);

    for (int i = 0; i < gNbCubes; i++)
    {
        KString lCubeName = "Cube ";
        lCubeName += (i+1);

        if (i == 0)
        {
            // Create first cube
            lCubeNode = CreateCube(pScene, lCubeName.Buffer());
            lCubeMesh = lCubeNode->GetMesh();
        }
        else
        {
            // Create cube instance
            lCubeNode = CreateCubeInstance(pScene, lCubeName.Buffer(), lCubeMesh);
        }

        // set the cube position
        lCubeNode->LclTranslation.Set(KFbxVector4(lX, lY, lZ));

        // Animate cube
        // Create an animation curve for each node.
        KFbxAnimCurve* lAnimCurve = CreateAnimCurve(pScene);
        AnimateCube(lCubeNode, lAnimLayer, lAnimCurve, i%3);
        // alternate sides of X
        if (lX >= 0)
        {
            lX += 50;
        }
        else
        {
            lX -= 50;
        }
        lX *= -1.0;
        lY += 30.0;
    }

    return true;
}


// The cube rotates around X, Y or Z.
void AnimateCube(KFbxNode* pCube, KFbxAnimLayer* pAnimLayer, KFbxAnimCurve* pAnimCurve, int pRotAxis)
{
    KFbxAnimCurveNode *lCurveNode = pCube->LclRotation.GetCurveNode(pAnimLayer, true);
    
    // Find out which channel to animate: rotate around X axis, Y axis, or Z axis.
    if(pRotAxis == 0) 
    {
        lCurveNode->ConnectToChannel(pAnimCurve, KFCURVENODE_R_X);
    }
    else if(pRotAxis == 1)
    {
        lCurveNode->ConnectToChannel(pAnimCurve, KFCURVENODE_R_Y);
    }
    else if(pRotAxis == 2)
    {
        lCurveNode->ConnectToChannel(pAnimCurve, KFCURVENODE_R_Z);
    }
}

// Create a cube with a fresh new mesh, and add it to the scene.
KFbxNode* CreateCube(KFbxScene* pScene, const 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;

    // Set the normals on Layer 0.
    KFbxLayer* lLayer = lMesh->GetLayer(0);
    if (lLayer == NULL)
    {
        lMesh->CreateLayer();
        lLayer = lMesh->GetLayer(0);
    }

    // We want to have one normal for each vertex (or control point),
    // so we set the mapping mode to eBY_CONTROL_POINT.
    KFbxLayerElementNormal* lLayerElementNormal= KFbxLayerElementNormal::Create(lMesh, "");

    lLayerElementNormal->SetMappingMode(KFbxLayerElement::eBY_CONTROL_POINT);

    // Set the normal values for every control point.
    lLayerElementNormal->SetReferenceMode(KFbxLayerElement::eDIRECT);

    lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalZPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalXPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalZNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalXNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalYPos);
    lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);
    lLayerElementNormal->GetDirectArray().Add(lNormalYNeg);

    lLayer->SetNormals(lLayerElementNormal);

    // 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.
    KFbxLayerElementUV* lUVDiffuseLayer = KFbxLayerElementUV::Create(lMesh, "DiffuseUV");
    lUVDiffuseLayer->SetMappingMode(KFbxLayerElement::eBY_POLYGON_VERTEX);
    lUVDiffuseLayer->SetReferenceMode(KFbxLayerElement::eINDEX_TO_DIRECT);
    lLayer->SetUVs(lUVDiffuseLayer, KFbxLayerElement::eDIFFUSE_TEXTURES);

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

    lUVDiffuseLayer->GetDirectArray().Add(lVectors0);
    lUVDiffuseLayer->GetDirectArray().Add(lVectors1);
    lUVDiffuseLayer->GetDirectArray().Add(lVectors2);
    lUVDiffuseLayer->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.
    lUVDiffuseLayer->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
            lUVDiffuseLayer->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));

    // Add node to the scene
    pScene->GetRootNode()->AddChild(lNode);

    // return the KFbxNode
    return lNode;
}

// Create a cube instance with the given mesh as node attribute, and add it to the scene.
KFbxNode* CreateCubeInstance(KFbxScene* pScene, const char* pName, KFbxMesh* pFirstCube)
{
    // create a KFbxNode
    KFbxNode* lNode = KFbxNode::Create(pScene,pName);

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

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

    // Add node to the scene
    pScene->GetRootNode()->AddChild(lNode);

    // return the KFbxNode
    return lNode;
}

// Create a single animation curve that will be used by all cubes.
KFbxAnimCurve* CreateAnimCurve(KFbxScene* pScene)
{
    KTime lTime;
    int lKeyIndex = 0;

    // Create curve
    KFbxAnimCurve* lAnimCurve = KFbxAnimCurve::Create(pScene, "Cube Animation");

    // Add keys to the curve
    lAnimCurve->KeyModifyBegin();

    // First key: time 0, value 0
    lTime.SetSecondDouble(0.0);
    lKeyIndex = lAnimCurve->KeyAdd(lTime);
    lAnimCurve->KeySet(lKeyIndex, lTime, 0.0, KFbxAnimCurveDef::eINTERPOLATION_LINEAR);

    // Second key: time 20s, value -3600
    // Since this curve will describe rotation, each cube will rotate 10 times around itself during 20 seconds.
    lTime.SetSecondDouble(20.0);
    lKeyIndex = lAnimCurve->KeyAdd(lTime);
    lAnimCurve->KeySet(lKeyIndex, lTime, -3600, KFbxAnimCurveDef::eINTERPOLATION_LINEAR);

    // Done adding keys.
    lAnimCurve->KeyModifyEnd();

    return lAnimCurve;
}