DOM guide: Representing elements in the object model

From COLLADA Public Wiki
Jump to navigation Jump to search

The COLLADA Object Model is a set of objects that correspond to elements in a COLLADA XML instance document (a COLLADA document). This article describes what these objects look like and how an application can manipulate them.

Representation of COLLADA Object Model Elements

After an application loads a COLLADA document so that the DOM has created equivalent objects in the COLLADA Object Model, the structure of COLLADA Object Model objects closely matches the element structure found in the original COLLADA document.

Note: The DOM does not perfectly preserve the structure of the original COLLADA instance document; some pieces of information that don’t affect the interpretation of the file may be rearranged. This means that, if an application uses the DOM to read a COLLADA document and immediately write it back out without making any changes, the documents may not be identical, though they will be functionally equivalent. You may notice differences in the order of attributes within an XML tag, or in the precision or output format of floating-point numbers. If an optional attribute is set to its default value, that attribute is suppressed when writing to the output document.

Element Object Names

Every element defined in the COLLADA Specification maps onto a corresponding dom* class, which is derived from the daeElement class. The code generator used to create the C++ classes creates the name of the class based on the element’s XML type. Most elements in COLLADA use an XML tag that is the same as their type. For example, the <camera> element maps onto the domCamera class, the <bool_array> element maps onto the domBool_array class, and the <source> element maps on the domSource class. However, some element's names are different from their types; for example, the <xfov> element maps to the domTargettableFloat class and the <instance_visual_scene> element maps to the domInstanceWithExtra class.

Attributes

Attributes of the element map onto data members of the dom* object. For example, the <geometry> element as defined in the COLLADA Specification includes two attributes, id and name. The domGeometry class includes attrName and attrId data members for the attributes.

Child elements

Children of the element map into data members that are typed to match the type of the child element. When an element can have multiple children of the same type, the data member is an array. For example, the <geometry> element as defined in the COLLADA Specification can have one <mesh> child and any number of <extra> elements as children. The domGeometry class provides an elemMesh data member to hold the object’s domMesh child and an elemExtra_array to hold any number of domExtra child elements.

Reference Counting Classes

To facilitate tracking of dom* objects within the DOM, the DOM uses reference counting classes that correspond to each dom* class. The daeSmartRef<T> class template is used to define reference counting objects for daeElement subclasses. So, for example, the definition:

typedef daeSmartRef<domCOLLADA> domCOLLADARef;

defines domCOLLADARef, which is a reference counted domCOLLADA object. Every dom* class provides one of these reference counting classes. In most cases you do not need to use the domCOLLADARef reference directly; however, whenever a daeElement is placed into the tree using the daeElement::createAndPlace method (described in the following “Adding and Removing Elements” section), the DOM creates the reference counted version of the object.

Note: The DOM makes extensive use of the daeElementRef reference counting classes. If you use a creation method that returns a daeElementRef (such as those in daeMetaElement), be sure to receive the return value into a daeElementRef rather than into a dom* element class pointer. If the value is not returned into a daeElementRef, it can result in incorrect reference counting, which may cause the object to immediately try and delete itself, leaving a pointer to uninitialized memory. Generally you will not want to change the reference count of an object on your own; avoid calling the daeElement::ref or release methods.

Casting Types Using daeSafeCast

The COLLADA DOM provides a C++-style cast operator to safely downcast daeElement and daeElementRef to their strongly-typed dom* equivalent:

template <typename T> inline T *daeSafeCast( daeElement *element ) 

Many functions in the DOM return a reference to the base class daeElement and you must then cast the reference to its specific type for further use. For example:

domInstanceGeometry *instance_geometry; //retrieved from a domNode element
domGeometry *geometry = daeSafeCast< domGeometry >( instance_geometry->getUrl().getElement() );

The daeSafeCast operator returns NULL if the element provided does not match the type specified.

Working with daeTArrays

The DOM uses the templated class daeTArray object as the base class for many of the data members of objects derived from the daeElement class. It is used to represent the links between a parent and a child and to hold lists of other types of data. When you work with a daeTArray object, you need to use different methods to manipulate the array depending on what type of data the array contains.

daeTArrays containing daeElements

When a daeTArray contains something derived from a daeElement, such as a domInput_Array, domParam_Array, or domP_Array:

  • To add elements to the array, call daeElement::createAndPlace on the daeElement containing the daeTArray.
  • To explicitly place an element at a specific position within the array of child elements, use daeElement::createAndPlaceAt, which does the same thing as daeElement::createAndPlace, but places the element at a specific index in the daeTArray.
  • To remove elements from the array, use daeElement::removeChildElement.

daeTArrays containing other objects

When a daeTArray contains something other than an object derived from daeElement:

  • To append a new array element, use the daeTArray::append method.
  • To append the array element only if it doesn’t already exist in the array, use the daeTArray::appendUnique method.
  • To insert a new element at a specific index within the array, use the daeTArray::insertAt method, which grows the array if necessary.
  • To change the value of a specific index, use the daeTArray::set method.
  • To remove an array element, use the daeTArray::remove method to remove the array element by value, or daeTArray::removeIndex to remove the array element at a specific index.

Example

The following example shows how to add data to both types of arrays. In this example, tristrips is a pointer to domTristrips, and we want to add a new triangle strip to its elemP_array. The content of the elemP_array is derived from a daeElement, while the _value array in the elemP_array is a simple list of integers.

// Add a new <p> element. Use createAndPlace because domP is derived from daeElement.
// This adds a new element to domTristrips::elemP_array.
domP* p = daeSafeCast<domP>(tristrips->createAndPlace("p"));
// Then use daeTArray::append to add indices to the domP::_value array, which is a list of domUint.
p->getValue().append3(1, 2, 3);
tristrips->setCount(1);

Note: The count data field found in some DOM objects, such as domTristrips, domPolygons, domTriangles, domAccessor, or domFloat_Array, is not updated automatically when you call createAndPlace or removeChildElement. For example, if you change the number of elements in the elemP_array of a domTristrips, you need to update the count data member yourself. That's the reason for the last line in the previous code excerpt.

Setting and retrieving daeTArray data

The daeTArray class contains many functions to help with the manipulation of arrays of data. These functions include set2, set3, set4, append2, append3, append4, get2, get3, get4, and so on.

To get the data from either type of array, use the get or index operator[] and getCount methods. You can also use the find method to find a specific value within the array.

The following example shows iterating through the polygon groups on a mesh. The object thisMesh is a pointer to domMesh.

int polygonCnt = thisMesh->getPolygons_array().getCount();
for (int currPoly = 0; currPoly < polygonCnt; currPoly++)
{
    domPolygons *thisPoly = thisMesh->getPolygons_array().get(currPoly);
    …
}

Caution: Do not use the daeArray::clear method on any array whose contents are daeElement objects (e.g. domTristrips::elemP_array). This method does not update the _contents array of the containing object, so while it may appear to delete data, the data may still be written to the COLLADA instance document when the database is saved.


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