Layers/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 cube with Layered textures and
// with materials mapped on it's faces. 
//
// This sample illustrates how to use the following Elements:
//      - Normal
//      - Material
//      - UVs
//      - Vertex Color
//      - Polygon Group
//

#include <fbxsdk.h>

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

#define SAMPLE_FILENAME "Layers.fbx"
#define BACKGROUND_IMAGE_NAME "Spotty"
#define BACKGROUND_IMAGE      "spotty.jpg"
#define GEO1_IMAGE_NAME     "One"
#define GEO1_IMAGE          "1.jpg"
#define GEO2_IMAGE_NAME     "Waffle"
#define GEO2_IMAGE          "waffle.jpg"

typedef double Vector4[4];
typedef double Vector2[2];

// Function prototypes.
KFbxNode* CreateCube(KFbxScene* pScene, char* pName);
KFbxTexture* CreateTexture(KFbxScene* pScene, char* name, char* filename);

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

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

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

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


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


// Create a cube.
KFbxNode* CreateCube(KFbxScene* pScene, char* pName)
{
    // indices of the vertices per each polygon
    static int vtxId[24] = {
        0,1,2,3, // front  face  (Z+)
        1,5,6,2, // right  side  (X+)
        5,4,7,6, // back   face  (Z-)
        4,0,3,7, // left   side  (X-)
        0,4,5,1, // bottom face  (Y-)
        3,2,6,7  // top    face  (Y+)
    };

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

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

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

    static Vector2 lUVs1[14] = {
        { 0.0, 1.0}, 
        { 1.0, 0.0}, 
        { 0.0, 0.0},
        { 1.0, 1.0}
    };

    // indices of the uvs per each polygon
    static int uvsId[24] = {
        0,1,3,2,2,3,5,4,4,5,7,6,6,7,9,8,1,10,11,3,12,0,2,13
    };

    // colors
    static Vector4 lColors[8] = {
        // colors used for the materials
        {1.0, 1.0, 1.0, 1.0},
        {1.0, 1.0, 0.0, 1.0},
        {1.0, 0.0, 1.0, 1.0},
        {0.0, 1.0, 1.0, 1.0},
        {0.0, 0.0, 1.0, 1.0},
        {1.0, 0.0, 0.0, 1.0},
        {0.0, 1.0, 0.0, 1.0},
        {0.0, 0.0, 0.0, 1.0},
    };

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

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

    // create the materials.
    /* Each polygon face will be assigned a unique material.
    */
    KFbxLayerContainer* lLayerContainer = (KFbxLayerContainer*)lMesh;
    KFbxGeometryElementMaterial* lMaterialElement = lMesh->CreateElementMaterial();
    lMaterialElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON);
    lMaterialElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

    // Create polygons later after KFbxGeometryElementMaterial is created. Assign material indices.
    int vId = 0;
    for (int f=0; f<6; f++)
    {
        lMesh->BeginPolygon(f);//Material index.
        for (int v=0; v<4; v++)
            lMesh->AddPolygon(vtxId[vId++]);
        lMesh->EndPolygon();
    }

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

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


    // create color vertices
    /* We choose to define one color per control point. The other choice would
    have been to use the eBY_POLYGON_VERTEX mapping mode. In this second case,
    the reference mode should become eINDEX_TO_DIRECT.
    */
    KFbxGeometryElementVertexColor* lVertexColorElement = lMesh->CreateElementVertexColor();
    lVertexColorElement->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);
    lVertexColorElement->SetReferenceMode(KFbxGeometryElement::eDIRECT);

    for (int c=0; c<8; c++)
        lVertexColorElement->GetDirectArray().Add(KFbxColor(lColors[c][0]*.8, lColors[c][1]*0.8, lColors[c][2]*.8, lColors[c][3]*.8));



    // create polygroups. 
    /* We are going to make a first group with the 4 sides.
    And a second group with the top and bottom sides.

    NOTE that the only reference mode allowed is eINDEX
    */ 
    KFbxGeometryElementPolygonGroup* lPolygonGroupElement = lMesh->CreateElementPolygonGroup();
    lPolygonGroupElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON);
    lPolygonGroupElement->SetReferenceMode(KFbxGeometryElement::eINDEX);
    lPolygonGroupElement->GetIndexArray().Add(0); // front face assigned to group 0
    lPolygonGroupElement->GetIndexArray().Add(0); // right side assigned to group 0
    lPolygonGroupElement->GetIndexArray().Add(0); // back face assigned to group 0
    lPolygonGroupElement->GetIndexArray().Add(0); // left side assigned to group 0
    lPolygonGroupElement->GetIndexArray().Add(1); // bottom face assigned to group 1
    lPolygonGroupElement->GetIndexArray().Add(1); // top face assigned to group 1


    // create the UV textures mapping.
    KFbxTexture* lTexture[3];
    KFbxLayeredTexture::EBlendMode lBlendMode[3];

    // On layer 0 all the faces have the same texture
    KFbxGeometryElementUV* lUVElement0 = lMesh->CreateElementUV( BACKGROUND_IMAGE_NAME);
    K_ASSERT( lUVElement0 != NULL);
    lUVElement0->SetMappingMode(KFbxGeometryElement::eBY_POLYGON_VERTEX);
    lUVElement0->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);

    int i;
    for (i = 0; i<14; i++)
        lUVElement0->GetDirectArray().Add(KFbxVector2(lUVs0[i][0], lUVs0[i][1]));

    for (i = 0; i<24; i++)
        lUVElement0->GetIndexArray().Add(uvsId[i]);

    // Create the node containing the mesh
    KFbxNode* lNode = KFbxNode::Create(pScene,pName);

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

    // Put a different material on each polygon
    KFbxSurfacePhong* lMaterial[6];
    for (i = 0; i < 6; i++ )
    {
        KString lMaterialName = "material";
        lMaterialName += i;

        lMaterial[i] = KFbxSurfacePhong::Create(pScene,lMaterialName.Buffer());

        // Generate primary and secondary colors.
        lMaterial[i]->Emissive.Set(fbxDouble3(0.0, 0.0, 0.0));
        lMaterial[i]->Ambient.Set(fbxDouble3(lColors[i][0], lColors[i][1], lColors[i][2]));
        lMaterial[i]->Diffuse.Set(fbxDouble3(1.0, 1.0, 1.0));
        lMaterial[i]->Specular.Set(fbxDouble3(0.0, 0.0, 0.0));
        lMaterial[i]->TransparencyFactor.Set(0.0);
        lMaterial[i]->Shininess.Set(0.5);
        lMaterial[i]->ShadingModel.Set(KString("phong"));

        // add materials to the node
        lNode->AddMaterial(lMaterial[i]);
    }

    // Create textures and texture mappings.
    lTexture[0] = CreateTexture(pScene, BACKGROUND_IMAGE_NAME, BACKGROUND_IMAGE);
    
    lBlendMode[0] = KFbxLayeredTexture::eTRANSLUCENT;
    
    // create second UVset
    KFbxGeometryElementUV* lUVElement1 = lMesh->CreateElementUV( GEO1_IMAGE_NAME);
    K_ASSERT( lUVElement1 != NULL);
    lUVElement1->SetMappingMode(KFbxGeometryElement::eBY_POLYGON_VERTEX);
    lUVElement1->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);
    for (i = 0; i <4; i++)
        lUVElement1->GetDirectArray().Add(KFbxVector2(lUVs1[i][0], lUVs1[i][1]));

    for (i = 0; i<24; i++)
        lUVElement1->GetIndexArray().Add(uvsId[i%4]);

    lTexture[1] = CreateTexture(pScene, GEO1_IMAGE_NAME, GEO1_IMAGE);
    lBlendMode[1] = KFbxLayeredTexture::eMODULATE;
    


    KFbxGeometryElementUV* lUVElement2 = lMesh->CreateElementUV( GEO2_IMAGE_NAME);
    K_ASSERT( lUVElement2 != NULL);
    // we re-use the UV mapping.
    *lUVElement2 = *lUVElement0;  

    lTexture[2] = CreateTexture(pScene, GEO2_IMAGE_NAME, GEO2_IMAGE);
    
    lBlendMode[2] = KFbxLayeredTexture::eMODULATE;

    // Because we can only connect one texture to the material propery, we need
    // to use a layered texture object to connect the multiple textures created above
    // we know that 5 faces of the cube will use 2 textures (lTexture[0] and lTexture[1])
    // and only one face uses the three textures. Therefore we need two layered textures.

    KFbxLayeredTexture* lLayeredTexture[2];

    // the lLayeredTexure[1] is used for the 5 faces with two textures and lLayeredTexture[0] for
    // the only face that uses the three textures
    lLayeredTexture[0] = KFbxLayeredTexture::Create(pScene, "layeredTexture0");
    lLayeredTexture[1] = KFbxLayeredTexture::Create(pScene, "layeredTexture1");

    // the first connected texture is the bottom one!
    for (i = 0; i < 3; i++)
    {
        lLayeredTexture[0]->ConnectSrcObject(lTexture[i]);
        lLayeredTexture[0]->SetTextureBlendMode(i, lBlendMode[i]);
        if (i < 2)
        {
            lLayeredTexture[1]->ConnectSrcObject(lTexture[i]);
            lLayeredTexture[1]->SetTextureBlendMode(i, lBlendMode[i]);
        }
    }

    // connect the layered textures to the 6 materials allocated before (material[0] is the
    // material connected to the face 0 of the cube so it is the one that will have lLayeredTexture[0].
    lMaterial[0]->Diffuse.ConnectSrcObject(lLayeredTexture[0]);
    for (i = 1; i < 6; i++)
        lMaterial[i]->Diffuse.ConnectSrcObject(lLayeredTexture[1]);    

    return lNode;
}


KFbxTexture* CreateTexture(KFbxScene* pScene, char* name, char* filename)
{
    KFbxFileTexture* lTexture = KFbxFileTexture::Create(pScene,name);
    lTexture->SetFileName(filename); // 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);    
    return lTexture;
}