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

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

//
// The document created in this example is a container for objects.
// The document includes two sub-documents to contain materials and lights separately.
//
// The example illustrates how to:
//        1) create document and export
//        2) create objects that connect to document directly
//        3) create sub-document
//        4) create materials and textures, connect texture to material
//        5) create lights
//        6) export a document in a .FBX file (ASCII mode)
//

#include <fbxsdk.h>

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

#define SAMPLE_FILENAME "ExportDocument.fbx"

bool CreateDocument(KFbxSdkManager* pSdkManager, KFbxDocument* pDocument);
void CreateMatDocument(KFbxSdkManager* pSdkManager, KFbxDocument* pMatDocument);
void CreateLightDocument(KFbxSdkManager* pSdkManager, KFbxDocument* pLightDocument);
KFbxNode* CreatePlane(KFbxSdkManager* pSdkManager, char* pName);
KFbxSurfacePhong* CreateMaterial(KFbxSdkManager* pSdkManager);
KFbxTexture*  CreateTexture(KFbxSdkManager* pSdkManager);
KFbxNode* CreateLight(KFbxSdkManager* pSdkManager, KFbxLight::ELightType pType);


void InitializeSdkObjects(KFbxSdkManager*& pSdkManager)
{
    // 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)
    {
        printf("Unable to create the FBX SDK manager\n");
        exit(0);
    }

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

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();
    pSdkManager = NULL;
}

// Export document, the format is ascii by default
bool SaveDocument(KFbxSdkManager* pSdkManager, KFbxDocument* pDocument, const char* pFilename, int pFileFormat=-1, bool pEmbedMedia=false)
{
    int lMajor, lMinor, lRevision;
    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;
                    }
                }
            }
        }
    }

    // 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_ANIMATION,       true);
    IOS_REF.SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true);

    // Initialize the exporter by providing a filename.
    if(lExporter->Initialize(pFilename, pFileFormat, pSdkManager->GetIOSettings()) == false)
    {
        printf("Call to KFbxExporter::Initialize() failed.\n");
        printf("Error returned: %s\n\n", lExporter->GetLastErrorString());
        return false;
    }

    KFbxSdkManager::GetFileFormatVersion(lMajor, lMinor, lRevision);
    printf("FBX version number for this version of the FBX SDK is %d.%d.%d\n\n", lMajor, lMinor, lRevision);

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

    // Destroy the exporter.
    lExporter->Destroy();
    return lStatus;
}

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

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

    // create the main document
    lDocument = KFbxDocument::Create(lSdkManager,"RootDoc");

    // Create the scene.
    lResult = CreateDocument(lSdkManager, lDocument);

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

    // Save the document.

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

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

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

    return 0;
}

bool CreateDocument(KFbxSdkManager* pSdkManager, KFbxDocument* pDocument)
{
    int lCount;

    // create document info
    KFbxDocumentInfo* lDocInfo = KFbxDocumentInfo::Create(pSdkManager,"DocInfo");
    lDocInfo->mTitle = "Example document";
    lDocInfo->mSubject = "Illustrates the creation of KFbxDocument with geometries, materials and lights.";
    lDocInfo->mAuthor = "ExportDocument.exe sample program.";
    lDocInfo->mRevision = "rev. 1.0";
    lDocInfo->mKeywords = "Fbx document";
    lDocInfo->mComment = "no particular comments required.";

    // add the documentInfo
    pDocument->SetDocumentInfo(lDocInfo);

    // NOTE: Objects created directly in the SDK Manager are not visible
    // to the disk save routines unless they are manually connected to the
    // documents (see below). Ideally, one would directly use the KFbxScene/KFbxDocument
    // during the creation of objects so they are automatically connected and become visible
    // to the disk save routines.
    KFbxNode* lPlane = CreatePlane(pSdkManager, "Plane");
    
    // add the geometry to the main document.
    pDocument->AddRootMember(lPlane);
    lCount = pDocument->GetRootMemberCount();  // lCount = 1: only the lPlane
    lCount = pDocument->GetMemberCount();      // lCount = 3: the KFbxNode - lPlane; KFbxMesh belongs to lPlane; Material that connect to lPlane

    // Create sub document to contain materials.
    KFbxDocument* lMatDocument = KFbxDocument::Create(pSdkManager,"Material");

    CreateMatDocument(pSdkManager, lMatDocument);
    // Connect the light sub document to main document
    pDocument->AddMember(lMatDocument);    

    // Create sub document to contain lights
    KFbxDocument* lLightDocument = KFbxDocument::Create(pSdkManager,"Light");
    CreateLightDocument(pSdkManager, lLightDocument);
    // Connect the light sub document to main document
    pDocument->AddMember(lLightDocument);

    lCount = pDocument->GetMemberCount();       // lCount = 5 : 3 add two sub document

    // document can contain animation. Please refer to other sample about how to set animation
    pDocument->CreateAnimStack("PlanAnim");

    lCount = pDocument->GetRootMemberCount();  // lCount = 1: only the lPlane
    lCount = pDocument->GetMemberCount();      // lCount = 7: 5 add AnimStack and AnimLayer
    lCount = pDocument->GetMemberCount(FBX_TYPE(KFbxDocument));    // lCount = 2
    KFbxObject* lObj = pDocument->GetMember(FBX_TYPE(KFbxDocument), 1);  // lObj = lLightDocument, the second sub document

    return true;
}

// Create material sub document
void CreateMatDocument(KFbxSdkManager* pSdkManager, KFbxDocument* pMatDocument)
{
    // create document info
    KFbxDocumentInfo* lDocInfo = KFbxDocumentInfo::Create(pSdkManager,"DocInfo");
    lDocInfo->mTitle = "Sub document for materials";
    lDocInfo->mSubject = "Illustrates the creation of sub-KFbxDocument with materials.";
    lDocInfo->mAuthor = "ExportDocument.exe sample program.";
    lDocInfo->mRevision = "rev. 1.0";
    lDocInfo->mKeywords = "Fbx material document";
    lDocInfo->mComment = "no particular comments required.";

    // add the documentInfo
    pMatDocument->SetDocumentInfo(lDocInfo);

    // add material object to the sub document
    pMatDocument->AddMember(CreateMaterial(pSdkManager));
}

// Create light sub document
void CreateLightDocument(KFbxSdkManager* pSdkManager, KFbxDocument* pLightDocument)
{
    // create document info
    KFbxDocumentInfo* lDocInfo = KFbxDocumentInfo::Create(pSdkManager,"DocInfo");
    lDocInfo->mTitle = "Sub document for lights";
    lDocInfo->mSubject = "Illustrates the creation of sub-KFbxDocument with lights.";
    lDocInfo->mAuthor = "ExportDocument.exe sample program.";
    lDocInfo->mRevision = "rev. 1.0";
    lDocInfo->mKeywords = "Fbx light document";
    lDocInfo->mComment = "no particular comments required.";

    // add the documentInfo
    pLightDocument->SetDocumentInfo(lDocInfo);

    // add light objects to the sub document
    pLightDocument->AddMember(CreateLight(pSdkManager, KFbxLight::eSPOT));
    pLightDocument->AddMember(CreateLight(pSdkManager, KFbxLight::ePOINT));
}

// Create a plane mesh. 
KFbxNode* CreatePlane(KFbxSdkManager* pSdkManager, char* pName)
{
    int i;
    KFbxMesh* lMesh = KFbxMesh::Create(pSdkManager,pName);

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

    KFbxVector4 lNormalZPos(0, 0, 1);

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

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


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

    lElementNormal->SetMappingMode(KFbxGeometryElement::eBY_CONTROL_POINT);

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

    lElementNormal->GetDirectArray().Add(lNormalZPos);
    lElementNormal->GetDirectArray().Add(lNormalZPos);
    lElementNormal->GetDirectArray().Add(lNormalZPos);
    lElementNormal->GetDirectArray().Add(lNormalZPos);


    // Array of polygon vertices.
    int lPolygonVertices[] = { 0, 1, 2, 3 };

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

    // Create polygons. Assign texture and texture UV indices.
    // all faces of the cube have the same texture
    lMesh->BeginPolygon(-1, -1, -1, false);

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

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

    lMesh->EndPolygon ();

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

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

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

    // rotate the plane
    lNode->LclRotation.Set(KFbxVector4(90, 0, 0));


    // Set material mapping.
    KFbxGeometryElementMaterial* lMaterialElement = lMesh->CreateElementMaterial();
    lMaterialElement->SetMappingMode(KFbxGeometryElement::eBY_POLYGON);
    lMaterialElement->SetReferenceMode(KFbxGeometryElement::eINDEX_TO_DIRECT);
    if( !lMesh->GetElementMaterial( 0))
        return NULL;

    // add material to the node. 
    // the material can't in different document with the geometry node or in sub-document
    // we create a simple material here which belong to main document
    KString lMaterialName = "material_for_plane";
    KString lShadingName  = "Phong";
    KFbxSurfacePhong* lMaterial = KFbxSurfacePhong::Create(pSdkManager, lMaterialName.Buffer());

    lMaterial->Diffuse.Set(fbxDouble3(1.0, 1.0, 0));
    lMaterial->DiffuseFactor.Set(1.);

    lNode->AddMaterial(lMaterial);

    // We are in eBY_POLYGON, so there's only need for index (a plane has 1 polygon).
    lMaterialElement->GetIndexArray().SetCount(lMesh->GetPolygonCount());

    // Set the Index to the material
    for(int i=0; i<lMesh->GetPolygonCount(); ++i)
        lMaterialElement->GetIndexArray().SetAt(i,0);

    // return the KFbxNode
    return lNode;
}


// Create a texture
KFbxTexture*  CreateTexture(KFbxSdkManager* pSdkManager)
{
    KFbxFileTexture* lTexture = KFbxFileTexture::Create(pSdkManager,"");

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

    // Set texture properties.
    lTexture->SetFileName(lTexPath.Buffer());
    lTexture->SetName("Diffuse Texture");
    lTexture->SetTextureUse(KFbxTexture::eSTANDARD);
    lTexture->SetMappingType(KFbxTexture::eUV);
    lTexture->SetMaterialUse(KFbxFileTexture::eMODEL_MATERIAL);
    lTexture->SetSwapUV(false);
    lTexture->SetAlphaSource (KFbxTexture::eNONE);
    lTexture->SetTranslation(0.0, 0.0);
    lTexture->SetScale(1.0, 1.0);
    lTexture->SetRotation(0.0, 0.0);

    return lTexture;
}

// Create material.
// FBX scene must connect materials KFbxNode, otherwise materials will not be exported.
// FBX document don't need connect materials to KFbxNode, it can export standalone materials.
KFbxSurfacePhong* CreateMaterial(KFbxSdkManager* pSdkManager)
{
    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);
    KFbxSurfacePhong* lMaterial = KFbxSurfacePhong::Create(pSdkManager, 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       .ConnectSrcObject(CreateTexture(pSdkManager));
    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);

    return lMaterial;
}

// Create light.
KFbxNode* CreateLight(KFbxSdkManager* pSdkManager, KFbxLight::ELightType pType)
{
    KString lLightName;
    fbxDouble1 val;
    
    switch (pType)
    {
        case KFbxLight::eSPOT:
            lLightName = "SpotLight";
            break;
        case KFbxLight::ePOINT:
            lLightName = "PointLight";
            break;
        case KFbxLight::eDIRECTIONAL:
            lLightName = "DirectionalLight";
            break;
        default:
            break;
    }

    KFbxLight* lFbxLight = KFbxLight::Create(pSdkManager, lLightName.Buffer());

    lFbxLight->LightType.Set(pType);
    KFbxLight::ELightType lType = lFbxLight->LightType.Get();

    // parameters for spot light
    if (pType == KFbxLight::eSPOT)
    {
        lFbxLight->HotSpot.Set(40.0);
        val = lFbxLight->HotSpot.Get(); // val = 40

        lFbxLight->ConeAngle.Set(40);
        val = lFbxLight->ConeAngle.Get(); // val = 40
    }
    
    //
    // Light Color...
    //
    fbxDouble3 lColor;
    lColor[0] = 0.0;
    lColor[1] = 1.0;
    lColor[2] = 0.5;
    lFbxLight->Color.Set(lColor);
    fbxDouble3 val3 = lFbxLight->Color.Get(); // val3 = (0, 1, 0.5) 

    //
    // Light Intensity...
    //
    lFbxLight->Intensity.Set(100.0);
    val = lFbxLight->Intensity.Get(); // val = 100

    // create a KFbxNode
    KFbxNode* lNode = KFbxNode::Create(pSdkManager,lLightName+"Node");

    // set the node attribute
    lNode->SetNodeAttribute(lFbxLight);
    lNode->LclTranslation.Set(fbxDouble3(20, 30, 100));
    val3 = lNode->LclTranslation.Get(); // val3 = (20, 30, 100) 

    return lNode;
}