DOM guide: Integration templates: Difference between revisions

From COLLADA Public Wiki
Jump to navigation Jump to search
Alorino (talk | contribs)
No edit summary
 
SteveT (talk | contribs)
No edit summary
 
(57 intermediate revisions by 3 users not shown)
Line 1: Line 1:
Integration templates convert COLLADA DAE objects into application-specific objects and back again. This page provides a step-by-step example of how to integrate application data structures with the COLLADA runtime infrastructure (COLLADA DOM). Detailed knowledge of the COLLADA DOM architecture is not necessary to understand this example or to successfully integrate your own application-specific data structures.
When you [[DOM guide: Working with documents|load]] a [[COLLADA XML instance document]] into the [[COLLADA DOM]], it creates a DOM object for each XML element in the COLLADA document. An application can then use DOM methods to read and manipulate the DOM objects. However, your application might already have its own data structures designed to represent the data coming from COLLADA XML elements and its own tools for manipulating those data structures. In that case, you can implement the COLLADA DOM's '''integration templates''', which, when loading a COLLADA document, convert DOM objects into your application's data structures, and back again when saving. The integration templates cannot be used to create new COLLADA documents based on your runtime data structures.
 
This article provides a step-by-step example of how to integrate application data structures with the COLLADA runtime infrastructure ([[COLLADA DOM]]). Detailed knowledge of the COLLADA DOM architecture is not necessary to understand this example or to successfully integrate your own application-specific data structures.
 
==Overview==
==Overview==
The COLLADA Object Model is a set of runtime COLLADA DAE objects that correspond to elements in a COLLADA XML instance document. For importing, they are built when a COLLADA XML instance document is parsed. For exporting, you might need to provide code to build them.To convert data contained in COLLADA DAE objects into your own application data structures, and the reverse, the DOM provides integration templates for every object. The integration templates provide plugin points where you can insert conversion code. After a document has been imported by the DOM into COLLADA DAE objects, or when you request an export, your integration code is called to perform the conversion.  
The [[COLLADA Object Model]] is a set of runtime COLLADA DOM objects that correspond to elements in a [[COLLADA XML instance document]] (a ''COLLADA document''). For importing, the COLLADA DOM builds these objects when it parses a COLLADA document. For exporting into a COLLADA  document, you need to provide code to build the appropriate DOM objects.  
Figure 4 COLLADA DOM Integration Usage Model for Importing
 
To convert data contained in COLLADA DOM objects into your own application's data structures, the DOM provides integration templates for every object. The integration templates include plug-in points where you can insert conversion code (otherwise, the templates do nothing). After a COLLADA document has been imported by the DOM into COLLADA DOM objects, the DOM calls your conversion code to convert between the DOM objects and the application's data structures.
 
:'''The following figure shows what happens during a ''load'' when integration templates are used.'''
:[[Image:IntegrationTemplates.jpg|600px]]
 
The basic steps during integration are the following:
The basic steps during integration are the following:
(1) Initialize the COLLADA DOM by creating a new DAE object.
#Application initializes the COLLADA DOM by creating a <code>DAE</code> object.
(2) Register application-specific integration libraries.
#Application registers its application-specific integration libraries.
(3) To convert after importing:
#When importing a COLLADA document:
(a) Import a COLLADA XML file by loading it; this places the data into runtime COLLADA DAE
##Application begins the import by calling <code>load</code>.
objects. COLLADA DAE objects with registered integration libraries automatically create their
##The DOM reads the COLLADA document and places its elements into runtime COLLADA DOM objects.
associated application data structures.
##The COLLADA runtime calls the conversion methods in the integration libraries to convert the content of COLLADA DOM objects into their associated data structures.
(b) The COLLADA runtime calls the conversion methods in the integration libraries to convert the
#When exporting to a COLLADA document:
content of COLLADA DAE objects into their associated data structures.
##Application begins the export of a COLLADA DAE structure by calling <code>save</code>.
(4) To convert before exporting:
##The COLLADA runtime calls registered conversion methods in the integration libraries to convert the content of the application's data structures into COLLADA DOM objects.
(a) If matching COLLADA DAE objects don’t already exist, call createTo to create them.
##The DOM exports the COLLADA document.
(b) Export the COLLADA DAE structure by saving it; the COLLADA runtime calls the conversion
 
methods in the integration libraries to convert your data structures into COLLADA DAE objects
==COLLADA DOM Integration Templates==
and then export them.
Integration templates are used to convert DOM objects to your run-time data structures. Each DOM object has its own integration template consisting of a header (.h) file and a code (.cpp) file. For example, the DOM object for the COLLADA <node> element is  <code>domNode</code>, and the template files to convert <code>domNode</code> objects to or from application data structures are <code>intNode.cpp</code> and <code>intNode.h</code>.
©SCEI
 
- 23 -
Copy into your application directory the template files for the elements that you want to convert from DOM objects into application-specific data structures. These copies are the starting points for your customized integration libraries. You can then add code to these integration libraries to do the conversion.
COLLADA DOM Integration Templates
 
At import time, elements in COLLADA instance documents are loaded into a run-time COLLADA Object
The plug-in points for your code are identified in comments in the template source, which is available in the <code>integrationTemplates</code> folder.
Model. The COLLADA DOM provides integration templates to enable you to convert COLLADA data
 
between the COLLADA Object Model and your own run-time structures.
==Integration Objects==
Each element in the COLLADA Object Model has its own integration template consisting of a header (.h)
Your integration library provides the code that defines an integration object for each DOM object. The integration object serves as the focal point for all information about the conversion. It defines methods that perform the conversion, and it provides data members that bind the DOM object with the application-specific data structure being converted to.
file and a code (.cpp) file. Copy into your application directory the template files for the elements that
 
you want to convert from DOM structures into application-specific structures or vice versa. These copies
Integration objects are represented with the <code>daeIntegrationObject</code> class. Every class derived from <code>daeElement</code> provides for an integration object through the data member <code>daeElement::_intObject</code>.
are the starting points for your customized integration libraries You can then add code to these integration
 
libraries to convert data stored in the associated DOM object into your application object(s) and vice versa.
The integration object class for each element is defined with the prefix "int". For example, the <code>domGeometry</code> class provides an <code>intGeometry</code> integration object.
The plugin points for your code are identified in comments in the template source.
 
The COLLADA DOM provides two sets of integration templates:
When you define integration code for a DOM object, your code binds the DOM object with your application-specific object, via the <code>_element</code> and <code>_object</code> data members of the integration object. You can use methods <code>daeIntegrationObject::getElement</code> and <code>getObject</code> to access these data members.
• Simple Integration Templates: These templates are in the templates/integrationSimple
 
directory, and provide plugin points for the most commonly converted COLLADA DAE objects.
When you load a COLLADA instance document, the DOM automatically creates the appropriate integration objects.  
These templates do not provide plugin points for the nested XML elements, such as the <author>
 
element found in <asset> that is defined by the domAuthor class.
To get the integration object for an element, use the method <code>daeElement::getIntObject</code>, which also initiates the conversion process for any objects that have not yet been converted.
• Full Integration Templates: These templates are in the templates/integrationFull directory,
 
and provide plugin points for all COLLADA DAE objects.
==Integration Template Plugin Points==
The DOM provides a pair of template files for each basic COLLADA DAE object. For example, the
The integration class for each DOM object provides six plug-in points into which you can add conversion code. The plug-in points are implemented as methods; you provide the code for the method bodies. You need to implement the body of the methods only for those plug-in points that are relevant to your application.
template files to convert domNode objects are intNode.cpp and intNode.h.
 
Integration Objects
The methods are:
Your integration library for each element in the COLLADA Object Model provides the code that defines
*<code>createFrom</code>: Defines the code to create the application-specific data structure associated with the DAE class for this template. This method sets up the integration object for the DAE class.
an integration object for the element. The integration object serves as the focal point for all information
*<code>fromCOLLADA</code>: Defines the code to covert a DOM object into your application-specific data structure.
about the conversion. It defines methods that perform the conversion, and it provides data members that
*<code>fromCOLLADAPostProcess</code>: Defines any post-processing code that must execute after the basic conversion to your data structure.
bind the COLLADA Object Model element with the application-specific data structure being converted to.
*<code>createTo</code>: Defines code to create the DOM object associated with the DAE class for this template if such a structure does not already exist (for example, if it was not created while importing).
Integration objects are represented with the daeIntegrationObject class. Every class derived from
*<code>toCOLLADA</code>: Defines the code to covert the content of your application’s data structures into DOM objects.
daeElement provides for an integration object through the data member daeElement::_intObject.
*<code>toCOLLADAPostProcess</code>: Defines any post-processing code that must execute after the basic conversion from your application’s data structure.
The integration object class for each element is defined with the prefix “int”. For example, the
 
domGeometry class provides an intGeometry integration object.
==Geometry Integration Example==
When you define integration code for an element, your code binds the element with your
application-specific object, via the _element and _object data members of the integration object. You
can use methods daeIntegrationObject::getElement() and getObject() to access these data
members.
When you load a COLLADA instance document into a new collection, the DOM automatically creates the
appropriate integration objects. To get the integration object for an element, use the method
daeElement::getIntObject(), which also initiates the conversion process for any objects that have
not yet been converted.
Note. The beta release of the COLLADA DOM does not automatically call the plugin code to update the COLLADA
Object Model data structures from your application objects prior to saving the database. If you wish to write the
changed application data out to a new COLLADA instance document, you must ensure that the COLLADA Object
Model structures are updated by calling the “to” plugin points in your application code, which can usually be
accomplished by calling the daeElement::getIntObject() method, since this method converts application
objects both to and from their associated COLLADA DAE Objects.
©SCEI
- 24 -
Integration Template Plugin Points
The integration class for each COLLADA Object Model element provides six plugin points into which you
can add conversion code. The plugin points are implemented as methods; you need to provide the code
for the method bodies. You need to implement only the body of the methods for those plugin points that
are relevant to your application.
createFrom(): Defines the code to create the application-specific data structure associated with the
DOM class for this template. This method sets up the integration object for the DOM class.
fromCOLLADA(): Defines the code to covert the COLLADA Object Model data structure into your
application-specific data structure.
fromCOLLADAPostProcess(): Defines any postprocessing code that must execute after the basic
conversion to your application data structure.
createTo(): Defines code to create the COLLADA Object Model data structure associated with the
DOM class for this template if such a structure does not already exist (for example, if it was not
created while importing).
toCOLLADA(): Defines the code to covert the content of your application’s data structures into
COLLADA Object Model data structures.
toCOLLADAPostProcess(): Defines any postprocessing code that must execute after the basic
conversion from your application’s data structure.
Geometry Integration Example
This example demonstrates integration with the <geometry> element.
This example demonstrates integration with the <geometry> element.
Creating the example has three basic steps. You copy the corresponding integration templates to
application-specific versions. You register these classes with the COLLADA DOM. In the application
Creating the example has three basic steps, summarized as follows:
runtime, you add code to initialize the COLLADA DOM, call the file load, and request application objects
#Copy the corresponding integration templates to application-specific versions.
from the integration classes.
#Register these classes with the COLLADA DOM.  
Figure 5 Files Used in this Example
#In the application runtime, add code to initialize the COLLADA DOM, call the file load, and request application objects from the integration classes.
Make Copies of Relevant Integration Templates
 
The first step in the process of creating conversion code is to copy the relevant integration template files
===Copying Integration Templates===
from the integration subdirectory into your application’s directory. For this example, the relevant
The first step in the process of creating conversion code is to copy the relevant integration template files from the integration subdirectory into your application’s directory. For this example, the relevant integration templates are <code>intGeometry.cpp</code> and <code>intGeometry.h</code>. You will modify the copied files to contain your conversion code.
integration templates are intGeometry.cpp and intGeometry.h. You will modify the copied files to
 
contain your conversion code.
===Registering Integration Libraries===
©SCEI
For the DOM to create integration objects during parsing or before exporting, you must register the integration classes with the infrastructure. To do this, call the <code>registerElement</code> method on each integration class that you are using. A function to register these classes with the COLLADA DOM is defined in <code>intRegisterElements.cpp</code>, which is also provided in the template directory. You can copy this function into your application's directory, though you should only register those classes that you'll be using, which in this example is <code>intGeometry</code>. The relevant code is shown here:  
- 25 -
void intRegisterElements()
Register Integration Libraries with the DOM
{
For the DOM to create integration objects during parse time or before exporting, you must register the
    intGeometry::registerElement();
integration objects with the infrastructure. To do this, call the registerElement() method on each
}
integration library that you are using. A convenience function to register these classes with the COLLADA
The integration file <code>intGeometry.cpp</code> provides the definition for the <code>intGeometry</code> class. Its contents are explained in a later step.
DOM is defined in intRegisterElements.cpp, which is also found in the template directory. You can
 
copy this function into your application’s directory. The relevant code for this example is shown here:
After the <code>intRegisterElements</code> function is defined, pass a handle to this function into the <code>DAE::setIntegrationLibrary</code> method, as described in the “Invoking Integration Libraries Registration” section.
void intRegisterElements()
 
{
===Defining Your Application Data Structure===
intGeometry::registerElement();
Within the integration library files, provide the code to create your application-specific data structure for the COLLADA DOM objects that you want to convert.
}
 
The integration library file provides the definition for the intGeometry class. Its contents are explained
For this example, the application's data structure used to represent geometry is defined in an application header file, in this case, <code>myGeometry.h</code>:
in a later step.
// Definition of application's myPologon class also included here
After the intRegisterElements() function is defined, pass a handle to this function into the
class myGeometry
DAE::setIntegrationLibrary() method, as described in the “Invoke Integration Libraries
{
Registration” section.
public:
Setting up the Integration Library Header File
    unsigned int _iVertexCount;
Now we begin the process of editing the new integration library for the objects we want to convert. We
    float *_pVertices;
start with the intGeometry.h integration library copied from the intGeometry.h template. The
    std::vector<myPolygon> _vPolygons;
template must be modified to add information about the application object(s) to or from which the
};
COLLADA Object Model structure will be converted. For the header, we need to declare the class we are
 
converting into or from:
===Provide the Plug-in Code to Create an Application Object for Importing===
// class myGeometry is fully defined in an application header file.
The plug-in method relevant to this step for importing is the <code>createFrom</code> method. Within the <code>createFrom</code> method, add code to create a new <code>myGeometry</code> object, initialize it, and initialize the <code>intGeometry</code> integration object data members. Within <code>intGeometry.cpp</code>:  
class myGeometry;
void intGeometry::createFrom(daeElementRef element)
We also add code to define the relevant structures and provide a method to return those structures. These
{
definitions fall within the body of the intGeometry integration object declaration:
    // create class to hold geometry information and
class intGeometry : public daeIntegrationObject
    // initialize the new object as empty
{
    _object = new myGeometry();
// intGeometry template declarations provided here
    _object->pVertices = NULL;
. . .
    // set up the _element data member of the integration object
public: // USER CODE
    _element = element;
virtual ~intGeometry();
}
// define the accessor for the myGeometry object
Now, when a COLLADA instance document is loaded  and a <code>domGeometry</code> object is encountered, the <code>createFrom</code> method automatically creates a new <code>myGeometry</code> object, because the <code>intGeometry</code> integration class has been registered with the DOM.
myGeometry *getGeometry() { return _object; }
 
private: // USER CODE
===Build Conversion Code for Importing===
// declare the types for the integration object data members
Now add code to the integration library to translate the data stored in a DOM object to its associated application data structure. The <code>fromCOLLADA</code> method is the plug-in point for the basic application-specific conversion code. The <code>fromCOLLADAPostProcess</code> method provides additional flexibility for the conversion process.
myGeometry *_object;
 
daeElement *_element;
Depending on the COLLADA DOM object and the application data structure, the code may vary significantly. For this example, we use the <code>intGeometry::fromCOLLADA</code> method to create new vertex buffers from the COLLADA DOM geometry object.
};
 
Defining Your Application Data Structure
The following code retrieves the mesh object from the COLLADA <code>domGeometry</code> object:
Within the integration library files, you need to provide the code to create your application-specific data
// Get the geometry element from this integration object
structure for the COLLADA DAE objects that you want to convert.
domGeometry* geomElement = (domGeometry*)(domElement*)getElement();
©SCEI
domMesh *meshEl = geomElement->getMesh();
- 26 -
When we have the mesh, we can construct our application-specific data structure <code>iVertices</code> for the <code>myGeometry</code> object. The following code shows how to create the new vertex buffer.
For this example, we have an application-specific data structure used to represent geometry, defined in an
// Get a pointer to the application-defined geometry object that was
application header file, in this case, myGeometry.h:
// automatically created during load by calling createFrom.
// Definition of application's myPologon class also included here
myGeometry *local = (myGeometry *)_object;
class myGeometry
{
// Get a pointer to the domPolygons in this domMesh. To simplify this example,
public:
// we will handle only a domMesh that has a single domPolygons.
unsigned int _iVertexCount;
if(meshElement->getPolygons_array().getCount() != 1)
float *_pVertices;
{
std::vector<myPolygon> _vPolygons;
    fprintf(stderr, "This example supports only one domPolygons per domMesh\n");
};
    return;
Provide the Plugin Code to Create an Application Object for Importing
}
The plugin method relevant to this step for importing is the createFrom() method.Within the
domPolygons *polygons = meshElement->getPolygons_array()[0];
createFrom() method, we add code to create a new myGeometry object, initialize it, and initialize the
intGeometry integration object data members. Within intGeometry.cpp:
// To simplify this example, we assume the domPolygons has only one domInput.
void intGeometry::createFrom(daeElementRef element)
if(polygons->getInput_array().getCount() != 1)
{
{
// create class to hold geometry information and
    fprintf(stderr, "This example supports only one domInput per domPolygons\n");
// initialize the new object as empty
    return;
_object = new myGeometry();
}
_object->pVertices = NULL;
// set up the _element data member of the integration object
// Loop over all the polygons in the domPolygons element
_element = element;
int polygonCount = polygons->getCount();
}
for (int i=0;i<polygonCount;i++)
Now, when a COLLADA instance document is loaded into the COLLADA runtime database and a
{
domGeometry object is encountered, the createFrom() method automatically creates a new
    myPolygon myPoly;
myGeometry object, because the intGeometry integration library has been registered with the DOM.
    // Get pointer to this polygon (domP).
Build Conversion Code for Importing
    domPolygons::domP *poly = polygons->getP_array()[i];
Now we must add code to the integration library to translate the data stored in a DOM object to its
    // Get the number of indices from the domP and save it in my structure.
associated application object. The fromCOLLADA() method is the plugin point for the basic
    myPoly._iIndexCount = poly->getValue().getCount();
application-specific conversion code. The fromCOLLADAPostProcess() method provides additional
    // You can modify the data as you copy it from
flexibility for the conversion process.
    // the COLLADA object to your object.
Depending on the COLLADA DAE object and the application object, the code may vary significantly. For
    // Here we repeat the first index in list as the last index,
this example, we use the intGeometry::fromCOLLADA() method to create new vertex buffers from the
    // to form a closed loop that can be drawn as a line strip.
COLLADA DAE geometry object.
    myPoly._iIndexCount++;
The following code retrieves the mesh object from the COLLADA domGeometry object:
    myPoly._pIndexes = new unsigned short[myPoly._iIndexCount];
// Get the geometry element from this integration object
    // Copy all the indices from the domP into my structure.
domGeometry* geomElement = (domGeometry*)(domElement*)getElement();
    for (int j=0;j<myPoly._iIndexCount-1;j++)
domMesh *meshEl = geomElement->getMesh();
    myPoly._pIndexes[j] = poly->getValue()[j];
When we have the mesh, we can construct our application-specific data structure iVertices for the
    // Repeat the first index at the end of the list to create a closed loop.
myGeometry object. The following code shows how to create the new vertex buffer.
    myPoly._pIndexes[j] = myPoly._pIndexes[0];
// Get a pointer to the application-defined geometry object that was
    // Push this polygon into the list of polygons in my structure.
// automatically created during load by calling createFrom.
    local->_vPolygons.push_back(myPoly);
myGeometry *local = (myGeometry *)_object;
}
// Get a pointer to the domPolygons in this domMesh. To simplify this example,
// we will handle only a domMesh that has a single domPolygons.
// Copy the vertices we are going to use into myGeometry. To keep things simple,
©SCEI
// we will assume there is only one domSource and domFloatArray in the domMesh,
- 27 -
// that it is the array of vertices, and that it is in X, Y, Z format. A real
if(meshElement->getPolygons_array().getCount() != 1)
// app would find the vertices by starting with domPolygons and following
{
// the links through the domInput, domVertices, domSource, domFloat_array,
fprintf(stderr,
// and domTechnique.
"This example supports only one domPolygons per domMesh\n");
if(meshElement->getSource_array().getCount() != 1)
return;
{
}
    fprintf(stderr, "This example supports only one source array per domMesh\n");
domPolygons *polygons = meshElement->getPolygons_array()[0];
    return;
int polygonCount = polygons->getCount();
}
// To simplify this example, we assume the domPolygons has only one domInput.
domSource *source = meshElement->getSource_array()[0];
if(polygons->getInput_array().getCount() != 1)
{
if(source->getFloat_array().getCount() != 1)
fprintf(stderr,
{
"This example supports only one domInput per domPolygons\n");
    fprintf(stderr, "This example supports only one float array per source\n");
return;
}
}
domFloat_array *floatArray = source->getFloat_array()[0];
// Loop over all the polygons in the domPolygons element
for (int i=0;i<polygonCount;i++)
// Assume there are 3 values per vertex with a stride of 3.
{
local->_iVertexCount = floatArray->getCount()/3;
myPolygon myPoly;
local->_pVertices = new float[local->_iVertexCount*3];
// Get pointer to this polygon (domP).
domPolygons::domP *poly = polygons->getP_array()[i];
// Copy the vertices into my structure one-by-one
// Get the number of indices from the domP and save it in my structure.
// (converts from COLLADA's doubles to floats).
myPoly._iIndexCount = poly->getValue().getCount();
for ( unsigned int i = 0; i < local->_iVertexCount*3; i++ )  
// You can modify the data as you copy it from
{
// the COLLADA object to your object.
    local->_pVertices[i] = floatArray->getValue()[i];
// Here we repeat the first index in list as the last index,
}
// to form a closed loop that can be drawn as a line strip.
 
myPoly._iIndexCount++;
===Access COLLADA Objects from the Application===
myPoly._pIndexes = new unsigned short[myPoly._iIndexCount];
Now put together the application code that registers the integration libraries, loads the COLLADA elements into the in-memory DOM objects, and accesses the converted data.
// Copy all the indices from the domP into my structure.
 
for (int j=0;j<myPoly._iIndexCount-1;j++)
====Invoke Integration Library Registration====
myPoly._pIndexes[j] = poly->getValue()[j];
You defined the <code>intRegisterElements</code> function as described in “Register Integration Libraries with the DOM.” Now you must pass a handle to this function into the <code>DAE::setIntegrationLibrary</code> method.
// Repeat the first index at the end of the list to create a closed loop.
 
myPoly._pIndexes[j] = myPoly._pIndexes[0];
With this step, the elements that you want to convert are registered with the DOM, and are converted from COLLADA DOM objects to application-specific structures when a COLLADA document is loaded, or the reverse when an object is saved.
// Push this polygon into the list of polygons in my structure.
 
local->_vPolygons.push_back(myPoly);
For example, your main application code could pass a handle to the integration library registration function as follows:
}
// Instantiate the reference implementation
// Copy the vertices we are going to use into myGeometry. To keep things simple,
collada_dom = new DAE;
// we will assume there is only one domSource and domFloatArray in the domMesh,
//register the integration objects
// that it is the array of vertices, and that it is in X, Y, Z format. A real
collada_dom->setIntegrationLibrary(&intRegisterElements);
// app would find the vertices by starting with domPolygons and following
 
// the links through the domInput, domVertices, domSource, domFloat_array,
====Parse a COLLADA File====
// and domTechnique.
Now parse a COLLADA document into the run-time COLLADA Object Model. The load step creates the integration objects and invokes the <code>createFrom</code> and <code>fromCOLLADA</code> methods to convert the data from the COLLADA DOM objects into the application-defined data structures.
if(meshElement->getSource_array().getCount() != 1)
//load the COLLADA file
{
int res = collada_dom->load(filename);
fprintf(stderr,
 
"This example supports only one source array per domMesh\n");
====Access a COLLADA Object====
return;
Now we'll find a <code>domGeometry</code> object to get at our application-specific data. We'll use the database for this. Here we request that the database return the first geometry element, placing it into <code>domGeom</code>.
}
 
domSource *source = meshElement->getSource_array()[0];
:'''''Note:''' See [[DOM guide: Working with elements#Querying Database Elements|Querying Database Elements]] for additional details on how to use the database to retrieve COLLADA DOM objects.''
if(source->getFloat_array_array().getCount() != 1)
 
{
//query the runtime to retrieve an element
fprintf(stderr,
domGeometry* domGeom = 0;
"This example supports only one float array per source\n");
int res = collada_dom->getDatabase()->getElement(
©SCEI
  (daeElement**)&domGeom, 0, NULL, COLLADA_TYPE_GEOMETRY);
- 28 -
 
}
====Acquire the Converted Application Object====
domFloat_array *floatArray = source->getFloat_array_array()[0];
In the following code, the <code>myGeometry</code> class represents the application data structure containing geometry data. The <code>domGeometry</code> object retrieved above into <code>domGeom</code> contains a reference to its associated integration object (thanks to its earlier registration). The <code>intGeometry</code> class converts the <code>domGeometry</code> data into a <code>myGeometry</code> instance, which can then be retrieved with the <code>getObject</code> method.
// Assume there are 3 values per vertex with a stride of 3.
// Get the integration object from the element
local->_iVertexCount = floatArray->getCount()/3;
intGeometry *intGeom = (intGeometry*)domGeom->getIntObject();
local->_pVertices = new float[local->_iVertexCount*3];
// Extract the user data from the integration object
// Copy the vertices into my structure one-by-one
myGeometry* myGeom = (myGeometry*)intGeom->getObject();
// (converts from COLLADA's doubles to floats).
 
for ( unsigned int i = 0; i < local->_iVertexCount*3; i++ ) {
==Exporting Using Integration==
local->_pVertices[i] = floatArray->getValue()[i];
The preceding sections showed how to set up your integration objects, import a COLLADA instance document, and ensure that the data is converted into data structures specific to your application.
}
 
Access COLLADA Objects from the Application
If you modify data values and want to write it to a COLLADA instance document, the process is similar to reversing the import-and-convert process, although there are some differences.  
We can now put together the application code that registers the integration libraries, loads the COLLADA
 
data into the in-memory DOM structures, and accesses the converted data.
===Creating a New COLLADA DOM Object Using createTo===
Invoke Integration Libraries Registration
The functionality for creating new COLLADA DOM documents with the integration templates via the <code>createTo</code> method has always been broken, and wouldn't have been very useful even if it did work. Creating new COLLADA documents via the integration templates is now officially deprecated and won't be supported in the future. See [https://collada.org/public_forum/viewtopic.php?t=777&sid=2d247be12d1d1924baa2922a44f956b2 this thread] in the COLLADA forums for more details.
You defined the intRegisterElements() function as described in “Register Integration Libraries with
 
the DOM.” Now you must pass a handle to this function into the DAE::setIntegrationLibrary()
===Exporting Modified Data Values===
method.
If you have modified the values of your application object and want to save to a revised COLLADA instance document, you'll need to update the associated COLLADA DOM objects before saving. This example shows how you might do that.
With this step, the elements that you want to convert are registered with the DOM, and are converted
 
from COLLADA Object Model structures to application-specific structures when a COLLADA instance
When you call <code>save</code>, the DOM calls <code>toCOLLADA</code>  for every COLLADA element with an integration class. In the <code>toCOLLADA</code> method we'll copy the data from our application objects to the COLLADA objects. The code is essentially the reverse of the <code>fromCOLLADA</code> code in the importing example. We make several assumptions about the nature of the changes to make the code simpler. These assumptions are explained in the comments.
document is loaded into the DOM, or the reverse when an object is saved.
 
For example, your main application code could pass a handle to the integration library registration
void intGeometry::toCOLLADA()
function as follows:
{
// Instantiate the reference implementation
    // This code takes data from an application-defined object and
daeObject = new DAE;
    // puts it back into the appropriate collada objects.
//register the integration objects
    // Get a pointer to the COLLADA domGeometry element
daeObject->setIntegrationLibrary(&intRegisterElements);
    // (this is the element that called us)
Parse a COLLADA File
    domGeometry* geometryElement = (domGeometry*)_element;
Now we are ready to parse a COLLADA instance into the run-time COLLADA Object Model. The load
step creates the integration objects and invokes the createFrom() and fromCOLLADA() methods to
    // Get a pointer to the domGeometry's domMesh element
convert the data from the COLLADA Object Model structures into the application-defined structures.
    domMesh *meshElement = geometryElement->getMesh();
//load the COLLADA file
int res = daeObject->load(filename);
    // Get a pointer to my object that's assocated with this collada object
Access a COLLADA Object
    myGeometry *local = (myGeometry *)_object;
With the COLLADA file parsed and loaded into in-memory database objects, it is now possible to request
objects from the database. Here we request that the database return the first geometry element, placing it
    // Get a pointer to the domPolygons in this domMesh.
into pElem.
    // To simplify this example, we will handle only a domMesh
//query the runtime to retrieve an element
    // that has a single domPolygons
int res = daeObject->getDatabase()->getElement
    if(meshElement->getPolygons_array().getCount() != 1)
((daeElement**)&pElem,0,NULL,COLLADA_ELEMENT_GEOMETRY);
    {
©SCEI
        fprintf(stderr, "this example supports only one domPolygons per domMesh\n");
- 29 -
        return;
Acquire the Converted Application Object
    }
In the following code, the myGeometry class represents the application object containing geometry data.
    domPolygons *polygons = meshElement->getPolygons_array()[0];
The COLLADA DAE object retrieved above into pElem contains a reference to its associated integration
object (thanks to its earlier registration). The intGeometry class converts the COLLADA data and returns
    // To simplify this example, we assume that the domPolygons has
the converted data as a myGeometry instance via the getGeometry()method, which was defined in
    // only one domInput.
importGeometry.h.
    if(polygons->getInput_array().getCount() != 1)
// Get the integration object from the element
    {
daeIntegrationObject *pIntegrationObj = pElem->getIntObject();
        fprintf(stderr, "this example supports only one domInput per domPolygons\n");
intGeometry *importGeometry =(intGeometry *)pIntegrationObj;
        return;
//extract the user data from the integration object
    }
myGeometry geom = importGeometry->getGeometry();
Exporting Using Integration
    // Loop over all the polygons in the domPolygons element and
The preceding sections showed how to set up your integration objects, import a COLLADA instance
    // put the data from myGeometry back into it. For the purposes
document, and ensure that the data is converted into data structures specific to your application.
    // of the example, assume the number of polygons and indices
If you modify data values and want to write it to a COLLADA instance document, the process is similar to
    // hasn't changed so we can just update the values in place.
reversing the import-and-convert process, although there are some differences.
    int polygonCount = local->_vPolygons.size();
Figure 6 COLLADA DOM Integration Usage Model for Exporting
    polygons->setCount(polygonCount);
Modifying a COLLADA DOM Object Structure
    for (int i=0;i<polygonCount;i++)
In some cases, your application might modify the structure of your application objects that you loaded
    {
from a COLLADA DOM object. If you want to export the data back to a COLLADA instance document,
        // Get pointer to this polygon (domP)
you must change the structure of the COLLADA DOM object to match your changes before converting
        domPolygons::domP *poly = polygons->getP_array()[i];
from your application-specific structures. The example code does not show how to do this.
        // Copy all the indices from my structure back to the domP
Creating a New COLLADA DOM Object Using createTo
        for (int j=0;j< local->_vPolygons[i]._iIndexCount-1;j++)
In some cases, you might need to export your data to an entirely new COLLADA document, such as when
            poly->getValue()[j] = local->_vPolygons[i]._pIndexes[j] ;
the data originated in your application rather than by being imported from an existing COLLADA
    }
instance document. In this cases, to use the integration methods to convert from your application
structure to a DOM object and then export to a new COLLADA instance document, you must create the
    // Now copy the vertices from myGeometry back to the source.
matching COLLADA DOM structure before you can convert from your application’s structure.
    // Assume that the number of vertices hasn't changed so we can
Compare with the import process: When your application loads a COLLADA instance document, the
    // just update them in place.
COLLADA DOM automatically creates DAE objects in which to load the COLLADA data, then calls your
    if(meshElement->getSource_array().getCount() != 1)
customized createFrom for each element that has an integration class, which creates your
    {
©SCEI
        fprintf(stderr, "this example supports only one source array per domMesh\n");
- 30 -
        return;
application-specific objects. Then it calls fromCOLLADA, which copies the data from the DAE objects to
    }
your application objects.
    domSource *source = meshElement->getSource_array()[0];
If you create a new application object, there are no COLLADA elements (no DAE objects) associated with
it. If you simply save the file, this new data is not written. The purpose of createTo is to create these
    if(source->getFloat_array_array().getCount() != 1)
COLLADA elements and associate them with the application object. createTo is not called automatically
    {
when a new application object is created; you must call it explicitly. After createTo has been called, the
        fprintf(stderr, "this example supports only one float array per source\n");
rest of the saving process is automatic, that is, if you have registered your integration library, when you
    }
call save, toCOLLADA is called for every COLLADA element with an integration class to copy the data
    domFloat_array *floatArray = source->getFloat_array()[0];
from the application objects into the COLLADA objects.
This example code does not show how to do this. The createTo method looks like this:
    // Copy the vertices from myGeometry back into the COLLADA float array
void
    floatArray->setCount(local->_iVertexCount*3);
intGeometry::createTo(void *userData)
    for ( unsigned int i = 0; i < local->_iVertexCount*3; i++ )
{
    {
// This function would create new COLLADA elements from an
        floatArray->getValue()[i] = local->_pVertices[i];
// application- defined object
    }
// It is NOT called automatically by the COLLADA DOM.
}
// The user needs to call this when creating a new
 
// application-specific object.
==Using integration versus using the DOM directly==
}
 
Exporting Data Value Modifications
Integration classes provide a SAX-like approach to using the DOM. The biggest problem with this is that the interdependent links between COLLADA elements have become very complex. Parsing a DOM structure lends itself better to handling these problems. Creating an integration class to process <material>s, for example, may take the same amount of work as accessing the DOM structures directly. Here's a comparison.
If you have modified only the values of the COLLADA data and want to export to a revised COLLADA
 
instance document, this example shows how you might convert the data.
Import <material> with an integration class:
The saving process for this example is automatic because you have registered your integration library.
#Create class that inherits from <code>daeIntegrationObject</code>.
When you call save, toCOLLADA is called for every COLLADA element with an integration class. This
#Implement the <code>fromCOLLADA</code> code to do the conversion.
copies the data from the application objects into the COLLADA ones.
#Implement the <code>fromCOLLADAPostProcess</code> code to do the processing needed to resolve links to other elements, for example, <effect>.
Note that the code is essentially the reverse of the fromCOLLADA code in the importing example.
#Register your integration library with the DOM.
void
 
intGeometry::toCOLLADA()
Import <material> without an integration class:
{
#Get DOM by calling <code>getDOM</code> or <code>getDOMRoot</code> from the <code>daeDocument</code>.
// INSERT CODE TO TRANSLATE TO YOUR RUNTIME HERE
#For each <material> in each <library_materials>, call your conversion function (which would be the same as your fromCOLLADA). Your conversion function can even do the postprocess linking since all the data required by the other elements would be available.
// The following lines are example code from the template:
 
// myRuntimeClassType* local = (myRuntimeClassType*)_object;
Also you can't create COLLADA documents from scratch with an integration library.
// element->foo = local->foo;
 
// element->subelem[0]->bar = local->bar;
DOM structures give you finer-grained control over what to convert; for example, you can spend time converting only the <geometry> elements used by a specific <visual_scene> and not the possibly dozens (or hundreds or thousands) of other <geometry> elements, while the integration classes convert all of them. This is especially useful if you have documents that cross-reference other documents. For example, imagine loading a document that contains a <visual_scene> where all the <instance_geometry> elements reference a <geometry> from a separate document. This document has only one <library_geometries> and contains all the <geometry> elements used for all your scenes, not just the one being loaded. Because the COLLADA DOM, by default, loads external documents to resolve the URIs, the integration classes will convert every <geometry> found in the second document. There could be lots of <geometry> elements and that could take a long time to process. This is especially wasteful since many <geometry> elements won't ever get used in the scene you're loading.
// This code takes data from an application-defined object and
 
// puts it back into the appropriate collada objects.
{{DOM navigation}}
// Get a pointer to the COLLADA domGeometry element
 
// (this is the element that called us)
[[Category:COLLADA DOM|Integration templates]]
domGeometry* geometryElement = (domGeometry*)(domElement*)_element;
// Get a pointer to the domGeometry's domMesh element
domMesh *meshElement = geometryElement->getMesh();
// Get a pointer to my object that's assocated with this collada object
myGeometry *local = (myGeometry *)_object;
// Get a pointer to the domPolygons in this domMesh.
// To simplify this example,
// we will handle only a domMesh that has a single domPolygons
if(meshElement->getPolygons_array().getCount() != 1)
©SCEI
- 31 -
{
fprintf(stderr,
"this example supports only one domPolygons per domMesh\n");
return;
}
domPolygons *polygons = meshElement->getPolygons_array()[0];
int polygonCount = local->_vPolygons.size();
// To simplify this example, we assume that the domPolygons has
// only one domInput
if(polygons->getInput_array().getCount() != 1)
{
fprintf(stderr,
"this example supports only one domInput per domPolygons\n");
return;
}
// Loop over all the polygons in the domPolygons element and
// put the data from
// myGeometry back into it. For the purposes of the example,
// assume the number of polygons and indices hasn't changed
// so we can just update the values in place
polygons->setCount(polygonCount);
for (int i=0;i<polygonCount;i++)
{
// Get pointer to this polygon (domP)
domPolygons::domP *poly = polygons->getP_array()[i];
// Copy all the indices from my structure back to the domP
for (int j=0;j< local->_vPolygons[i]._iIndexCount-1;j++)
poly->getValue()[j] =
local->_vPolygons[i]._pIndexes[j] ;
}
// Now copy the vertices from myGeometry back to the source.
// Assume that the number of
// vertices hasn't changed so we can just update them in place.
if(meshElement->getSource_array().getCount() != 1)
{
fprintf(stderr,
"this example supports only one source array per domMesh\n");
return;
}
domSource *source = meshElement->getSource_array()[0];
if(source->getFloat_array_array().getCount() != 1)
{
fprintf(stderr,
"this example supports only one float array per source\n");
}
domFloat_array *floatArray = source->getFloat_array_array()[0];
// Copy the vertices into from myGeometry back into the
// COLLADA float array
floatArray->setCount(local->_iVertexCount*3);
for ( unsigned int i = 0; i < local->_iVertexCount*3; i++ ) {
floatArray->getValue()[i] = local->_pVertices[i];
}
}

Latest revision as of 23:47, 6 February 2008

When you load a COLLADA XML instance document into the COLLADA DOM, it creates a DOM object for each XML element in the COLLADA document. An application can then use DOM methods to read and manipulate the DOM objects. However, your application might already have its own data structures designed to represent the data coming from COLLADA XML elements and its own tools for manipulating those data structures. In that case, you can implement the COLLADA DOM's integration templates, which, when loading a COLLADA document, convert DOM objects into your application's data structures, and back again when saving. The integration templates cannot be used to create new COLLADA documents based on your runtime data structures.

This article provides a step-by-step example of how to integrate application data structures with the COLLADA runtime infrastructure (COLLADA DOM). Detailed knowledge of the COLLADA DOM architecture is not necessary to understand this example or to successfully integrate your own application-specific data structures.

Overview

The COLLADA Object Model is a set of runtime COLLADA DOM objects that correspond to elements in a COLLADA XML instance document (a COLLADA document). For importing, the COLLADA DOM builds these objects when it parses a COLLADA document. For exporting into a COLLADA document, you need to provide code to build the appropriate DOM objects.

To convert data contained in COLLADA DOM objects into your own application's data structures, the DOM provides integration templates for every object. The integration templates include plug-in points where you can insert conversion code (otherwise, the templates do nothing). After a COLLADA document has been imported by the DOM into COLLADA DOM objects, the DOM calls your conversion code to convert between the DOM objects and the application's data structures.

The following figure shows what happens during a load when integration templates are used.

The basic steps during integration are the following:

  1. Application initializes the COLLADA DOM by creating a DAE object.
  2. Application registers its application-specific integration libraries.
  3. When importing a COLLADA document:
    1. Application begins the import by calling load.
    2. The DOM reads the COLLADA document and places its elements into runtime COLLADA DOM objects.
    3. The COLLADA runtime calls the conversion methods in the integration libraries to convert the content of COLLADA DOM objects into their associated data structures.
  4. When exporting to a COLLADA document:
    1. Application begins the export of a COLLADA DAE structure by calling save.
    2. The COLLADA runtime calls registered conversion methods in the integration libraries to convert the content of the application's data structures into COLLADA DOM objects.
    3. The DOM exports the COLLADA document.

COLLADA DOM Integration Templates

Integration templates are used to convert DOM objects to your run-time data structures. Each DOM object has its own integration template consisting of a header (.h) file and a code (.cpp) file. For example, the DOM object for the COLLADA <node> element is domNode, and the template files to convert domNode objects to or from application data structures are intNode.cpp and intNode.h.

Copy into your application directory the template files for the elements that you want to convert from DOM objects into application-specific data structures. These copies are the starting points for your customized integration libraries. You can then add code to these integration libraries to do the conversion.

The plug-in points for your code are identified in comments in the template source, which is available in the integrationTemplates folder.

Integration Objects

Your integration library provides the code that defines an integration object for each DOM object. The integration object serves as the focal point for all information about the conversion. It defines methods that perform the conversion, and it provides data members that bind the DOM object with the application-specific data structure being converted to.

Integration objects are represented with the daeIntegrationObject class. Every class derived from daeElement provides for an integration object through the data member daeElement::_intObject.

The integration object class for each element is defined with the prefix "int". For example, the domGeometry class provides an intGeometry integration object.

When you define integration code for a DOM object, your code binds the DOM object with your application-specific object, via the _element and _object data members of the integration object. You can use methods daeIntegrationObject::getElement and getObject to access these data members.

When you load a COLLADA instance document, the DOM automatically creates the appropriate integration objects.

To get the integration object for an element, use the method daeElement::getIntObject, which also initiates the conversion process for any objects that have not yet been converted.

Integration Template Plugin Points

The integration class for each DOM object provides six plug-in points into which you can add conversion code. The plug-in points are implemented as methods; you provide the code for the method bodies. You need to implement the body of the methods only for those plug-in points that are relevant to your application.

The methods are:

  • createFrom: Defines the code to create the application-specific data structure associated with the DAE class for this template. This method sets up the integration object for the DAE class.
  • fromCOLLADA: Defines the code to covert a DOM object into your application-specific data structure.
  • fromCOLLADAPostProcess: Defines any post-processing code that must execute after the basic conversion to your data structure.
  • createTo: Defines code to create the DOM object associated with the DAE class for this template if such a structure does not already exist (for example, if it was not created while importing).
  • toCOLLADA: Defines the code to covert the content of your application’s data structures into DOM objects.
  • toCOLLADAPostProcess: Defines any post-processing code that must execute after the basic conversion from your application’s data structure.

Geometry Integration Example

This example demonstrates integration with the <geometry> element.

Creating the example has three basic steps, summarized as follows:

  1. Copy the corresponding integration templates to application-specific versions.
  2. Register these classes with the COLLADA DOM.
  3. In the application runtime, add code to initialize the COLLADA DOM, call the file load, and request application objects from the integration classes.

Copying Integration Templates

The first step in the process of creating conversion code is to copy the relevant integration template files from the integration subdirectory into your application’s directory. For this example, the relevant integration templates are intGeometry.cpp and intGeometry.h. You will modify the copied files to contain your conversion code.

Registering Integration Libraries

For the DOM to create integration objects during parsing or before exporting, you must register the integration classes with the infrastructure. To do this, call the registerElement method on each integration class that you are using. A function to register these classes with the COLLADA DOM is defined in intRegisterElements.cpp, which is also provided in the template directory. You can copy this function into your application's directory, though you should only register those classes that you'll be using, which in this example is intGeometry. The relevant code is shown here:

void intRegisterElements()
{
    intGeometry::registerElement();
}

The integration file intGeometry.cpp provides the definition for the intGeometry class. Its contents are explained in a later step.

After the intRegisterElements function is defined, pass a handle to this function into the DAE::setIntegrationLibrary method, as described in the “Invoking Integration Libraries Registration” section.

Defining Your Application Data Structure

Within the integration library files, provide the code to create your application-specific data structure for the COLLADA DOM objects that you want to convert.

For this example, the application's data structure used to represent geometry is defined in an application header file, in this case, myGeometry.h:

// Definition of application's myPologon class also included here
class myGeometry
{
public:
    unsigned int _iVertexCount;
    float *_pVertices;
    std::vector<myPolygon> _vPolygons;
};

Provide the Plug-in Code to Create an Application Object for Importing

The plug-in method relevant to this step for importing is the createFrom method. Within the createFrom method, add code to create a new myGeometry object, initialize it, and initialize the intGeometry integration object data members. Within intGeometry.cpp:

void intGeometry::createFrom(daeElementRef element)
{
    // create class to hold geometry information and
    // initialize the new object as empty
    _object = new myGeometry();
    _object->pVertices = NULL;
    // set up the _element data member of the integration object
    _element = element;
}

Now, when a COLLADA instance document is loaded and a domGeometry object is encountered, the createFrom method automatically creates a new myGeometry object, because the intGeometry integration class has been registered with the DOM.

Build Conversion Code for Importing

Now add code to the integration library to translate the data stored in a DOM object to its associated application data structure. The fromCOLLADA method is the plug-in point for the basic application-specific conversion code. The fromCOLLADAPostProcess method provides additional flexibility for the conversion process.

Depending on the COLLADA DOM object and the application data structure, the code may vary significantly. For this example, we use the intGeometry::fromCOLLADA method to create new vertex buffers from the COLLADA DOM geometry object.

The following code retrieves the mesh object from the COLLADA domGeometry object:

// Get the geometry element from this integration object
domGeometry* geomElement = (domGeometry*)(domElement*)getElement();
domMesh *meshEl = geomElement->getMesh();

When we have the mesh, we can construct our application-specific data structure iVertices for the myGeometry object. The following code shows how to create the new vertex buffer.

// Get a pointer to the application-defined geometry object that was
// automatically created during load by calling createFrom.
myGeometry *local = (myGeometry *)_object;

// Get a pointer to the domPolygons in this domMesh. To simplify this example,
// we will handle only a domMesh that has a single domPolygons.
if(meshElement->getPolygons_array().getCount() != 1)
{
    fprintf(stderr, "This example supports only one domPolygons per domMesh\n");
    return;
}
domPolygons *polygons = meshElement->getPolygons_array()[0];

// To simplify this example, we assume the domPolygons has only one domInput.
if(polygons->getInput_array().getCount() != 1)
{
    fprintf(stderr, "This example supports only one domInput per domPolygons\n");
    return;
}

// Loop over all the polygons in the domPolygons element
int polygonCount = polygons->getCount();
for (int i=0;i<polygonCount;i++)
{
    myPolygon myPoly;
    // Get pointer to this polygon (domP).
    domPolygons::domP *poly = polygons->getP_array()[i];
    // Get the number of indices from the domP and save it in my structure.
    myPoly._iIndexCount = poly->getValue().getCount();
    // You can modify the data as you copy it from
    // the COLLADA object to your object.
    // Here we repeat the first index in list as the last index,
    // to form a closed loop that can be drawn as a line strip.
    myPoly._iIndexCount++;
    myPoly._pIndexes = new unsigned short[myPoly._iIndexCount];
    // Copy all the indices from the domP into my structure.
    for (int j=0;j<myPoly._iIndexCount-1;j++)
    myPoly._pIndexes[j] = poly->getValue()[j];
    // Repeat the first index at the end of the list to create a closed loop.
    myPoly._pIndexes[j] = myPoly._pIndexes[0];
    // Push this polygon into the list of polygons in my structure.
    local->_vPolygons.push_back(myPoly);
}

// Copy the vertices we are going to use into myGeometry. To keep things simple,
// we will assume there is only one domSource and domFloatArray in the domMesh,
// that it is the array of vertices, and that it is in X, Y, Z format. A real
// app would find the vertices by starting with domPolygons and following
// the links through the domInput, domVertices, domSource, domFloat_array,
// and domTechnique.
if(meshElement->getSource_array().getCount() != 1)
{
    fprintf(stderr, "This example supports only one source array per domMesh\n");
    return;
}
domSource *source = meshElement->getSource_array()[0];

if(source->getFloat_array().getCount() != 1)
{
    fprintf(stderr, "This example supports only one float array per source\n");
}
domFloat_array *floatArray = source->getFloat_array()[0];

// Assume there are 3 values per vertex with a stride of 3.
local->_iVertexCount = floatArray->getCount()/3;
local->_pVertices = new float[local->_iVertexCount*3];

// Copy the vertices into my structure one-by-one
// (converts from COLLADA's doubles to floats).
for ( unsigned int i = 0; i < local->_iVertexCount*3; i++ ) 
{
    local->_pVertices[i] = floatArray->getValue()[i];
}

Access COLLADA Objects from the Application

Now put together the application code that registers the integration libraries, loads the COLLADA elements into the in-memory DOM objects, and accesses the converted data.

Invoke Integration Library Registration

You defined the intRegisterElements function as described in “Register Integration Libraries with the DOM.” Now you must pass a handle to this function into the DAE::setIntegrationLibrary method.

With this step, the elements that you want to convert are registered with the DOM, and are converted from COLLADA DOM objects to application-specific structures when a COLLADA document is loaded, or the reverse when an object is saved.

For example, your main application code could pass a handle to the integration library registration function as follows:

// Instantiate the reference implementation
collada_dom = new DAE;
//register the integration objects
collada_dom->setIntegrationLibrary(&intRegisterElements);

Parse a COLLADA File

Now parse a COLLADA document into the run-time COLLADA Object Model. The load step creates the integration objects and invokes the createFrom and fromCOLLADA methods to convert the data from the COLLADA DOM objects into the application-defined data structures.

//load the COLLADA file
int res = collada_dom->load(filename);

Access a COLLADA Object

Now we'll find a domGeometry object to get at our application-specific data. We'll use the database for this. Here we request that the database return the first geometry element, placing it into domGeom.

Note: See Querying Database Elements for additional details on how to use the database to retrieve COLLADA DOM objects.
//query the runtime to retrieve an element
domGeometry* domGeom = 0;
int res = collada_dom->getDatabase()->getElement(
  (daeElement**)&domGeom, 0, NULL, COLLADA_TYPE_GEOMETRY);

Acquire the Converted Application Object

In the following code, the myGeometry class represents the application data structure containing geometry data. The domGeometry object retrieved above into domGeom contains a reference to its associated integration object (thanks to its earlier registration). The intGeometry class converts the domGeometry data into a myGeometry instance, which can then be retrieved with the getObject method.

// Get the integration object from the element
intGeometry *intGeom = (intGeometry*)domGeom->getIntObject();
// Extract the user data from the integration object
myGeometry* myGeom = (myGeometry*)intGeom->getObject();

Exporting Using Integration

The preceding sections showed how to set up your integration objects, import a COLLADA instance document, and ensure that the data is converted into data structures specific to your application.

If you modify data values and want to write it to a COLLADA instance document, the process is similar to reversing the import-and-convert process, although there are some differences.

Creating a New COLLADA DOM Object Using createTo

The functionality for creating new COLLADA DOM documents with the integration templates via the createTo method has always been broken, and wouldn't have been very useful even if it did work. Creating new COLLADA documents via the integration templates is now officially deprecated and won't be supported in the future. See this thread in the COLLADA forums for more details.

Exporting Modified Data Values

If you have modified the values of your application object and want to save to a revised COLLADA instance document, you'll need to update the associated COLLADA DOM objects before saving. This example shows how you might do that.

When you call save, the DOM calls toCOLLADA for every COLLADA element with an integration class. In the toCOLLADA method we'll copy the data from our application objects to the COLLADA objects. The code is essentially the reverse of the fromCOLLADA code in the importing example. We make several assumptions about the nature of the changes to make the code simpler. These assumptions are explained in the comments.

void intGeometry::toCOLLADA()
{
    // This code takes data from an application-defined object and
    // puts it back into the appropriate collada objects.
    // Get a pointer to the COLLADA domGeometry element
    // (this is the element that called us)
    domGeometry* geometryElement = (domGeometry*)_element;

    // Get a pointer to the domGeometry's domMesh element
    domMesh *meshElement = geometryElement->getMesh();

    // Get a pointer to my object that's assocated with this collada object
    myGeometry *local = (myGeometry *)_object;

    // Get a pointer to the domPolygons in this domMesh.
    // To simplify this example, we will handle only a domMesh 
    // that has a single domPolygons
    if(meshElement->getPolygons_array().getCount() != 1)
    {
        fprintf(stderr, "this example supports only one domPolygons per domMesh\n");
        return;
    }
    domPolygons *polygons = meshElement->getPolygons_array()[0];

    // To simplify this example, we assume that the domPolygons has
    // only one domInput.
    if(polygons->getInput_array().getCount() != 1)
    {
        fprintf(stderr, "this example supports only one domInput per domPolygons\n");
        return;
    }

    // Loop over all the polygons in the domPolygons element and
    // put the data from myGeometry back into it. For the purposes 
    // of the example, assume the number of polygons and indices 
    // hasn't changed so we can just update the values in place.
    int polygonCount = local->_vPolygons.size();
    polygons->setCount(polygonCount);
    for (int i=0;i<polygonCount;i++)
    {
        // Get pointer to this polygon (domP)
        domPolygons::domP *poly = polygons->getP_array()[i];
        // Copy all the indices from my structure back to the domP
        for (int j=0;j< local->_vPolygons[i]._iIndexCount-1;j++)
            poly->getValue()[j] = local->_vPolygons[i]._pIndexes[j] ;
    }

    // Now copy the vertices from myGeometry back to the source.
    // Assume that the number of vertices hasn't changed so we can 
    // just update them in place.
    if(meshElement->getSource_array().getCount() != 1)
    {
        fprintf(stderr, "this example supports only one source array per domMesh\n");
        return;
    }
    domSource *source = meshElement->getSource_array()[0];

    if(source->getFloat_array_array().getCount() != 1)
    {
        fprintf(stderr, "this example supports only one float array per source\n");
    }
    domFloat_array *floatArray = source->getFloat_array()[0];

    // Copy the vertices from myGeometry back into the COLLADA float array
    floatArray->setCount(local->_iVertexCount*3);
    for ( unsigned int i = 0; i < local->_iVertexCount*3; i++ ) 
    {
        floatArray->getValue()[i] = local->_pVertices[i];
    }
}

Using integration versus using the DOM directly

Integration classes provide a SAX-like approach to using the DOM. The biggest problem with this is that the interdependent links between COLLADA elements have become very complex. Parsing a DOM structure lends itself better to handling these problems. Creating an integration class to process <material>s, for example, may take the same amount of work as accessing the DOM structures directly. Here's a comparison.

Import <material> with an integration class:

  1. Create class that inherits from daeIntegrationObject.
  2. Implement the fromCOLLADA code to do the conversion.
  3. Implement the fromCOLLADAPostProcess code to do the processing needed to resolve links to other elements, for example, <effect>.
  4. Register your integration library with the DOM.

Import <material> without an integration class:

  1. Get DOM by calling getDOM or getDOMRoot from the daeDocument.
  2. For each <material> in each <library_materials>, call your conversion function (which would be the same as your fromCOLLADA). Your conversion function can even do the postprocess linking since all the data required by the other elements would be available.

Also you can't create COLLADA documents from scratch with an integration library.

DOM structures give you finer-grained control over what to convert; for example, you can spend time converting only the <geometry> elements used by a specific <visual_scene> and not the possibly dozens (or hundreds or thousands) of other <geometry> elements, while the integration classes convert all of them. This is especially useful if you have documents that cross-reference other documents. For example, imagine loading a document that contains a <visual_scene> where all the <instance_geometry> elements reference a <geometry> from a separate document. This document has only one <library_geometries> and contains all the <geometry> elements used for all your scenes, not just the one being loaded. Because the COLLADA DOM, by default, loads external documents to resolve the URIs, the integration classes will convert every <geometry> found in the second document. There could be lots of <geometry> elements and that could take a long time to process. This is especially wasteful since many <geometry> elements won't ever get used in the scene you're loading.


COLLADA DOM - Version 2.4 Historical Reference
List of main articles under the DOM portal.
User Guide chapters:  • Intro  • Architecture  • Setting up  • Working with documents  • Creating docs  • Importing docs  • Representing elements  • Working with elements  • Resolving URIs  • Resolving SIDs  • Using custom COLLADA data  • Integration templates  • Error handling

Systems:  • URI resolver  • Meta  • Load/save flow  • Runtime database  • Memory • StringRef  • Code generator
Additional information:  • What's new  • Backward compatibility  • Future work
Terminology categories:  • COLLADA  • DOM  • XML