Thus far, you’ve seen 2D applications that access the frame buffer, 3D applications coded in OpenGL, and 3D games coded with Ogre3D. This chapter doesn’t create any new types of graphics or graphical applications, but shows how to package existing graphics using a powerful file format and object model. This is called the Collaborative Design Activity, or COLLADA.
Packaging graphics isn’t usually an interesting subject, and after all you’ve been through, it may seem like an anticlimax. But COLLADA is an incredibly new and exciting topic in the field of graphics, and given its wide adoption, it’s an important technology to become familiar with. When you know how to read and modify COLLADA files, you’ll be able to share your graphics with others and take the best advantage of COLLADA-compatible tools.
This chapter explains the COLLADA file format in detail and explains how to build applications that access these files. Before getting into the technical details, however, it’s important to come to grips with what COLLADA’ is and why it was created.
Chapter 21, “Building Games with Ogre3D,” explained how graphical objects in Ogre3D are defined with mesh, skeleton, and material files. The mesh file stores the object’s geometric data, the skeleton file stores its animation structure, and the material file stores its rendering parameters. These file formats are specific to Ogre3D.
The files created by proprietary tools contain similar information as Ogre3D files but have completely different formats. Each new format requires new tools for importing, exporting, and converting files to the formats used by other tools. The problem is clear: The more formats there are, the more difficult it is to communicate between them.
Rather than deal with the complexity, many game development houses build custom toolchains from scratch. This removes the need to convert between file formats but requires a great deal of time and effort. And when a new modeling tool or game engine reaches the market, there’s no way to incorporate it into their development process.
Many have recognized the need for a common medium for digitally created content (DCC). In 2003, Sony Computer Entertainment (SCE) discussed this need with a number of other graphic development companies, including Criterion Software and Vicarious Vision. The following year, SCE presented their results at the Association for Computing Machinery’s (ACM’s) Special Interest Group for Graphics and Interactive Techniques (SIGGRAPH).
What SCE demonstrated was a new method of storing and accessing graphic data. In their demonstration, they displayed the content of one vendor with the tools of another. SCE also showed how a single graphic could be portrayed on both the Xbox and PlayStation. The common file format was called the Collaborative Design Activity, or COLLADA.
COLLADA allows applications to access DCC generated by many different tools. Reading XML files can be time-intensive, so most tools won’t access COLLADA’s Digital Asset Exchange (DAE) files directly. Instead, COLLADA data can be placed in a runtime cache that applications can read and write to quickly.
The importance of COLLADA was recognized early on, and companies such as NVidia and Nokia joined the working group. The standard has been revised four times since 2003. The most recent version, 1.4, organizes data related to shading, textures, physics, and animation. COLLADA converters have been developed for many modeling tools including Maya, Blender, LightWave, Ogre3D, and 3ds Max. Google Earth, the globe visualization software, stores its graphical data in COLLADA format.
In addition to the DAE file format, SCE has created libraries whose functions can access and modify data contained in a COLLADA file. This document object model (DOM) makes it easy for applications to read, edit, and store information using the COLLADA format. This chapter examines both the file format and the associated DOM.
The COLLADA format is detailed but straightforward. Like all XML-based documents, it consists of a root element that contains elements that may contain subelements. Elements are organized in a hierarchical structure and their information is delimited by tags (for instance, <TAG></TAG>
). In addition to having parent and child elements, an element may also contain attributes: data defined within the element declaration.
If you followed the previous chapter’s discussion of Ogre3D files, you’ll find the COLLADA file structure easy to understand. You can think of a DAE file as a combination of the Ogre3D mesh, skeleton, and material files. Not only are the concepts similar, but in many cases the COLLADA names are identical to those used by Ogre3D.
All DAE files have the following characteristics:
In addition to the <asset>
element, a DAE file may contain zero or more library elements that identify the graphic’s shape and appearance. For example, a file may have a <library_geometries>
element to define its vertices and/or a <library_materials>
element to identify properties such as reflectance. After the library elements, a file may contain additional elements such as <scene>
and <extra>
.
The <asset>
element of the DAE file provides basic meta information about the content that follows: the title, creator, creation/modification time, version numbers, and associated comments. Table 22.1 lists the possible subelements inside the <asset></asset>
tags. Required subelements are presented in bold.
Table 22.1. Asset Subelements
Subelement | Description |
---|---|
| Name of the author, modeling tool, and other creation information |
| Date/time the document was created: YYYY-MM-DDTHH:MM:SSTZ |
| Date/time the document was last modified: YYYY-MM-DDTHH:MM:SSTZ |
| URI that locates the source data/code for the created content |
| Units of length used in the content and the relationship to the meter |
| Identifies which vector is “up”: |
| Content version: major_num.minor_num.revision_num |
| Name of the overall project for which the content was created |
| Identifies and describes the file’s graphical content |
| Words that identify the content in a search routine |
| Further information about the content and project |
The <contributor>
element is optional, but is usually present to identify the content’s creator and the creation tool. This is the only element in Table 22.1 that contains further subelements, and these are given by the following:
None of these subelements are required.
The <created>
, <modified>
, and <unit>
elements require explanation. The <created>
and <modified>
elements identify exactly when the content was created and last modified. The format is given by the ISO 8601 standard: YYYY-MM-DDTHH:NN:SSTZ.Y stands for year, M stands for month, D stands for day, H stands for hour, N stands for minute, S stands for second, and TZ stands for time zone. For example, if the DCC was created just before the turn of the millennium in Hollywood, the creation date could be identified with the following:
<created>1999-12-31T23:59:59U</created>
T separates the date from the time, and U denotes the standard time zone for the western United States.
The <unit>
element contains attributes related to units of length. If you choose to measure your graphic in feet (1 foot = .3048 m), you’d express this with the following:
<unit name="foot" meter="0.3048" />
The following DAE file contains a complete <asset>
subelement:
<?xml version="1.0" encoding="utf-8"?> <COLLADA version="1.4.0" xmlns="http://www.collada.org/2005/11/COLLADASchema"> <asset> <contributor> <author>Matthew Scarpino</author> <authoring_tool>By Hand</authoring_tool> <comments>COLLADA is cool.</comments> </contributor> <created>2008-03-25T14:21:29U</created> <modified>2008-03-25T21:51:29U</modified> <unit name="millimeter" meter="0.001"/> <title>Chapter22</title> <subject>Example DAE File</subject> </asset> <library_geometries> (Data defining the graphic's shape) </library_geometries> <library_controllers> (Data defining the graphic's structure) </library_controllers> <library_materials> (Data defining the graphic's appearance) </library_materials> </COLLADA>
The <asset>
element contains top-level header information, but the real description of graphic content is contained in the library elements. The rest of this section describes three of them:
<library_geometries>
: Defines the vertices in the object’s mesh
<library_controllers>
: Defines how the mesh vertices relate to bones
<library_materials>
: Defines parameters related to the object’s material rendering
These elements are called libraries because they can be incorporated into separate files that can be accessed with <asset>
elements. This modular usage is similar to that of regular code libraries.
The <library_geometries>
element contains much of the same information as in an Ogre3D mesh file. It holds data related to a graphic’s vertices, triangles, and overall geometry. A <library_geometries>
element has three possible subelements:
<asset>
: Identifies the library metadata (see previous element)
<geometry>
: Contains information about the graphic’s shape
<extra>
: Additional information related to the graphic
The most important of these is the <geometry>
element, which contains the graphic’s geometric definition. It has two optional attributes: name
and id
. The name
attribute is a human-readable label and id
provides a unique identifier for the element.
Only one of <spline>
, <mesh>
, or <convex-mesh>
can be included in a given <geometry>
element. Each defines a different shape:
<spline>
: A curve defined by a polynomial. Includes linear splines, Bézier curves, B-splines, and Nonuniform rational B-splines (NURBS).
<mesh>
: A structure composed of vertices organized into lines, triangles, and polygons.
<convex-mesh>
: Similar to <mesh>
, but may derive its structure from another <geometry>
element.
This presentation focuses on the <mesh>
element, which holds the same type of vertex information as in the Ogre3D ninja.mesh file. The most important subelement of <mesh>
is <source>
.
A <mesh>
usually has multiple <source>
elements, each containing numeric values related to the same set of vertices. One <source>
element might contain vertex positions. while another holds normal vector parameters. A third <source>
element may store texture coordinates. These elements are distinguished by their id
attribute, which is required and must be unique.
Information in <source>
elements usually consists of two parts. The first part holds a numeric array within a subelement that identifies the datatype of the numbers. The second part specifies how the numbers should be accessed. This is contained within <technique_common>
or <technique>
subelements.
There are many possible subelements that hold numeric arrays. <float_array>
, <int_array>
, and <bool_array>
are the most popular. Only one array can be included in a <source>
element. In each case, the only required attribute is count
, which represents the number of values inside the element.
For example, the following <source>
element contains 12 floating-point values. The only required attribute of <source>
is id
, and the only required attribute of <float_array>
is count
:
<source name="Example Source" id="example_source"> <float_array count="12" id="example_array"> 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 </float> </source>
This example doesn’t say anything about the purpose of these numeric values: They could be coordinates or color values or polynomial coefficients. The <source>
element just lists them in order and matches them to an identifier. The interpretation of these values is identified by the <technique>
or <technique_common>
subelement.
A technique specifies the manner in which a graphic should be displayed on a target platform. An object may need to be rendered one way on mobile platforms and another way on personal computers. For each method of presentation, you should have a separate <technique>
subelement under the <source>
. In many cases, however, you’ll provide only a single technique that applies to all platforms. This is identified with <technique_common>
.
As a child of <source>
, the <technique_common>
element contains a single child of its own: <accessor>
. This defines how the values in the array should be accessed. It does this by partitioning the source array into subarrays with specific names. For example, the following code defines the same 12-element array as before, but the accessor divides the values into four subarrays with three elements (x, y, and z) each:
<source name="Example Source" id="example_source"> <float_array count="12" id="example_array"> 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 </float> <technique-common> <accessor count="4" source="#example_source" stride="3"> <param name="x" type="float"/> <param name="y" type="float"/> <param name="z" type="float"/> </accessor> </technique-common> </source>
The <technique_common>
element has two required attributes: source
and count
. The first defines the source element whose numeric arrays will be accessed. Notice that the name of the source element is preceded with a #
. This states that the text that follows is an id
value. The count
attribute identifies how many subarrays the source array will be divided into.
The stride
attribute isn’t required, but it shows how many array elements should be accessed at a time. With each access, the array elements are assigned names identified in the param
declarations. In the example, the array is accessed three elements at a time, and the floating-point values are named x
, y
, and z
in succession.
Many of the other <mesh>
subelements identify how the <source>
values should be used. The <vertices>
subelement matches subarrays in the <source>
element with coordinates in the mesh. It has one required attribute, id
, and must have at least one child element, <input>
.
The <input>
element identifies the <source>
of the numeric values and states that they represent positions within the mesh. The following <vertices>
element accesses the values in the example_source
element and identifies them as vertex positions:
<vertices id="example_vertices"> <input semantic="POSITION" source="#example_source"/> </vertices>
Each <input>
inside a <vertices>
element must have a semantic
attribute equal to POSITION
and a source
attribute equal to a defined source element. It may also have an optional offset
attribute that identifies a position inside the array. A <vertices>
element may have multiple <input>
elements identifying different <source>
elements.
Before proceeding further, let’s summarize the elements discussed so far:
The <source>
element defines an array of numeric values.
The <source>
element contains a <technique_common>
element that partitions the numeric values into groups.
The <vertices>
element identifies the numeric groups in the <source>
element as being coordinates of vertices in a mesh.
The vertices can now be combined to create shapes, such as lines, triangles, and polygons. This is discussed next.
Chapter 20, “OpenGL on the Cell: Gallium and Mesa,” explained how OpenGL creates shapes from vertices. COLLADA shapes are similar, and are defined by one of the following:
<lines>
: Vertices are accessed in twos and form separate lines.
<linestrips>
: Each new vertex forms a connected line.
<triangles>
: Vertices form separate triangles.
<tristrips>
: Each new vertex forms a triangle.
<trifans>
: Each new vertex forms a triangle connected at the first point.
<polygons>
: Vertices form multisided shapes that may contain holes.
<polylist>
: Vertices form multi-sided shapes that cannot contain holes.
These shape elements all have the same three attributes: name
, count
, and material
. Only count
is required, and states how many shapes will be constructed. material
is optional and identifies a defined material. If the mesh material isn’t defined, properties like light and shading must be defined elsewhere.
Each shape element has at least three subelements: <input>
, <p>
, and <extra>
. The <input>
subelement has the same attributes as the <input>
subelement inside <vertices>
. But the offset
attribute is required and possible values for semantic
include VERTEX
, COLOR
, NORMAL
, TEXCOORD
, TEXTURE
, TANGENT
, BINORMAL
, and UV
. Different values of semantic
refer to different <source>
elements, and a shape element may contain multiple <input>
values to represent different vertex data.
The <p>
subelement creates shapes from vertices. Each vertex is assigned an index, and the <p>
element lists indices in the order in which they form the named shape. An example will clarify how <p>
is used. Let’s say you want to create two triangles from five vertices: Triangle 1 consists of P0, P1, and P2, and Triangle 2 consists of P1, P3, and P4. The <triangles>
element would be declared as follows:
<triangles count="2"> <input offset="0" semantic="VERTEX" source="vertex_data"> <p>0 1 2 1 3 4</p> </triangles>
Instead of listing all the indices in a single <p>
element, a separate <p>
subelement can be declared for each shape. For this simple example, the triangles are created with the following:
<triangles count="2"> <input offset="0" semantic="VERTEX" source="vertex_data"> <p>0 1 2</p> <p>1 3 4</p> </triangles>
To show how a shape element accesses multiple inputs, the next <triangles>
element associates each vertex index with an index into an array of normal data. Here, the vertices are defined in the vertex_data
source, and the normal data is defined in the normal_data
source:
<triangles count="2"> <input offset="0" semantic="VERTEX" source="vertex_data"> <input offset="1" semantic="NORMAL" source="normal_data"> <p>0 0 1 1 2 3</p> <p>1 4 3 5 4 6</p> </triangles>
There are two inputs and twice as many values in the <p>
elements. In this case, the <p>
values are grouped in twos: The first value in each group is taken from the input with offset
0, and the second is taken from the input with offset
1. Each vertex index is matched with a corresponding normal vector.
The shape determines how many vertices are accessed at a time, but polygons may have an arbitrary number of points. For this reason, the vertex indices are separated into individual <p>
elements rather than being grouped into a single list. For example, the following element defines two polygons, the first consisting of five vertices and the second consisting of seven:
<polygons count="2"> <input offset="0" semantic="VERTEX" source="vertex_data" /> <p>0 3 2 4 1</p> <p>3 7 5 8 6 9 10</p> </polygons>
If the polygon contains holes, the <ph>
element is used. This contains subelements <p>
and <h>
, where <p>
represents the vertices of the polygon and <h>
represents vertices of the hole.
The last shape element is <polylist>
, which differs from <polygons>
in two respects. First, it identifies how many vertices are in each polygon with the <vcount>
subelement. This makes it possible to specify all the vertices in a single list. The following code shows how a <polylist>
can declare the same two polygons as those declared previously:
<polylist count="2"> <input offset="0" semantic="VERTEX" source="vertex_data" /> <vcount>5 7</vcount> <p>0 3 2 4 1 3 7 5 8 6 9 10</p> </polylist>
The second difference between <polylist>
and <polygons>
is that no holes are allowed in a <polylist>
declaration.
Ogre3D skeleton files define bones and joints in an object’s skeleton. In COLLADA, this information is contained in a <library_controllers>
element. This element contains one or more <controller>
elements.
The primary subelements of <controller>
are <skin>
and <morph>
. The <skin>
subelement contains much of the same information as in an Ogre3D skeleton file, such as joint identification and vertex weighting. The <morph>
subelement is completely new, and contains information related to animating individual vertices.
Ogre3D skeleton files group vertices and polygons into bones, which move as individual objects during animation. A bone’s position is determined by the joint that connects it with its parent bone. The entire parent-child bone hierarchy forms the object’s skeleton. The vertices and polygons covering the skeleton form its skin.
The goal of the <skin>
element is to show how vertices combine to form bones and how the bones move. It’s important to understand the coordinates and transformations involved. An object’s bind shape is formed of the vertices that make up its skin. The inverse bind matrix transforms a vertex’s world coordinates so that it can be expressed in the local coordinates used by its joint.
The bind shape matrix transforms the local coordinates so that the vertices move as their joints move. This is identified with the optional <bind_shape_matrix>
element, which is set to the identity matrix by default. Both matrices have to be identified for animation to work properly.
Like the <mesh>
element described earlier, the <skin>
element places its numeric values inside <source>
elements. At least three <source>
elements are required for each <skin>
element:
A <source>
element that lists the names of the joints
A <source>
element that identifies the weight of each vertex
A <source>
element that defines inverse bind matrices
These values are accessed by the other <skin>
subelements: <joints>
and <vertex_weights>
.
The <joints>
element creates an association between the names of the joints and their attributes. At the very least, it has to match joint names to inverse bind matrices. This is shown with the following code:
<joints> <input semantic="JOINT" source="#example_joints" /> <input semantic="INV_BIND_MATRIX" source="#example_ibms" /> </joints>
As the skeleton moves, multiple bones may influence a vertex’s transformation. This information is provided by the <vertex_weights>
element. The numeric weights are identified in a <source>
element and accessed through an <input>
element. A second <input>
element must identify the <source>
element holding the names of the joints.
The <vertex_weights>
attribute has a required count
attribute that specifies the number of vertices. For each vertex, the <vcount>
element contains the number of bones that influence it. Finally, the <v>
parameter matches the bone index with the weight index. If the bone index is −1, the weight is matched to the entire body pose.
For example, the following declaration identifies weighting for three vertices:
<vertex_weights count="3"> <input semantic="JOINT" source="#example_joints" /> <input semantic="WEIGHT" source="#example_weights" /> <vcount>3 2 4</vcount> <v>-1 0 1 1 4 2 -1 3 2 4 -1 5 2 6 3 7 4 8</v> </vertex_weights>
The first vertex is affected by Bones −1, 1, and 4; the second is affected by Bones −1 and 2; and the third is affected by −1, 2, 3, and 4. Each influence is identified by sequential weights indexed from zero to eight.
Morphing is similar to animation, but transforms each vertex separately, without bones or a skeleton structure. This process interpolates values between a base mesh and a target mesh, and transforms each vertex of the base mesh until they form the target mesh. The nature of the transformation is affected by the base mesh, the target mesh, and vertex weighting.
There are two main methods of morphing vertices. The first, called normalized morphing, normalizes vertex weights so that their sum equals one. Then it performs the transformation using the following equation:
The relative method is simpler, and is given by the following formula:
The <morph>
element sets up the transformation by matching target meshes and vertex weights. One attribute, method
, identifies the morphing method, and source
names the symbol corresponding to the base mesh. It also has three subelements: <source>
, <targets>
, and <extra>
. The <targets>
element contains at least two <input>
elements. This is shown in the following declaration:
<morph method="normalized" source="#example_base"> <input semantic="MORPH_TARGET" source="#example_target" /> <input semantic="MORPH_WEIGHT" source="#example_weight" /> </morph>
In this example, the morphing process transforms the vertices in example_base
mesh into the example_target
mesh using the normalized method and the weights in the example_weight
source element.
Ogre3D material scripts define visual properties like an object’s reflectance, texture, and shading. A COLLADA file provides the same information with the <library_materials>
element. This consists of one or more <material>
elements.
Each <material>
element defines material properties with a single required <instance_effect>
. The <instance_effect>
has a url
attribute that identifies the location of the object to be rendered. It also has two important subelements: <technique_hint>
and <setparam>
. The first identifies target platforms and profiles, and the second specifies material parameters. Both are optional.
Before defining the rendering properties of a material, it’s important to specify what rendering platform should be used. The <technique_hint>
subelement identifies a platform that the material properties are intended for. A <material>
element may have more than one <technique_hint>
to identify multiple suitable platforms.
The <technique_hint>
element has three attributes and no subelements. The attributes are as follows:
platform
: Name of the intended platform
ref
: Unique identifier for the technique hint
profile
: Name of the renderer/shader to be used
Only the ref
attribute is required. Examples of profile
values include profile_COMMON
, profile_CG
, profile_GLES
, and profile_GLSL
. In each case, the attribute’s value is set to the profile name without the profile_
. This is shown in the following declarations:
<technique_hint platform="PS3" ref="texture" profile="CG"/> <technique_hint ref="shader" profile="GLSL"/>
In this example, profile_CG
should be used for texture on the PS3, and profile_GLSL
should be used for shading.
<setparam>
assigns a value to a previously identified parameter. The name of the parameter is identified by the ref
attribute, which is required for all <setparam>
elements. Its value is specified by placing numbers or characters between tags that represent datatypes. Table 22.2 lists the datatypes available for parameter values.
Table 22.2. <setparam>
Parameter Datatypes
Datatype | Description |
---|---|
| Boolean (true/false) value |
| Array of |
| Integer value |
| Array of |
| Floating-point value |
| Array of |
| Two-dimensional array of floating-point values, where |
| Source of texture samples |
| One-dimensional texture sampler |
| Two-dimensional texture sampler |
| Three-dimensional texture sampler |
| Texture sampler for cube maps |
| Texture sampler for two-dimensional maps |
| Texture sampler for depth maps |
| Enumerated value |
The parameter can be set to a single value or an array of values. For numbers, one-dimensional arrays are identified with a number following the datatype (for instance, int3
), and two-dimensional arrays are identified with two numbers following the datatype, such as float3x2
. Parameters can also be set to previously declared objects, such as surfaces and samplers.
As an example, the following <setparam>
declaration assigns the array (0.25 0.50 0.75) to the parameter emit_color
:
<setparam ref="emit_color"> <float3>0.25 0.50 0.75</float> </setparam>
The COLLADA standard provides for a number of other libraries and elements, and many of these are needed to fully flesh out an object’s properties and rendering. For a thorough treatment of the subject, I strongly recommend COLLADA: Sailing the Gulf of 3-D Content Creation, by Remi Arnaud and Mark Barnes.
Many conversion utilities are available to transform digitally created content into the COLLADA format. However, if you’re designing games, you’ll need to access this content in code. This section explains how to obtain the COLLADA libraries and use them to build applications.
Before you can use the COLLADA libraries, you need to install two dependencies: Perl Compatible Regular Expressions (PCRE) and the XML library. To install these modules, enter the following at the command line:
yum install pcre pcre-devel libxml2 libxml2-devel
To access DAE elements in code, the COLLADA libraries must be compiled from source. The source code for the COLLADA project is hosted at SourceForge: http://sourceforge.net/projects/COLLADA-dom. Click the Download link and save the archive to your system.
Change to $COLLADA/dom and enter make
. This creates a folder called build and a directory structure beneath it. At the time of this writing, the library libcollada14dom can be found in $COLLADA/dom/linux-1.4. This library provides the routines that make it possible to access COLLADA elements in code.
There are at least two ways to make sure this library is included into the build. You can add a line to /etc/ld.so.conf identifying the directory containing libcollada14dom. Then run ldconfig
to update the dynamic loader cache.
Alternatively, you can update the LD_LIBRARY_PATH
environment variable. Either enter the following command at the command line
export LD_LIBRARY_PATH=$COLLADA/dom/linux-1.4:$LD_LIBRARY_PATH
or permanently change the LD_LIBRARY_PATH
environment variable in .bashrc.
Let’s start with a simple example. The code in Listing 22.1 performs three operations. It reads a DAE file’s contents into the runtime database, displays the number of documents in the database, and then frees memory. The application is trivial, but it provides insight into how the basic DAE classes operate.
Example 22.1. Introduction to the COLLADA API: dae_basic.cpp
#include <iostream> #include <dae.h> using namespace std; int main(int, char **) { unsigned int docnum; /* Create a DAE object */ DAE* dae = new DAE; /* Load file content and access the database */ dae->load("example.dae"); dae->setDatabase(0); /* Insert document into database */ daeDocument *daeDoc = NULL; dae->getDatabase()->insertDocument("example.dae", &daeDoc); /* Obtain and display number of documents */ docnum = dae->getDatabase()->getDocumentCount(); cout << "Document count: " << docnum << endl; /* Unload document and deallocate DAE */ dae->unload("example.dae"); delete dae; return 0; }
This application relies on three objects:
DAE
: Loads and saves documents, accesses the database
daeDatabase
: Provides access to documents and elements
daeDocument
: Represents a document in the database
This presentation discusses each of these classes and then explains how to analyze the document with the COLLADA Document Object Model (DOM).
As shown in Listing 22.1, the primary object in the COLLADA API is the DAE
. This opens the COLLADA file and creates a document object with the same name. The XML elements in the document are cached inside a runtime database, which may contain elements of one or more documents. The elements can then be read, written to, and saved to a file. Table 22.3 lists some of the DAE
functions that make this possible.
Table 22.3. Functions of the DAE
Class
Description | |
---|---|
| Creates a document from the specified URI |
| Unloads the named document |
| Unloads all documents in the database |
| Stores the named document to its original URI |
| Stores the indexed document to its original URI |
| Stores the named document to the named URI |
| Stores the indexed document to the indexed URI |
| Returns a pointer to the |
| Specifies the |
The first seven functions in the table are concerned with documents stored in the runtime database. They can be accessed by name or index, and can be loaded, unloaded, or saved as needed. The save
function stores the document to the URI it was extracted from, and saveAs
saves it to a new URI.
getDatabase
returns a pointer to the runtime database. This is encapsulated within a daeDatabase
object whose functions access the list of documents and their elements. The default database can be accessed by calling setDatabase
with 0
or NULL
as the argument:
dae->setDatabase(0);
The database can now be used to create documents and analyze their structure.
The daeDatabase
class provides access to documents stored in the database. These documents can be inserted, as shown in the code in Listing 22.1, or directly created inside the database. In addition, the database also provides access to the elements inside the documents. Table 22.4 presents the functions that perform these tasks.
Table 22.4. Functions of the daeDatabase
Class
Datatype | Description |
---|---|
| Creates the named document in the database and returns a pointer ( |
| Creates the named document into the database, sets the root of the document to |
| Inserts an existing document into the database |
| Inserts the named document into the database and returns a pointer ( |
| Inserts the named document into the database, sets the root of the document to |
| Removes the document from the runtime database |
| Removes all documents from the runtime database |
| Returns a pointer to the named document |
| Returns a pointer to the indexed document |
| Returns the name of the indexed document |
| Returns the number of inserted documents |
| Returns whether the named document is in the database |
| Inserts an element in the document into the runtime database |
| Removes an element in the document out of the runtime database |
| Returns the number of elements given the input parameters (which can all be null) |
| Sets the input pointer to the |
Most of these functions deal with daeDocument
objects: creating, inserting, accessing, and removing. A document corresponds to a DAE file, but may be created in other ways. A database’s documents can be accessed by name or by index.
Just as a DAE file is composed of XML elements, a daeDocument
is composed of daeElement
s. These daeElement
s correspond directly to the file’s XML elements and are structured within the same hierarchy. This hierarchy is explored further in the next section, but it’s important to know that each document has a single root element. This is the daeElement
parameter referenced by the createDocument
and insertDocument
functions.
The getElementCount
and getElement
functions require explanation. The first function returns the number of elements that meet the given criteria. The input parameters are given as follows:
elementName
: The name
or id
attribute of the XML element
elementType
: The type of the XML element, such as mesh
or input
doc
: The name of the document or file, such as example.dae
Each of these parameters can be set to NULL
. This makes the search less restrictive.
The getElement
function has the same search parameters as getElementCount
, but sets the first parameter to point to the indexed element that meets the criteria. These two functions commonly work together: getElementCount
determines how many elements meet the search parameters, and getElement
searches for an element in a loop until the right element is found. This is shown in the following code, which searches for all animation
elements in the database:
/* Find the number of elements */ unsigned int numElements = dae->getDatabase()->getElementCount(NULL, "animation", NULL); unsigned int i; for(i=0; i<numElements; i++) { /* Access each element object */ dae->getDatabase()->getElement((daeElement**)&element, i, NULL, "animation", NULL); /* Process element */ }
Instead of using the actual element name (animation
in the preceding code), it’s better to access one of the constants in domConstants.h. For example, animation in the code above can be replaced by the COLLADA_ELEMENT_ANIMATION
constant, which has the same value.
The daeDocument
class doesn’t provide as many functions as DAE
or daeDatabase
, but its getDomRoot
function is central to this discussion. This returns a domCOLLADA
object that represents the top of the element hierarchy. This object can then be used to read and modify the information stored in the document.
Table 22.5 lists many of the domCOLLADA
functions that make this possible. Function names preceded by (1)
belong to the daeElement
class, the superclass of domCOLLADA
.
Table 22.5. Functions of the domCOLLADA/daeElement
Classes
Datatype | Description |
---|---|
| Sets the namespace attribute ( |
| Returns the |
| Returns the |
| Returns a DOM object corresponding to the |
| Returns a |
| Returns an |
| Creates the element with the specified name |
| Adds the specified element as a child |
| Creates the element and adds it as a child |
| Adds the specified element as a child at the specified index |
| Creates the element and adds it as a child at the specified index |
| Adds the specified element as a child before the |
| Adds the specified element as a child after the |
| Removes the specified element as a child |
| Returns whether the element has the specified attribute |
| Returns a |
| Returns a pointer to the attribute value |
| Sets the attribute |
| Specifies the element’s |
| Returns a |
| Returns a |
The domCOLLADA
functions provide access to the top-level elements of a DAE file: <asset>
, <library_
XXX
>
, <scene>
, and <extras>
elements. getLibrary_
XXX
refers to any of the possible library elements, including <library_geometries>
, <library_controllers>
, or <library_materials>
. In each case, the object returned is specific to the element. For example, getAsset
returns a domAssetRef
object, and getScene
returns a domSceneRef
.
The functions of the daeElement
class (superclass of domCOLLADA
), provide many more capabilities for dealing with a document’s elements. Most of them are variations of createElement
or placeElement
, and it’s important to understand the distinction. createElement
constructs a new daeElement
object, but placeElement
makes it a child of the element. In many cases, you’ll use a function that combines both tasks, such as createAndPlaceElement
.
The last functions in the table deal with an element’s attributes. General attributes can be accessed with getAttributeValue
or setAttribute
. Specific attributes, such as an element’s name or ID, can also be accessed with getElementName
, setElementName
, and getID
.
The daeElement
class has no functions that read/write an element’s value. For example, you can set the name
attribute of a float_array
element with setElementName
, but you can’t use any of these functions to change the actual floating-point values. Only specific types of elements have values, and only specific subclasses of daeElement
have the setValue
function available. This is shown in Listing 22.2, which creates a new DAE file whose <created>
and <modified>
element values are defined in code.
Example 22.2. Creating a DAE Document in Code: dom_create.cpp
#include <iostream> #include <dae.h> #include <dom/domCOLLADA.h> #include <dom/domAsset.h> using namespace std; int main(int, char **) { /* Create document and insert into database */ DAE* dae = new DAE; dae->setDatabase(0); daeDocument* daeDoc = NULL; dae->getDatabase()->insertDocument("new.dae", &daeDoc); /* Get root element of document and create asset */ domCOLLADA* dom = (domCOLLADA*)daeDoc->getDomRoot(); domAsset* asset = (domAsset*)dom->createAndPlace("asset"); /* Set values for the created and modified elements */ domAsset::domCreated* created = (domAsset::domCreated*)asset-> createAndPlace("created"); created->setValue("2008-03-25T14:21:29U"); domAsset::domModified* modified = (domAsset::domModified*)asset-> createAndPlace("modified"); modified->setValue("2008-03-25T21:51:29U"); /* Unload document and deallocate DAE */ dae->save("new.dae"); delete dae; return 0; }
This application does not access an existing file. Instead, it constructs a daeDocument
object, inserts it into the database, and adds content to the document. It adds content by accessing the DOM root and creating a domAsset
. This represents the <asset>
declaration in a DAE file. Then it calls createAndPlace
to add two child elements: a domCreated
and a domModified
. Both of these classes have a setValue
function, and the application calls this to set the document’s creation and modification date.
After the document’s elements and subelements have been created, the code saves the document to a file called new.dae. The content of new.dae is shown here:
<?xml version="1.0"?> <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1"> <asset> <created>2008-03-25T14:21:29U</created> <modified>2008-03-25T21:51:29U</modified> </asset> </COLLADA>
As you can see, the application automatically generated attributes for the <?xml>
and <COLLADA>
elements. These do not have to be specifically identified in code.
The COLLADA methodology makes it possible to exchange digital content between modelers, renderers, and other graphical tools. The structure of a COLLADA document is involved, with many library elements, attribute formats, and custom datatypes. But a single digital asset exchange (DAE) file can be used across a broad field of applications.
The first part of this chapter presents the XML-based structure of DAE files. Each file must start with an <asset>
element that identifies metadata such as the file’s author, authoring tool, and time of the file’s creation and modification. Afterward, DAE files provide graphic information in a series of library elements. This chapter has discussed the <library_geometries>
, <library_controllers>
, and <library_materials>
elements, but the COLLADA standard defines many more.
In many cases, it’s easier to analyze DAE files as C++ objects than as text files. This is made possible through the COLLADA API, which is discussed in the second part of this chapter. The classes are simple to understand: The DAE
is the primary object for accessing files and the daeDatabase
stores documents. Using the COLLADA DOM, you can read and modify the information in these documents.
This chapter has covered only the rudiments of COLLADA. There are many more library elements and elements for configuring scenes in DAE files. Further, the COLLADA API provides many more classes and functions than those described here. For more information about this subject, you can find excellent documentation in the $COLLADA/doc directory.
3.17.110.58