ProceduralTexture/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 scene created in this example is a plane with a procedural texture applied.
//
// The example illustrates how to:
//        1) Create a procedural texture
//        2) Set the blob property of a procedural texture
//        3) Get the blob property of a procedural texture and dump it on disk
//

#include <fbxsdk.h>
#include <fstream>
#include "../Common/Common.h"

#define SAMPLE_FILENAME "ProceduralTexture.fbx"
#define TEXTURE_FILENAME "a_texture.jpg"
#define FILENAME_PROP "Filename"

// Function prototypes.
bool CreateSceneAndSaveFile(int argc, char** argv);
bool ReadFileAndDumpProceduralTextureBlobOnDisk(int argc, char** argv);
bool DumpProceduralTextureBlobOnDisk(KFbxScene* pScene);
bool CreateScene(KFbxScene* pScene);
KFbxNode* CreatePlane(KFbxScene* pScene, char* pName);
KFbxSurfacePhong* CreatePhongMaterial(KFbxScene* pScene, char* pName);
KFbxProceduralTexture* CreateProceduralTexture(KFbxScene* pScene, char* pName);
void MapPhong(KFbxSurfacePhong* pPhong, KFbxNode* pNode);
void MapProceduralTexure(KFbxProceduralTexture* pProcTex, KFbxNode* pNode);

int main(int argc, char** argv)
{
    bool lResult;
    
    lResult = CreateSceneAndSaveFile(argc, argv);
    if(lResult == false)
    {
        return 1;
    }

    lResult = ReadFileAndDumpProceduralTextureBlobOnDisk(argc, argv);
    if(lResult == false)
    {
        return 1;
    }

    return 0;
}

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

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

    // Save the scene.
    printf("Saving the file...\n");
    // 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 false;
    }

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

    return true;
}

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

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

    printf("Reading the FBX file...\n");
    // The example can take an input file name as an argument.
    if(argc > 1)
    {
        lResult = LoadScene(lSdkManager, lScene, argv[1]);
    }
    // A default input file name is given otherwise.
    else
    {
        lResult = LoadScene(lSdkManager, lScene, SAMPLE_FILENAME);
    }

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

    // Create a new file for each procedural texture found in the file.
    lResult = DumpProceduralTextureBlobOnDisk(lScene);
    if(lResult == false)
    {
        printf("\n\nAn error occurred while dumping procedural texture blobs on disk...\n");
        DestroySdkObjects(lSdkManager);
        return false;
    }

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

    return true;
}

bool DumpProceduralTextureBlobOnDisk(KFbxScene* pScene)
{
    printf("Writing the blob on disk...\n");
    // Collect all the procedural textures proxy objects in scene
    int lNbProcTex = KFbxGetSrcCount<KFbxProceduralTexture>(pScene);

    if (!lNbProcTex)
    {
        return true;
    }

    bool lWroteBlob = false; // Bool: Wrote at least one blob on disk

    // Directory for blob extraction
    KString lDirPath = KFbxGetCurrentDirectory() + "/Blobs/";

    for(int lIndex = 0; lIndex < lNbProcTex; lIndex++)
    {
        KFbxProceduralTexture* lProcTex = KFbxGetSrc<KFbxProceduralTexture>(pScene, lIndex);
        if(!lProcTex)
        {
            continue;
        }

        KFbxProperty lFilenameProp = lProcTex->GetRootProperty().Find(FILENAME_PROP);
        if (!lFilenameProp.IsValid())
        {
            continue;
        }

        // Read binary blob
        void* lBlobBegin = NULL;
        size_t lBlobSize = 0;
        KFbxBlob lBinaryBlob = lProcTex->GetBlob();
        lBlobSize = lBinaryBlob.Size();
        lBlobBegin = const_cast<void*>(lBinaryBlob.Access());
        
        // Get file name to dump the blob to.
        KString lFilename;
        KFbxGet(lFilenameProp, lFilename);
        KString lFilePath = lDirPath + KFbxExtractFileName(lFilename, false) + lIndex + "." + KFbxExtractFileExtension(lFilename);

        bool lIsWritable = KFbxEnsureDirectoryExistance(lFilePath);
        if (lIsWritable)
        {
            std::ofstream lDataStreamOut(lFilePath.Buffer(), std::ofstream::binary);
            lDataStreamOut.write((const char *)lBlobBegin, lBlobSize);
            lDataStreamOut.close();
            // So now we wrote the file!
            lWroteBlob = true;
            printf("Blob is written on disk! File: %s\n", lFilePath.Buffer());
        }
    }

    return lWroteBlob;
}

bool CreateScene(KFbxScene* pScene)
{
    printf("Creating the scene...\n");
    KFbxNode* lPlane = CreatePlane(pScene, "Plane");

    KFbxSurfacePhong* lPhong = CreatePhongMaterial(pScene, "Phong");
    MapPhong(lPhong, lPlane);
    KFbxProceduralTexture* lProcTex = CreateProceduralTexture(pScene, "ProcTex");
    MapProceduralTexure(lProcTex, lPlane);

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

    return true;
}

// Create a plane. 
KFbxNode* CreatePlane(KFbxScene* pScene, char* pName)
{
    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 lNormal(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;

    // Set the normals.
    // 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 actual normal value for all 4 control points.
    lGeometryElementNormal->SetReferenceMode(KFbxGeometryElement::eDIRECT);
    lGeometryElementNormal->GetDirectArray().Add(lNormal);
    lGeometryElementNormal->GetDirectArray().Add(lNormal);
    lGeometryElementNormal->GetDirectArray().Add(lNormal);
    lGeometryElementNormal->GetDirectArray().Add(lNormal);

    // 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 polygon
    lMesh->BeginPolygon(-1, -1, false);
    for (int j = 0; j < 4; j++)
    {
        //this function points 
        lMesh->AddPolygon(lPolygonVertices[j]);
        //Now we have to update the index array of the UVs for diffuse
        lUVDiffuseElement->GetIndexArray().SetAt(j, j);
    }
    lMesh->EndPolygon();

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

    lNode->SetNodeAttribute(lMesh);

    return lNode;
}

// Create a procedural texture.
KFbxProceduralTexture* CreateProceduralTexture(KFbxScene* pScene, char* pName)
{
    KFbxProceduralTexture* lProceduralTexture = KFbxProceduralTexture::Create(pScene, pName);

    // For this example we simply dump the binary content of a jpg to the 
    // procedural texture's blob property.
    // In the general case, dump whatever information needed for the procedural
    // texture to the blob property.
    
    KString lFilename = TEXTURE_FILENAME;

    //check whether the file is readable
    bool lIsReadable = KFbxFileExist(lFilename);

    if (lIsReadable)
    {
        // create binary blob from the texture file
        KFbxBlob lBinaryBlob;
        std::ifstream lDataStreamIn(lFilename, std::ifstream::binary);
        char* lBlobBegin = (char *)FbxSdkMalloc(4096);
        char* lBlobEnd = lBlobBegin;
        long long lBlobSize = 0;
        long long lBlobPointerSize = 4096;
        std::streamsize lNbRead = 0;
        while(!lDataStreamIn.eof())
        {
            lBlobEnd = lBlobBegin + lBlobSize;
            lDataStreamIn.read(lBlobEnd, 4096);
            lNbRead = lDataStreamIn.gcount();
            lBlobPointerSize += 4096;
            lBlobBegin = (char *)FbxSdkRealloc(lBlobBegin, size_t(lBlobPointerSize));
            lBlobSize += lNbRead;
        }
        lDataStreamIn.close();
        lBinaryBlob.Assign(lBlobBegin, (int)lBlobSize);
        FbxSdkFree(lBlobBegin);

        lProceduralTexture->SetBlob(lBinaryBlob); 
    }

    // Add a property to retain file name
    KFbxProperty lFilenameProp = KFbxProperty::Create( lProceduralTexture->GetRootProperty(), DTString, FILENAME_PROP);
    if (lFilenameProp.IsValid())
    {
        KFbxSet<fbxString>(lFilenameProp, TEXTURE_FILENAME);
    }

    return lProceduralTexture;
}


KFbxSurfacePhong* CreatePhongMaterial(KFbxScene* pScene, char* pName)
{
    KFbxSurfacePhong* lPhong = KFbxSurfacePhong::Create(pScene, pName);
    
    return lPhong;
}

// Map procedural texture over plane.
void MapProceduralTexure(KFbxProceduralTexture* pProceduralTexture, KFbxNode* pNode)
{
    // The note shading mode has to be set to KFbxNode::eTEXTURE_SHADING for the texture to be displayed.
    pNode->SetShadingMode(KFbxNode::eTEXTURE_SHADING);

    // we have to connect the texture to the material DiffuseColor property
    KFbxSurfacePhong* lMaterial = pNode->GetSrcObject<KFbxSurfacePhong>(0);
    if (lMaterial)
    {
        lMaterial->Diffuse.ConnectSrcObject(pProceduralTexture);
    }

}

// Map material over mesh.
void MapPhong(KFbxSurfacePhong* pPhong, KFbxNode* pNode)
{
    // Create MaterialElement in the mesh
    KFbxMesh*                       lMesh                   = pNode->GetMesh();
    KFbxGeometryElementMaterial*    lGeometryElementMaterial    = lMesh->CreateElementMaterial();

    // The material is mapped to the whole mesh
    lGeometryElementMaterial->SetMappingMode(KFbxGeometryElement::eALL_SAME);

    // And the material is avalible in the Direct array
    lGeometryElementMaterial->SetReferenceMode(KFbxGeometryElement::eDIRECT);
    pNode->AddMaterial(pPhong);
}