Chapter

The FTK API


The Softimage|XSI FTK has two components:

The FTK has traditionally only provided what is now called an IO layer. This layer allows minimal access to the data in template form with no understanding or validation of the data at all. The developer has to manually extract all the information and know exactly where to look for that information.

This can mean having to create complex programs just to do simple tasks such as reading the file and extracting simple information. In order to improve the usability of the dotXSI file format, the Semantic Layer has been built on top of the IO layer. It adds a new level of understanding of how a semantically correct file should be presented to the developer.

The IO Layer

The FTK is composed of two layers: the IO Layer and the Semantic Layer. You do not have to use the Semantic layer, although it simplifies using the dotXSI file format.

The IO layer is a small core of classes that uses generic objects to represent a dotXSI file. The IO layer is essentially a parser class with a collection of templates which in turn contains a collection of parameters. The IO layer provides the following classes:

Category
Class Name
Description
File parsing
CXSIParser
The CXSIParser provides all the basic functionality of reading and writing a dotXSI file. It contains specific functions to handle different versions of dotXSI and provides a set of reading and writing callbacks that are used by these different versions.
Templates
CdotXSITemplate
CdotXSITemplates
The CdotXSITemplate class implements a dotXSI template. The CdotXSITemplates class implements a collection of dotXSI templates.
Parameters
CdotXSIParam
CdotXSIParams
SI_TinyVariant
The CdotXSIParam class implements a dotXSI parameter. The CdotXSIParams class implements a collection of dotXSI parameters and the SI_TinyVariant is a small structure used to represent a platform independent variant type.

The Semantic Layer

The semantic layer is built on top of the IO layer. Each semantic layer class uses the data stored in the IO layer, which means that there is no redundancy of data in memory.

For example, the CSLTransform class implements the SI_Transform template. That class allows the user to set or get transformation parameters. These parameters are themselves stored internally inside a CdotXSIParams collection which belongs to a CdotXSITemplate, but accessed through the CSLTransform class methods. This means also that the same data can be accessed either using the CSLTransform class (semantic layer) or the CdotXSITemplate class (IO layer).

Connecting the semantic layer with the IO layer

To get the IO layer handle of a semantic layer object, use the CSLTemplate::Template() method. All semantic layer objects that implement a dotXSI template are derived from the CSLTemplate class.

To properly synchronize the IO layer from a freshly created semantic layer structure, use the CSLTemplate::Synchronize() method.

At the heart of the Semantic layer are several base classes:

Scene Class

The scene class (CSLScene) is the doorway to the semantic layer. You have to instantiate a CSLScene object in order to have access to the other objects. You do not need to create any other objects directly; all objects should be created by a method either on the CSLScene object or on an object created from it. The CSLScene object allows you to create, read, and write files, access all models in the scene, and access all the top-level constructs of the dotXSI file including:

Example (Scene Class)
CSLScene l_scene;
// Make sure the scene is either read in or initialized 
SI_Float l_Red = l_scene->Ambience ()->GetColor()->m_fR; 

Proxy Classes

The proxy classes are widely used to represent a parameter within the template itself. They provide a user-friendly format of the data. They support all the possible types supported by the semantic layer:

Each proxy returns and accepts the types as expected. For example, a bool will not take a TinyVariant type parameter, but only a bool type parameter. The same is true for String Enum, such as for the Scene Info, where we can have the frames or seconds for the timing. Normally, one has to compare the values with strings. This is not the most convenient way to work, especially because only two values are possible ("SECONDS" or "FRAMES"). Now the developer can just use the following:

CSLScene l_scene;
// Make sure the scene is either read in or initialized 
l_scene->SceneInfo()->SetTimingType( CSLSceneInfo::SI_SECONDS ); 

This sets the proper value in the corresponding template (in this case "SECONDS").

The CSLVariantProxy class can be used for parameters with variable type. Unlike the proxy classes for the basic types, a TinyVariant value is returned.

Array Classes

Array proxies simplify the use of the various array types in the dotXSI file format, whether it is an array of vertex positions (floats), Vertex indices (int), or Joint List (string).

The array proxy allows you to work either on the 1D format or in the 2D format where you can add Rows. For example, the CSLVector2Darray class lets you get and manipulate rows of 2D entries whose types are vector2d. This makes accessing these arrays easier.

Example (Array Classes)

For example, to write all the vertices in one given shape (ordered in this case) you would write:

CSLBaseShape * l_shape;
// Make sure l_shape is set properly to a Shape;
CSLBaseShape::CSLVector3DArray * VertexList = l_shape->GetVertexList();
for (int i = 0; i < l_shape->GetVertexCount(); i++)
{
	SI_Float fX, fY, fZ;
	fX = (*VertexList)[i].GetX();
	fY = (*VertexList)[i].GetY();
	fZ = (*VertexList)[i].GetZ();
} 

Utility Classes

There are also several classes that simplify access to different pieces of data (components):

Example (Utility Classes)

For example, to list the values at a given Fcurve:

CSLFCurve l_fcurve;
//
// Check the type
//
if (l_fcurve->Getinterpolationtype() == CSLTemplate::SI_HERMITE)
{
 	CSLHermiteKey * list = l_fcurve->GetHermiteKeyListPtr();
 	for (int i = 0; i < l_fcurve->GetKeyCount(); i++)
	{
		printf( "Values for Hermite keys are: [Frame=%f][Value=%f][Left 
Tan=%f][Right Tan=%f]\n",
			list[i].m_fTime,
			list[i].m_fValue,
			list[i].m_fInTangent,
			list[i].m_fOutTangent );
	}
} 

Tip  

Notice that this example did not use the array structure, but just the straight pointer to the list of keys. This speeds up access, but does not allow any modifications, as it is an accessor only. It is quick and simple instead of the more feature-complete CSLHermiteKeyArray that lets you add and remove keys.

FTK Examples

This section contains examples of how to use the Semantic Layer to extract information from the dotXSI file format. These examples answer the questions, “How do I...”:

Other FTK Examples

Source code examples for Windows and Linux can be found in the ...\src\ directory.

Find out how many materials are present in the scene
#include "stdafx.h"
#include "SemanticLayer.h"

main()
{
	CSLScene Scene;

	if (Scene.Open( "c:\\example.xsi" ) == SI_SUCCESS)
	{
		Scene.Read();
	}
	ULONG l_ulNumMaterial = Scene.GetMaterialLibrary()->GetMaterialCount();
	// Now you have the number of Materials in your material library
	// Regardless of where you have the template, whether it is the first, or the 
last.
} 
Iterate over all the models in the scene
#include "stdafx.h"
#include "SemanticLayer.h"

void Recurse( CSLModel* in_child )
{
	//
	// Do what you want with the child,
	//

	printf( "%s\n", in_child->GetName() );

	//
	// Now recurse its children
	//

	CSLModel* *l_childrenList = in_child->GetChildrenList();

	//
	// Loop through all children
	//

	for (int i = 0; i < in_child->GetChildrenCount(); i++ )
	{
		Recurse( l_childrenList[i] );
	}
	return;
}


main()
{
	CSLScene Scene;

	if (Scene.Open( "c:\\example.xsi" ) == SI_SUCCESS)
	{
		Scene.Read();
		Recurse( Scene.Root() );
	}
} 
Create a scene object
CSLScene l_scene; 
Read a file
if (l_scene.Open( "C:\\example.xsi" ) == SI_SUCCESS)
{
       l_scene.Read();
} 
Get information from the scene
EtimingType l_Timing = l_scene.GetTimingType();
SI_Float l_Start = l_scene.GetStart();
SI_Float l_End = l_scene.GetEnd();
SI_Float l_FrameRate = l_scene.GetFrameRate(); 
Navigate the list of children and identify the primitive type
void Recurse( CSLModel* in_child )
{
		switch (in_child->GetPrimitiveType() ) {
		case CSLTemplate::SI_MESH:
			{
				CSLMesh* left_primitive = (CSLMesh*) in_child->Primitive();
				break;
			}
		case CSLTemplate::SI_CAMERA:
			{
				CSLCamera* left_prim  = (CSLCamera*) in_child ->Primitive();
				break;
			}
		case CSLTemplate::SI_DIRECTIONAL_LIGHT:
		case CSLTemplate::SI_INFINITE_LIGHT:
		case CSLTemplate::SI_POINT_LIGHT:
		case CSLTemplate::SI_SPOT_LIGHT:
			{
				CSLLight* left_prim  = (CSLLight*) in_child ->Primitive();
				break;
			}
		case CSLTemplate::SI_IK_ROOT:
			{
				CSLIKRoot* left_prim  = (CSLIKRoot*) in_child ->Primitive();
				break;
			}
		case CSLTemplate::SI_IK_JOINT:
			{
				CSLIKJoint* left_prim  = (CSLIKJoint*) in_child ->Primitive();
				break;
			}
		case CSLTemplate::SI_IK_EFFECTOR:
			{
				CSLIKEffector* left_prim  = (CSLIKEffector*) in_child->Primitive();
				break;
			}
		case CSLTemplate::SI_NURBS_SURFACE:
			{
				CSLNurbsSurface* left_prim  = (CSLNurbsSurface*) 
in_child->Primitive();
				break;
			}
		case CSLTemplate::SI_NURBS_CURVE:
			{
				CSLNurbsCurve* left_prim  = (CSLNurbsCurve*) in_child->Primitive();
				break;
			}
		case CSLTemplate::SI_INSTANCE:
			{
				CSLInstance* left_prim  = (CSLInstance*) in_child->Primitive();
				break;
			}
		case CSLTemplate::SI_NULL_OBJECT:
			{
			}
		} 
Find the template type for a given primitive and cast the pointer to the appropriate type
if (model->GetPrimitiveType() == CSLTemplate::SI_IK_ROOT)
		CSLIKRoot* primitive = model->GetPrimitive(); 
Use Triangles list & Strips and iterate through all the lists and vertices
CSLTriangleList* in_left;
SI_Int* vertex_ptr = in_left->GetVertexIndicesPtr();
for (unsigned int i = 0; i < num_triangles; i++)
	{
		printf("Triangle [%d] vertices are [%d,%d,%d]\n", i,
			vertex_ptr[i*3+0],
			vertex_ptr[i*3+1],
			vertex_ptr[i*3+2]);
	} 
Find all sub-operators in an Fx Tree
CSLFXTree* in_left;
CSLFXOperator** left  =  in_left->GetFXOperatorList();
	for (int i = 0; i < in_left->GetFXOperatorCount(); i++)
		printf("Operator [%s] has [%d] Parameters\n",
			left[i]->GetName(),
			left[i]->GetParameterCount()); 
Find all parameters of an operator in an Fx Tree
	CSLFXOperator* in_left;
	CSLVariantParameter* param_list = in_left->GetParameterList();
	for (int i=0;i<in_left->GetParameterCount();i++)
		printf("Operator [%s] has Parameter [%s]\n",
			in_left->GetName(),
			param_list[i]->GetName()); 
Get the value of a parameter in an Fx Tree
SI_TinyVariant value = parameter->GetValue(); 
Find the CustomParamInfo
CSLXSICustomParamInfo* custom_info = in_parameter->CustomParamInfo();
if (custom_info)
{
	SI_TinyVariant max, min;
	custom_info->GetMinValue( min );
	custom_info->GetMaxValue( max );
	printf("Capabilities are [%d]\n", custom_info->GetCapabilities() );
} 
Build a triangle strip converter
#include "stdafx.h"
#include <SemanticLayer.h>
#include <windows.h>
#include "nvstrip\NVTriStrip.h"

char l_buf[512];

int StripTriangleList( CSLTriangleList* in_left, CSLMesh* in_mesh )
{
	//
	// Now we will get the values from the Triangle List and
	// be ready to convert into a Triangle Strip
	//
	SI_Int* vertex_ptr = in_left->GetVertexIndicesPtr();
	SI_Int* nrm_ptr = in_left->GetNormalIndicesPtr();
	SI_Int* col_ptr = in_left->GetColorIndicesPtr();
	SI_Int number_of_uv = in_left->GetUVArrayCount();
	//
	// Prepare the data for stripification
	//
	PrimitiveGroup* result;
	unsigned short num_groups;
	//
	// Get the number of triangle in the triangle list.
	//
	unsigned long num_triangles = in_left->GetTriangleCount();
	//
	// Allocate the data for the indices in unsigned format.
	//
	unsigned short *indices = new unsigned short [ num_triangles * 3 ];
	//
	// Allocate the array for remapping  of the Normals, Vertex Colors, 
	// & UV coordinates...
	//
	unsigned short *remap = new unsigned short [ num_triangles * 3];
	for (unsigned int i = 0; i < num_triangles * 3; i++)
	{
		indices[i] = vertex_ptr[i];
	}
	//
	// Set the cache size for the initial size based on the number of triangles.
	//
	SetCacheSize( num_triangles * 3 );
	//
	// Generate the strips based on the indices passed in.
	//
	::GenerateStrips( indices, num_triangles * 3,  &result, &num_groups );
	for (i = 0; i < result->numIndices; i++)
	{
		sprintf( l_buf, "Value[%d] = %d\n", i, result->indices[i] );
		OutputDebugString( l_buf );
	}
	//
	// Build a remap table
	//
	{
		for (i = num_triangles * 3; i > 0; i--)
		{
			remap[vertex_ptr[i-1]] = i-1;
		}
	}
	//
	// Remap the normals
	//

	{
		for (i = 0; i < result->numIndices; i++)
		{
			sprintf( l_buf, "Normal[%i] = %i\n", i, 
remap[nrm_ptr[result->indices[i]]] );
			OutputDebugString( l_buf );
		}
	}
	//
	// Adds a new triangle strip list
	//
	CSLTriangleStripList* sl = in_mesh->AddTriangleStripList();
	//
	// Set its material based on what was the material of the triangle list
	//
	sl->SetMaterial( in_left->GetMaterial() );
	//
	// Adds a new strip to the triangle strip
	//
	CSLTriangleStrip* ts = sl->AddTriangleStrip();
	//
	// Transfer the data to the vertex array of the triangle strip.
	//
	CSLTriangleStrip::CSLIntArray* vertex_array = ts->GetVertexIndices();
	for (i = 0; i < result->numIndices; i++)
	{
		vertex_array->Add( result->indices[i] );
	}
	//
	// Now deal with the normal data...
	//
	CSLTriangleStrip::CSLIntArray* normal_array = ts->CreateNormalIndices();
	for (i = 0; i < result->numIndices; i++)
	{
		normal_array->Add(remap[nrm_ptr[result->indices[i]]]);
	}
	if (col_ptr)
	{
		//
		// Now deal with the vertex colors
		//
		CSLTriangleStrip::CSLIntArray* color_array = ts->CreateColorIndices();
		for (i = 0; i < result->numIndices; i++)
		{
			color_array->Add(remap[col_ptr[result->indices[i]]]);
		}
	}
	for (i = 0; i < (unsigned int) number_of_uv; i++)
	{
		//
		// Now deal with the vertex colors
		//
		SI_Int* uv_ptr = in_left->GetUVIndicesPtr(i);

		CSLTriangleStrip::CSLIntArray* uv_array = ts->CreateColorIndices();
		for (i = 0; i < result->numIndices; i++)
		{
			uv_array->Add(remap[uv_ptr[result->indices[i]]]);
		}
	}

	delete [] indices;
	delete [] remap;
	delete [] result;
	//
	// Remove the triangle list...
	//
	in_mesh->RemoveTriangleList( in_left );
	return num_groups;
}

//
// Strips one mesh by processing all its triangle lists it has.
//
int StripMesh( CSLMesh* in_left )
{
	int l_lIndex = 0;
	CSLTriangleList** left;
	left = in_left->TriangleLists();
	
	for (l_lIndex = 0; l_lIndex < in_left->GetTriangleListCount(); l_lIndex++)
	{
		StripTriangleList( left[l_lIndex], in_left);
	}
	return 0;
}

//
// Strips a model, but will delegate to StripMesh if the primitive is a mesh.
//
int Strip( CSLModel* in_left )
{
	if (in_left->GetPrimitiveType() == CSLTemplate::SI_MESH)
	{
		CSLMesh*  left_primitive = (CSLMesh*) in_left->Primitive();
		StripMesh( left_primitive );
	}
	return 0;
}

void Recurse( CSLModel* in_child )
{
	//
	// Strip the child if need be.
	//
	Strip ( in_child );
	//
	// Now recurse its children
	//
	CSLModel* *l_childrenList = in_child->GetChildrenList();
	//
	// Loop through all children
	//
	for (int i = 0 ; i < in_child->GetChildrenCount(); i++ )
	{
		Recurse( l_childrenList[i] );
	}
	return;
}
int Stripify( SI_Char* in_left, SI_Char* in_right )

{
	CSLScene Scene;

	if (Scene.Open( in_left ) == SI_SUCCESS)
	{
		Scene.Read();
		Recurse( Scene.Root() );
	}

	Scene.Write( in_right );

	Scene.Close();

	return 0;
}