Merging Two Scenes
 
 
 

This topic presents how to merge two imported scenes. It also provides basic insight on manipulating a scene and the nodes within it, and will initiate the reader to the concept of connections and connection management.

The steps taken for merging two scenes are as follows:

  1. Load a scene.
  2. Modify a node within the loaded scene.
  3. Move the contents of the loaded scene into a reference scene.
  4. Load a different scene.
  5. Update the newly loaded scene with the contents of the reference scene.
  6. Destroy the reference scene.

Loading and modifying the scene

The first object we create is the KFbxSdkManager; this step is required to create subsequent FBX SDK objects. A newly created KFbxScene object, referred to as the "current scene", is then passed to our custom LoadScene() function, so it can be populated by the imported contents of "file1.fbx" (which should be in the same folder as the executable program). Once the scene's contents are populated, we simply change the name of the root node's first child (indexed at 0) to "Test Name".

    // Create an SDK manager.
    KFbxSdkManager* lSdkManager = KFbxSdkManager::Create();

    // Create a new scene so it can be populated by the imported file.
    KFbxScene* lCurrentScene = KFbxScene::Create(lSdkManager,"My Scene");

    // Load the scene.
    LoadScene(lSdkManager, lCurrentScene, "file1.fbx");

    // Modify the scene. In this example, only one node name is changed.
    lCurrentScene->GetRootNode()->GetChild(0)->SetName("Test Name");

Moving the contents of the loaded scene into a reference scene

Another KFbxScene object is created, which we will refer to as the "reference scene". Note that when a KFbxScene object is created, its root node and its global settings are also created with it. As such, the objective of this step is to remove the children from the current scene's root node so they may become the children of the reference scene's root node.

    // Create a reference scene to store the contents of the currently loaded scene.
    KFbxScene *lMyRefScene = KFbxScene::Create(lSdkManager, "My Reference Scene");

    // Move the node tree of the currently loaded scene into the reference scene.
    int lNumChildren = lCurrentScene->GetRootNode()->GetChildCount();
    for(int i = 0; i < lNumChildren; i++) {

        // Obtain a child node from the currently loaded scene.
        KFbxNode* lChildNode = lCurrentScene->GetRootNode()->GetChild(i);

        // Attach the child node to the reference scene's root node.
        lMyRefScene->GetRootNode()->AddChild(lChildNode);
    }

    // Remove the children from the root node.
    lCurrentScene->GetRootNode()->DisconnectAllSrcObject();

Other KFbxObjects contained in the current scene may need to be moved to the reference scene. Such objects may include instances of: KFbxCharacter, KFbxCharacterPose, and KFbxDocumentInfo to name a few. Observe that these objects are not children of the current scene's root node, and need to be moved differently. To achieve this, we will require some basic knowledge of "connections" in the context of the FBX SDK.

A connection is a directional "source to destination" relationship between either: two KFbxObjects, a KFbxObject and a KFbxProperty, or two KFbxPropertys. For more information on connections, see Connections. In the scope of this tutorial, we want to disconnect all the relevant source objects from the current scene, and connect them the reference scene. We do this using the KFbxObject::GetSrcObjectCount(), KFbxObject::DisconnectAllSrcObject(), and KFbxObject::ConnectDstObject().

    // Move other objects to the reference scene.
    int lNumSceneObjects = lCurrentScene->GetSrcObjectCount();
    for(int i = 0; i < lNumSceneObjects; i++) {
        KFbxObject* lObj = lCurrentScene->GetSrcObject(i);
        if(lObj == lCurrentScene->GetRootNode() || *lObj == lCurrentScene->GetGlobalSettings()){
            // Don't move the root node or the scene's global settings; these
            // objects are created for every scene.
            continue;
        }
        // Attach the object to the reference scene.
        lObj->ConnectDstObject(lMyRefScene);
    }

    // Disconnect all scene objects.
    lCurrentScene->DisconnectAllSrcObject();
NoteYou can get all the objects of a certain type in a scene using the concept of connections. In the following code snippet, we iterate through the meshes connected to a scene by calling lScene->GetSrcObjectCount(KFbxMesh::ClassId).
for(int i = 0; i < lScene->GetSrcObjectCount(KFbxMesh::ClassId); i++) {
    KFbxMesh* lMesh = (KFbxMesh*)lScene->GetSrcObject(KFbxMesh::ClassId, i);
    //...
}

Loading and updating a different scene

At this point, we have moved the elements of the current scene into the reference scene, and the current scene has become empty. We now take our current scene and repopulate it with the contents of "file2.fbx". Finally, we change the name of the root's first child in the current scene to the name of root's first child in the reference scene. In the end, the name of the root's first child in the current scene should be "Test Name".

    // Import the second file into lCurrentScene.
    LoadScene(lSdkManager, lCurrentScene, "file2.fbx");

    // Get the names after the second file is loaded.
    KString lNameBeforeUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();
    KString lReferenceName = lMyRefScene->GetRootNode()->GetChild(0)->GetName();

    // Update the root's child 0 name.
    lCurrentScene->GetRootNode()->GetChild(0)->SetName(lReferenceName);
    KString lNameAfterUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();

    // Destroy the reference scene.
    lMyRefScene->Destroy();
    lMyRefScene = NULL;

    // Verification step
    printf("Verification (0 for success): %d\n", lNameAfterUpdate.Compare("Test Name"));

Scene Merging Tutorial Program

The following is the scene merging tutorial program discussed above.

#include <fbxsdk.h>

/**
 * Load a scene given an KFbxSdkManager, a KFbxScene, and a valid filename.
 */
int LoadScene(KFbxSdkManager* pSdkManager, KFbxScene* pScene, char* filename) {
    // Create the io settings object.
    KFbxIOSettings *ios = KFbxIOSettings::Create(pSdkManager, IOSROOT);
    pSdkManager->SetIOSettings(ios);

    // Create an importer using our sdk manager.
    KFbxImporter* lImporter = KFbxImporter::Create(pSdkManager,"");
    
    // Use the first argument as the filename for the importer.
    if(!lImporter->Initialize(filename, -1, pSdkManager->GetIOSettings())) {
        printf("Call to KFbxImporter::Initialize() failed.\n");
        printf("Error returned: %s\n\n", lImporter->GetLastErrorString());
        lImporter->Destroy();
        return -1;
    }

    // Import the contents of the file into the scene.
    lImporter->Import(pScene);

    // The file has been imported; we can get rid of the importer.
    lImporter->Destroy();
    return 0;
}

/**
 * Entry point for the merging two scenes sample program.
 */
int main(int argc, char** argv) {
    // Create an SDK manager.
    KFbxSdkManager* lSdkManager = KFbxSdkManager::Create();

    // Create a new scene so it can be populated by the imported file.
    KFbxScene* lCurrentScene = KFbxScene::Create(lSdkManager,"My Scene");

    // Load the scene.
    LoadScene(lSdkManager, lCurrentScene, "file1.fbx");

    // Modify the scene. In this example, only one node name is changed.
    lCurrentScene->GetRootNode()->GetChild(0)->SetName("Test Name");

    // Create a reference scene to store the contents of the currently loaded scene.
    KFbxScene *lMyRefScene = KFbxScene::Create(lSdkManager, "My Reference Scene");

    // Move the node tree of the currently loaded scene into the reference scene.
    int lNumChildren = lCurrentScene->GetRootNode()->GetChildCount();
    for(int i = 0; i < lNumChildren; i++) {

        // Obtain a child node from the currently loaded scene.
        KFbxNode* lChildNode = lCurrentScene->GetRootNode()->GetChild(i);

        // Attach the child node to the reference scene's root node.
        lMyRefScene->GetRootNode()->AddChild(lChildNode);
    }

    // Remove the children from the root node.
    lCurrentScene->GetRootNode()->DisconnectAllSrcObject();

    // Move other objects to the reference scene.
    int lNumSceneObjects = lCurrentScene->GetSrcObjectCount();
    for(int i = 0; i < lNumSceneObjects; i++) {
        KFbxObject* lObj = lCurrentScene->GetSrcObject(i);
        if(lObj == lCurrentScene->GetRootNode() || *lObj == lCurrentScene->GetGlobalSettings()){
            // Don't move the root node or the scene's global settings; these
            // objects are created for every scene.
            continue;
        }
        // Attach the object to the reference scene.
        lObj->ConnectDstObject(lMyRefScene);
    }

    // Disconnect all scene objects.
    lCurrentScene->DisconnectAllSrcObject();

    // Import the second file into lCurrentScene.
    LoadScene(lSdkManager, lCurrentScene, "file1.fbx");

    // Get the names after the second file is loaded.
    KString lNameBeforeUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();
    KString lReferenceName = lMyRefScene->GetRootNode()->GetChild(0)->GetName();

    // Update the root's child 0 name.
    lCurrentScene->GetRootNode()->GetChild(0)->SetName(lReferenceName);
    KString lNameAfterUpdate = lCurrentScene->GetRootNode()->GetChild(0)->GetName();

    // Destroy the reference scene.
    lMyRefScene->Destroy();
    lMyRefScene = NULL;

    // Verification step
    printf("Verification (0 for success): %d\n", lNameAfterUpdate.Compare("Test Name"));

    // Destroy the sdk manager.
    lSdkManager->Destroy();
    exit(0);
}
See Also