Constructing the baseline scene
 
 
 

MethodCreateScene()constructs a scene consisting of a marker, a camera pointed at the marker, and a light.

Naming the take which will contain the animation

In live action filmmaking, a “take” refers to a single version of the filming of a scene. Directors can film multiple takes, and refer to them as “take one”, “take two”, etc.

In FBX, a take contains all the animation curves necessary to define the animation for a 3D object. An FBX scene can have multiple takes. This means that the 3D objects in the scene can be animated differently in each take.

A take is a concept used by MotionBuilder and FBX, but not used by many 3D tools including 3ds Max and Maya. Of the file formats supported by FBX SDK 2009.1, only FBX files can contain multiple takes. Other file formats can contain animation of course, but they cannot store explicit take information, and they can only contain animation equivalent to one FBX take.

KFbxNode’s methods and properties that manage takes are inherited from KFbxTakeNodeContainer:

In FBX SDK, a take is a container for animation data. Any scene with animation must contain at least one take. CubeCreator’s scene has only one take.

A take is identified by its name:

 // set the take name
 gTakeName = "Take camera animation";

A scene may have many takes, but only one of them can be the current take. When an FBX scene is exported to a file format that does not support takes, only the current take is exported.

Even though CubeCreator’s scene has only one take, we explicitly set it as the current take:

 // set current take name
 gScene->SetCurrentTake(gTakeName.Buffer());

Even though we have a name for our take, we haven’t actually created it yet. Nor have we assigned any animation to the take. We’ll do both inAdding animation to the camera.

NoteFor an example of an FBX file with several takes of animation, seehumanoid.fbx, located in\examples\viewscene. You can use FBX for QuickTime to cycle through the takes. Or, you can use Viewscene itself (seeViewScene).

Creating a marker to be used as a reference point

Markers are usually not rendered, i.e., they are usually not visible in a 3D view of a scene. But markers can be useful to the animator or (in this case) the programmer as a reference point.

CubeCreator creates a marker that will be used as the point of interest of the camera, and as the location of the first cube.

Here is the code inCreateScene():

 // create a marker
 KFbxNode* lMarker = CreateMarker(gSdkManager, "Marker");

And here is the code inCreateMarker():

// Create a marker to use a point of interest for the camera.
KFbxNode* CreateMarker(KFbxSdkManager* pSdkManager, char* pName)
{

First, we use the SDK Manager to allocate memory for the KFbxMarker object:

 KFbxMarker* lMarker = KFbxMarker::Create(pSdkManager,pName);

Then, we allocate the memory for the KFbxNode object:

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

We tell the node that its contents are the marker:

 lNode->SetNodeAttribute(lMarker);

Finally, we return the node that contains the marker:

 return lNode;
}

Creating a camera

CreateScene()callsCreateCamera()to create a node whose node attribute is a camera:

 // create a camera
 KFbxNode* lCamera = CreateCamera(gSdkManager, "Camera");
...
// Create a camera.
KFbxNode* CreateCamera(KFbxSdkManager* pSdkManager, char* pName)
{

First we instantiate a camera object, using the SDK Manager to manage memory:

 KFbxCamera* lCamera = KFbxCamera::Create(pSdkManager,pName);

Then we adjust the settings of the camera so that it “films” in a format suitable for (non-high-definition) television :

 // Set camera properties for classic TV (aspect ratio 4:3)
 lCamera->SetFormat(KFbxCamera::eNTSC);

Next we instantiate a node object:

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

We assign the camera to the node:

 lNode->SetNodeAttribute(lCamera);

Finally, we return the node that contains the camera:

 return lNode;
}

Creating one texture available to all cubes

CreateScene()callsCreateTexture()to create a texture object that is available for use by any of the cubes. The texture object is global:

KFbxTexture* gTexture = NULL;
...
 // create a single texture shared by all cubes
 CreateTexture(gSdkManager);
...
// Create a global texture for cube.
void CreateTexture(KFbxSdkManager* pSdkManager)
{
 gTexture = KFbxTexture::Create(pSdkManager,"Diffuse Texture");
 // Resource file is in the application directory.
 KString lTexPath = gAppPath + "\\Crate.jpg";
NoteThe texture filecrate.jpgmust be available to CubeCreator at run time, and available to the renderer (e.g., FBX for QuickTime) of the file exported by CubeCreator. See:
 // Set texture properties.
 gTexture->SetFileName(lTexPath.Buffer());
 gTexture->SetTextureUse(KFbxTexture::eSTANDARD);
 gTexture->SetMappingType(KFbxTexture::eUV);
 gTexture->SetMaterialUse(KFbxTexture::eMODEL_MATERIAL);
 gTexture->SetSwapUV(false);
 gTexture->SetTranslation(0.0, 0.0);
 gTexture->SetScale(1.0, 1.0);
 gTexture->SetRotation(0.0, 0.0);
}

Embedding media files in an FBX file

When you run CubeCreator, you can save the scene in the file format of your choice and in the folder of your choice.

If you save the scene as a binary FBX file, CubeCreator embeds the texture filecrate.jpgin the FBX file. This ensures that if you send your FBX file to a co-worker, the FBX file will contain all files referenced by the scene.

In general, you can embed any kind of media file in a binary FBX file—providing you set the appropriate export option. Here is how we do it in CubeCreator.

When the user clicks to Save to button, CubeCreator callsSaveScene(), passingtrueas the value ofpEmbedMedia:

// to save a scene to a FBX file
bool SaveScene(KFbxSdkManager* pSdkManager, KFbxDocument* pScene, const char* pFilename, int pFileFormat, bool pEmbedMedia)
...
 KFbxStreamOptionsFbxWriter* lExportOptions=
 KFbxStreamOptionsFbxWriter::Create(pSdkManager, "");
 if (pSdkManager->GetIOPluginRegistry()->WriterIsFBX(pFileFormat))
 {
 // Set the export states.
 // By default, the export states are always set to true except for
 // the option KFBXSTREAMOPT_FBX_EMBEDDED. The code below
 // shows how to change these states.
 lExportOptions->SetOption(KFBXSTREAMOPT_FBX_MATERIAL, true);
 lExportOptions->SetOption(KFBXSTREAMOPT_FBX_TEXTURE, true);
 lExportOptions->SetOption(KFBXSTREAMOPT_FBX_EMBEDDED, pEmbedMedia);
 lExportOptions->SetOption(KFBXSTREAMOPT_FBX_MEDIA, true);
...
 }

Processing FBX files that contain embedded media

Applications that render or otherwise process FBX files must access the textures and other media files.

If media files are embedded in a binary FBX file, when you import the file into the application, the FBX SDK importer will extract the media from the FBX file and copy them to a directory. By default:

Processing scene files with references to media

ASCII FBX files, OBJ files, and other FBX-supported file formats cannot contain embedded media. Moreover, you can create binary FBX files that need media files, but set the export option so that the files are not embedded. In these cases, the file will retain the addresses of the media files.

Then you must make sure that FBX for QuickTime (or 3ds Max, Maya, or any other application that you use to render or otherwise process the scene) can find those media.

Here is how FBX SDK searches for each non-embedded media file in, for example, an ASCII FBX file:

 // Resource file must be in the application's directory.
 KString lTexPath = gAppPath + "\\Crate.jpg";
...
 gTexture->SetFileName(lTexPath.Buffer());

Creating an array of materials to be shared

CreateScene()callsCreateMaterials()to create one material that will be applied to each face of the cube. These materials are available for use by all the cubes in the scene.

We’ll use a Phong shader for the materials.

KFbxSurfacePhong* gMaterial = NULL;
 // create a material shared by all faces of all cubes
 CreateMaterials(gSdkManager);
...
// Create global material for cube.
void 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);
 gMaterial = KFbxSurfacePhong::Create(pSdkManager,
 lMaterialName.Buffer());

The following properties are used by the Phong shader to calculate the color for each pixel in the material:

 // Generate primary and secondary colors.
 gMaterial->GetEmissiveColor() .Set(lBlack);
 gMaterial->GetAmbientColor() .Set(lRed);
 gMaterial->GetDiffuseColor() .Set(lDiffuseColor);
 gMaterial->GetTransparencyFactor() .Set(40.5);
 gMaterial->GetShadingModel() .Set(lShadingName);
 gMaterial->GetShininess() .Set(0.5);
}

Pointing the camera at the marker

CreateScene()callsSetCameraPointOfInterest()to set the marker as the target (i.e., point of interest) of the camera. This means that even if the camera and/or the target moves, the camera will continue to point at the target.

 // set the camera point of interest on the marker
 SetCameraPointOfInterest(lCamera, lMarker);

lMarkerandlCameraare pointers to KFbxNode objects, not pointers to KFbxMarker and KFbxCamera objects. Recall that each node (i.e., KFbxNode object) contains a pointer to its node attribute object (ornull): the node attribute object is the “contents” of the node object. In this case:

// Set target of the camera.
void SetCameraPointOfInterest(KFbxNode* pCamera, KFbxNode* pPointOfInterest)
{
 // Set the camera to always point at this node.
 pCamera->SetTarget(pPointOfInterest);
}

Setting the position of the marker

Here we set the marker’s translation, rotation, and scaling (the TRS properties):

 // set the marker position
 SetMarkerDefaultPosition(lMarker);
...
// Set marker default position.
void SetMarkerDefaultPosition(KFbxNode* pMarker)
{
 // The marker is above the origin.
 pMarker->LclTranslation.Set(KFbxVector4(0.0, 40.0, 0.0));
 pMarker->LclRotation.Set(KFbxVector4(0.0, 0.0, 0.0));
 pMarker->LclScaling.Set(KFbxVector4(1.0, 1.0, 1.0));
}

LclTranslation.Set()sets the position of a node—in this case, the camera. Note that:

Similarly, the rotation and scaling of a node are also defaults and are also relative to the parent.

Setting the position of the camera

 // set the camera position
	SetCameraDefaultPosition(lCamera);
...
// Compute the camera position.
void SetCameraDefaultPosition(KFbxNode* pCamera)
{
 // set the initial camera position
 KFbxVector4 lCameraLocation(0.0, 200.0, -100.0); 
 pCamera->LclTranslation.Set(lCameraLocation);
}

Adding animation to the camera

 // animate the camera
 AnimateCamera(lCamera, gTakeName);
// The camera move on X and Y axis.
void AnimateCamera(KFbxNode* pCamera, KString pTakeName)
{

Declare an animation curve (FCurve), a time object, and an animation key index. The key index is 0 for the first key, 1 for the second key, and so forth.

 KFCurve* lCurve = NULL;
 KTime lTime;
 int lKeyIndex = 0;

Here, we create onetake nodefor this scene. A node (KFbxNode object) is a container for take nodes. A take node (KFbxTakeNode object) is in turn a container for animation data. Animation data are stored as function curves, usually called FCurves.

We give the take node the name that we set inNaming the take which will contain the animation.

 pCamera->CreateTakeNode(pTakeName.Buffer());

A scene can have many takes. Only one of these takes can be the current take. CubeCreator’s scene has only one take, but we explicitly set it as the current take.

 pCamera->SetCurrentTakeNode(pTakeName.Buffer());

The next statement does the following:

 pCamera->LclTranslation.GetKFCurveNode(true, pTakeName.Buffer());

GetKFCurveNodemay not be an obvious name for a method to create FCurves, but iftrueis its first parameter, it will create a set of FCurves if the node (in this case,pCamera) does not already have FCurves. For local translation, the default values for the X, Y, and Z channels are 0, 0, and 0.

There are three FCurves each for translation: one for translation along the X-axis, one for translation along the Y-axis, and one for translation along the Z-axis. Similarly, there are three FCurves each for rotation and scaling.

Here are some defined symbols fromkfcurvenode.h(located in\include\kfcurve\):

 #define KFCURVENODE_TRANSFORM "Transform"
 #define KFCURVENODE_T "T"
 #define KFCURVENODE_T_X "X"
 #define KFCURVENODE_T_Y "Y"
 #define KFCURVENODE_T_Z "Z"
 #define KFCURVENODE_R "R"
 #define KFCURVENODE_R_X "X"
 #define KFCURVENODE_R_Y "Y"
 #define KFCURVENODE_R_Z "Z"
 #define KFCURVENODE_R_W "W"
 #define KFCURVENODE_S "S"
 #define KFCURVENODE_S_X "X"
 #define KFCURVENODE_S_Y "Y"
 #define KFCURVENODE_S_Z "Z"

Here is howAnimateCamera()sets up the animation curve for translation along the X-axis. We start by getting the animation curve with its default values:

 // X translation.
 lCurve = pCamera->LclTranslation.GetKFCurve(
 KFCURVENODE_T_X, pTakeName.Buffer());
 if (lCurve)
 {

The animation begins immediately (at 0.0 seconds), and ends at 20.0 seconds: 0.0 and 20.0 are keyframe values.

During that time, any node that is animated by this curve (in this case, the camera node) will move from its initial displacement along the X-axis (0.0 units, the third parameter ofKeyset()), to its final displacement (500.0 units).

The velocity of the displacement will be the same at the beginning of the animation as at the end (KFCURVE_INTERPOLATION_LINEAR).

 lCurve->KeyModifyBegin();
 lTime.SetSecondDouble(0.0);
 lKeyIndex = lCurve->KeyAdd(lTime);
 lCurve->KeySet(lKeyIndex, lTime, 0.0, KFCURVE_INTERPOLATION_LINEAR);
 lTime.SetSecondDouble(20.0);
 lKeyIndex = lCurve->KeyAdd(lTime);
 lCurve->KeySet(lKeyIndex, lTime, 500.0);
 lCurve->KeyModifyEnd();
 }

The code is similar for translation along the Y-axis. There is no code for the Z-axis: accordingly, the camera will not move along the Z-axis. Nor is there rotation or scaling for the Z-axis.

Build the initial scene graph

CubeCreator builds a scene with only two nodes, the camera and the marker. These nodes are both direct children of the scene’s root node.

 // build a minimum scene graph
 KFbxNode* lRootNode = gScene->GetRootNode();
 lRootNode->AddChild(lMarker);
 lRootNode->AddChild(lCamera);

CubeCreator’s UI will display this tree to the user, then allow the user to add cubes to it.

Set the camera as the scene’s default camera

Since a scene can have many cameras, FBX SDK allows you to switch from camera to camera. The default camera is the camera that will be used when the scene’s animation begins.

Even though CubeCreator’s scene has only one camera, we must explicitly set it as the default camera:

 // set camera as the default camera
 gScene->GetGlobalCameraSettings().SetDefaultCamera(
 (char *)lCamera->GetName());