ChapterThe 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:
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 ); } }
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.
- XSIDump: Prints information from a dotXSI file to a shell window. For more information about this example, see XSIDump: Creating a Standalone Executable to Print XSI Information.
- SLTest: A basic example that uses the Semantic Layer.
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
Read a file
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
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; }
![]()