ExportScene05/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 skeleton made of 3 segments. 
// The position of a node in a .FBX file is expressed in coordinates 
// relative to it's parent. This example shows how to convert to and 
// from a global position. 
//
// The example illustrates how to:
//        1) use a custom memory allocator
//        2) create a skeleton segment
//        3) get a node's global default position
//        4) set a node's global default position
//        5) set limits, rotation order and pre/post pivots
//        6) export a scene in a .FBX file
//

#include <fbxsdk.h>
#ifdef KARCH_ENV_WIN
// On Windows platform need to include this to define  _msize()
#include <malloc.h>
#endif

//include needed for custom reader/writer
#include "../MyOwnWriterReader/MyOwnWriterReader.h"

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

#define SAMPLE_FILENAME "ExportScene05.fbx"


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

void SetGlobalDefaultPosition(KFbxNode* pNode, KFbxXMatrix pGlobalPosition);
KFbxXMatrix GetGlobalDefaultPosition(KFbxNode* pNode);

// MyMemoryAllocator : a custom memory allocator 
// to be used by the FBX SDK
class MyMemoryAllocator : public KFbxMemoryAllocator
{
public:
    MyMemoryAllocator() 
        : KFbxMemoryAllocator(MyMalloc, MyCalloc, MyRealloc, MyFree, MyMsize)
    {
    }

    ~MyMemoryAllocator()  
    {
    }

    static void* MyMalloc(size_t pSize)       
    {
        return malloc(pSize);
    }

    static void* MyCalloc(size_t pCount,size_t pSize)
    {
        return calloc(pCount, pSize);
    }

    static void* MyRealloc(void* pData, size_t pSize)
    {
        return realloc(pData, pSize);
    }

    static void  MyFree(void* pData)
    {
        free(pData);
    }

    static size_t MyMsize(void* pData)
    {
        return _msize(pData);
    }
};

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

    // Specify a custom memory allocator to be used by the FBX SDK
    KFbxSdkManager::SetMemoryAllocator(&lMyMemoryAllocator);

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

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

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

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


    //At this point, we have an FBX file.  Let's try to write to a custom writer and read our file with our custom reader.

    printf("Writing to file with custom writer\n");
    int lRegisteredCount;
    int lPluginId;

    //We need to register the writer for the sdk to be aware.
    lSdkManager->GetIOPluginRegistry()->RegisterWriter(CreateMyOwnWriter, GetMyOwnWriterInfo,
        lPluginId, lRegisteredCount, FillOwnWriterIOSettings);


    //The filename
    const char* lFileName = "CustomWriter.ABC";

    //at this point use our custom writer to write:
    KFbxExporter* lExporter = KFbxExporter::Create(lSdkManager, "");

    //Here, we set the custom writer.
    int lFileFormat = lSdkManager->GetIOPluginRegistry()->FindWriterIDByExtension("ABC");
    
    //Initialize the file
    if(lExporter->Initialize(lFileName, lFileFormat, lSdkManager->GetIOSettings()) == false)
    {
        printf("Call to KFbxExporter::Initialize() failed.\n");
        printf("Error returned: %s\n\n", lExporter->GetLastErrorString());
        // Destroy all objects created by the FBX SDK.
        DestroySdkObjects(lSdkManager);
        return 1;
    }

    //This will call the write function of the custom writer.
    lResult = lExporter->Export(lScene); 
    if(lResult == false)
    {
        printf("Error in write of our custom writer\n");
        // Destroy all objects created by the FBX SDK.
        DestroySdkObjects(lSdkManager);
        return 1;
    }


    //At this point, we have written to the custom writer, let's read with our custom reader.

    //Again, we need to register the reader.
    lSdkManager->GetIOPluginRegistry()->RegisterReader(CreateMyOwnReader, GetMyOwnReaderInfo,
        lPluginId, lRegisteredCount, FillOwnReaderIOSettings);

    //Create the importer
    KFbxImporter* lImporter = KFbxImporter::Create(lSdkManager,"");


    //We initialize our file.
    //Here we can simply pass the filename, and it should automatically find the right file format and reader.
    lResult = lImporter->Initialize(lFileName, -1, lSdkManager->GetIOSettings() );

    //At one point, this will call our read function of our custom reader.
    lResult = lImporter->Import(lScene);

    if(lResult == false)
    {
        printf("There was a problem in the read of our custom reader\n");
        // Destroy all objects created by the FBX SDK.
        DestroySdkObjects(lSdkManager);
        return 1;
    }

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

    return 0;
}

bool CreateScene(KFbxScene* pScene)
{
    KFbxVector4 lT, lR, lS;
    KFbxXMatrix lGM;

    // Create nodes.
    KFbxNode* pNodeA = KFbxNode::Create(pScene,"A");
    KFbxNode* pNodeB = KFbxNode::Create(pScene,"B");
    KFbxNode* pNodeC = KFbxNode::Create(pScene,"C");
    KFbxNode* pNodeD = KFbxNode::Create(pScene,"D");

    // Create node attributes.
    KFbxSkeleton* lSkeletonA = KFbxSkeleton::Create(pScene,"");
    lSkeletonA->SetSkeletonType(KFbxSkeleton::eROOT);
    pNodeA->SetNodeAttribute(lSkeletonA);
    KFbxSkeleton* lSkeletonB = KFbxSkeleton::Create(pScene,"");
    lSkeletonB->SetSkeletonType(KFbxSkeleton::eLIMB_NODE);
    pNodeB->SetNodeAttribute(lSkeletonB);
    KFbxSkeleton* lSkeletonC = KFbxSkeleton::Create(pScene,"");
    lSkeletonC->SetSkeletonType(KFbxSkeleton::eLIMB_NODE);
    pNodeC->SetNodeAttribute(lSkeletonC);
    KFbxSkeleton* lSkeletonD = KFbxSkeleton::Create(pScene,"");
    lSkeletonD->SetSkeletonType(KFbxSkeleton::eLIMB_NODE);
    pNodeD->SetNodeAttribute(lSkeletonD);


    // On node A we set translation limits
    pNodeA->GetLimits().SetTranslationLimitActive(true);
    pNodeA->GetLimits().mTranslationLimits.SetLimitMinActive(true, true, true);
    pNodeA->GetLimits().mTranslationLimits.SetLimitMin(KFbxVector4(0.1, 0.2, 0.3));
    pNodeA->GetLimits().mTranslationLimits.SetLimitMaxActive(true, true, true);
    pNodeA->GetLimits().mTranslationLimits.SetLimitMax(KFbxVector4(5.0, 1.0, 0.0));

    // On node B we set the rotation order and the pre/post pivots
    // (for these value to have an effect, we need to enable the RotationActive flag)
    pNodeB->SetRotationActive(true);
    // alternatively, if we need to set rotation limits, we can activate the
    // RotationActive flag using the following call: 
    // pNodeB->GetLimits().SetRotationLimitActive(true);
    pNodeB->GetLimits().mRotationLimits.SetLimitMaxActive(true, false, false);
    pNodeB->GetLimits().mRotationLimits.SetLimitMax(KFbxVector4(33.3, 0.0, 0.0));

    pNodeB->SetPivotState(KFbxNode::eSOURCE_SET, KFbxNode::ePIVOT_STATE_ACTIVE);
    pNodeB->SetRotationOrder(KFbxNode::eSOURCE_SET, eSPHERIC_XYZ);
    pNodeB->SetUseRotationSpaceForLimitOnly(KFbxNode::eSOURCE_SET, false);
    pNodeB->SetQuaternionInterpolation(KFbxNode::eSOURCE_SET, eQUATINTERP_CLASSIC);
    pNodeB->SetRotationPivot(KFbxNode::eSOURCE_SET, KFbxVector4(11.1, 22.2, 33.3));
    pNodeB->SetPreRotation(KFbxNode::eSOURCE_SET, KFbxVector4(15.0, 30.0, 45.0));
    pNodeB->SetPostRotation(KFbxNode::eSOURCE_SET, KFbxVector4(-45.0, -30.0, -15.0));

    // Set node hierarchy.
    pScene->GetRootNode()->AddChild(pNodeA);
    pNodeA->AddChild(pNodeB);
    pNodeB->AddChild(pNodeC);
    pNodeC->AddChild(pNodeD);

    // Set global position of node A.
    lT.Set(0.0, 0.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, 45.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeA, lGM);

    // Set global position of node B.
    lT.Set(30.0, 20.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, 0.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeB, lGM);

    // Set global position of node C.
    lT.Set(55.0, 20.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, -40.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeC, lGM);

    // Set global position of node D.
    lT.Set(70.0, 10.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, 0.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeD, lGM);

    // Set meta-data on some of the nodes.
    //
    // For this sample, we'll use a hiearchical set of meta-data:
    // 
    // Family
    //      Type
    //          Instance
    // 
    // Family contains all the common properties, and the lower levels override various
    // values.
    //
    KFbxObjectMetaData* pFamilyMetaData = KFbxObjectMetaData::Create(pScene, "Family");
    KFbxProperty::Create(pFamilyMetaData, DTString, "Level", "Level").Set(KString("Family"));
    KFbxProperty::Create(pFamilyMetaData, DTString, "Type", "Type").Set(KString("Wall"));
    KFbxProperty::Create(pFamilyMetaData, DTFloat, "Width", "Width").Set(10.0f);
    KFbxProperty::Create(pFamilyMetaData, DTDouble, "Weight", "Weight").Set(25.0);
    KFbxProperty::Create(pFamilyMetaData, DTDouble, "Cost", "Cost").Set(1.25);

    KFbxObjectMetaData* pTypeMetaData = KFbxCast<KFbxObjectMetaData>(pFamilyMetaData->Clone(KFbxObject::eREFERENCE_CLONE, pScene));

    pTypeMetaData->SetName("Type");

    // On this level we'll just override two properties
    pTypeMetaData->FindProperty("Cost").Set(2500.0);
    pTypeMetaData->FindProperty("Level").Set(KString("Type"));

    KFbxObjectMetaData* pInstanceMetaData = KFbxCast<KFbxObjectMetaData>(pTypeMetaData->Clone(KFbxObject::eREFERENCE_CLONE, pScene));

    pInstanceMetaData->SetName("Instance");

    // And on this level, we'll go in and add a brand new property, too.
    KFbxProperty::Create(pInstanceMetaData, DTString, "Sku", "Sku#").Set(KString("143914-10"));
    pInstanceMetaData->FindProperty("Width").Set(1100.50f);
    pInstanceMetaData->FindProperty("Type").Set(KString("Super Heavy Duty Wall"));
    pInstanceMetaData->FindProperty("Level").Set(KString("Instance"));

    // Finally connect metadata information to some of our nodes.
    pNodeA->ConnectSrcObject(pInstanceMetaData);
    pNodeC->ConnectSrcObject(pInstanceMetaData);    // Share the same object

    pNodeD->ConnectSrcObject(pTypeMetaData);

    return true;
}

// Function to get a node's global default position.
// As a prerequisite, parent node's default local position must be already set.
void SetGlobalDefaultPosition(KFbxNode* pNode, KFbxXMatrix pGlobalPosition)
{
    KFbxXMatrix lLocalPosition;
    KFbxXMatrix lParentGlobalPosition;

    if (pNode->GetParent())
    {
        lParentGlobalPosition = GetGlobalDefaultPosition(pNode->GetParent());
        lLocalPosition = lParentGlobalPosition.Inverse() * pGlobalPosition;
    }
    else
    {
        lLocalPosition = pGlobalPosition;
    }

    pNode->LclTranslation.Set(lLocalPosition.GetT());
    pNode->LclRotation.Set(lLocalPosition.GetR());
    pNode->LclScaling.Set(lLocalPosition.GetS());
}

// Recursive function to get a node's global default position.
// As a prerequisite, parent node's default local position must be already set.
KFbxXMatrix GetGlobalDefaultPosition(KFbxNode* pNode)
{
    KFbxXMatrix lLocalPosition;
    KFbxXMatrix lGlobalPosition;
    KFbxXMatrix lParentGlobalPosition;

    lLocalPosition.SetT(pNode->LclTranslation.Get());
    lLocalPosition.SetR(pNode->LclRotation.Get());
    lLocalPosition.SetS(pNode->LclScaling.Get());

    if (pNode->GetParent())
    {
        lParentGlobalPosition = GetGlobalDefaultPosition(pNode->GetParent());
        lGlobalPosition = lParentGlobalPosition * lLocalPosition;
    }
    else
    {
        lGlobalPosition = lLocalPosition;
    }

    return lGlobalPosition;
}