Using accessors

From COLLADA Public Wiki
Jump to navigation Jump to search
Tutorials
This article is one several tutorials, guides, and annotated examples available in this wiki.
Multipage tutorials:  • COLLADA DOM user guide
Shorter how-tos:  • Using accessors  • Schema validation • Using URIs
 • Various annotated examples

Instructions for adding a tutorial

[[Category: ]]


This tutorial about using accessors provides examples of COLLADA <accessor>/<param> use with <source> element arrays.

Overview

Surveys of COLLADA users have shown that the 1.4.1 COLLADA Specification description of how to use <accessor>s and <param>s to read a <source> can be confusing and needs to be clarified. Many people think this is much more complicated than it actually is, so we recommend that everyone read this, as it may save you some work.

How accessors work

To properly read a <source> through an <accessor>, the program has to consider:

  • The data expected by a particular <input> semantic
  • The stride attribute value
  • The offset attribute value
  • The number of <param>s with nonnull names to decide which values to read and which to skip

Semantics in an <input> imply a specific data ordering in a <source>, such as X, Y, Z for positions or R, G, B for colors. If the name attribute in a <param> is missing or empty, the corresponding value in the <source> is skipped.

Note: The text in the name attribute is a user label only and does not affect how the <source> data is read.

If you have a POSITION semantic reading a <source> with three <param>s, it doesn’t matter whether the <param>s are named X, Y, Z or ONE, TWO, THREE: the first value should always be treated as the X position, the second as Y, and the third as Z. If there are fewer <param> elements than the stride for the source, skip the values that are missing <param>s. For example, if you have a stride of 5 and only three <param> elements, you would read three values, skip two, read three more, skip 2, and so on.

Example target data structure

The following examples show how <param>, count, stride, and offset interact. For all of them, assume that your application has the following vertex-map-like structure array that it’s trying to fill with geometry:

struct
{
   float x_pos, y_pos, z_pos, x_norm, y_norm, z_norm, text1_U, tex1_V, tex2_U tex2_V;
} my_array[1000];

Example 1: A simple case

Given a COLLADA <source> and a <triangles> element with <input>s like this:

<source id=test1>
  <float_array name="values" count="9">
    1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
  </ float _array>
  <technique_common>
    <accessor source="#values" count="3" stride=”3”>
      <param name="A" type="float"/>
      <param name="F" type="float"/>
      <param name="X" type="float"/>
    </accessor>
  </technique_common>
</source>

<triangles count=”1”> 
  <input semantic="POSITION" source="#test1" offset=”0”/>
  <p>0 1 2</p>
</triangles>

You can read the data into my_array sequentially. Because the stride of the <accessor> is 3 and all three <param>s have names, the <source> is assumed to contain 3D positions and my_array would be filled in like this:

X_pos Y_pos Z_pos X_norm Y_norm Z_norm Tex1_U Tex1_V Tex2_U Tex2_v
1.0 2.0 3.0
4.0 5.0 6.0
7.0 8.0 9.0

Example 2: Parameter skipping

This is an example of the advanced parameter skipping usage:

<source id=test1>
  <float_array name="values" count="9">
    1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
  </ float _array>
  <technique_common>
    <accessor source="#values" count="3" stride=”3”>
      <param name="A" type="float"/>
      <param type="float"/>
      <param name="X" type="float"/>
    </accessor>
  </technique_common>
</source> 

<triangles count=”1”>
  <input semantic="POSITION" source="#test1" offset=”0”/>
  <p>0 1 2</p> 
</triangles>

Because the second <param> now has no name, it is skipped. With only two named <param>s, the <source> is assumed to contain 2D positions and is read like this:


X_pos Y_pos Z_pos X_norm Y_norm Z_norm Tex1_U Tex1_V Tex2_U Tex2_v
1.0 3.0
4.0 6.0
7.0 9.0

Example 3: Multiple accessors with a single source array

Usually an application creates one <source>/<float_array> for each type of data, for example, a stride-3 array for positions, another for normals, a stride-2 array for texture coordinates, and so on. Most COLLADA documents that you download will be organized this way, so it’s easy to find an example of how this looks.

Some applications may want to pack all the information about a vertex into a single <source> array that is similar to an OpenGL vertex array. This requires creating several <source>s and <accessor>s that all reference data in the same <float_array>. This can be done using only basic COLLADA features, so all COLLADA applications should be able to handle this data.

<source id=positions>
  <float_array name="values" count="30">
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  </ float _array>
  <technique_common>
    <accessor source="#values" count="3" stride=”10”>
      <param name="NAME" type="float"/>
      <param name="VALUES" type="float"/>
      <param name="ARE" type="float"/>
    </accessor>
  </technique_common>
</source>
<source id=normals>
  <technique_common>
    <accessor source="#values" offset=”3” count="3" stride=”10”>
      <param name="NOT" type="float"/>
      <param name="SIGNIFICANT" type="float"/>
      <param name="X" type="float"/>
    </accessor>
  </technique_common>
</source>
<source id=texture1>
  <technique_common>
    <accessor source="#values" offset=”6” count="3" stride=”10”>
      <param name="A" type="float"/>
      <param name="F" type="float"/>
    </accessor>
  </technique_common>
</source>
<source id=texture2>
  <technique_common>
    <accessor source="#values" offset=”8”  count="3" stride=”10”>
      <param name="Q" type="float"/>
      <param name="F" type="float"/>
    </accessor>
  </technique_common>
</source>

<triangles count=”1”>
  <input semantic="POSITION" source="#positions" offset=”0”/>
  <input semantic="NORMAL"   source="#normals" offset=”0”/>
  <input semantic="TEXCOORD" source="#texture1" offset=”0”/>
  <input semantic="TEXCOORD" source="#texture2" offset=”0”/>
  <p>1 2 3</p> 
</triangles>

The first <accessor> tells you to start with the first value in the source, read three values into the position part of your array, skip 7 values, read three more positions, and so on. The second <accessor> begins by skipping 3 values (the offset) then reads 3, skips 7, and so on. An application could also decide to read the entire COLLADA float_array into a 10-column-wide array in memory, then use the <accessor>s as a way to identify what is in each column of the array. Regardless of how you do it, my_array would end up getting filled in like this:

X_pos Y_pos Z_pos X_norm Y_norm Z_norm Tex1_U Tex1_V Tex2_U Tex2_v
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0
21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0

Example 4: Parameters with blank names

This next example produces the same results as the preceding example, but it uses the “skipping <param>s with blank names” approach rather than an offset. This skipping behavior is supported only by advanced programs, so it may be wise to avoid using it if you can.

<source id=positions>
  <float_array name="values" count="30">
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  </ float _array>
  <technique_common>
    <accessor source="#values" count="3" stride=”10”>
      <param name="A" type="float"/>
      <param name="F" type="float"/>
      <param name="X" type="float"/>
    </accessor>
  </technique_common>
</source>
<source id=normals>
  <technique_common>
    <accessor source="#values" count="3" stride=”10”>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param name="A" type="float"/>
      <param name="F" type="float"/>
      <param name="X" type="float"/>
    </accessor>
  </technique_common>
</source>
<source id=texture1>
  <technique_common>
    <accessor source="#values" count="3" stride=”10”>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param name="A" type="float"/>
      <param name="F" type="float"/>
    </accessor>
  </technique_common>
</source>
<source id=texture2>
  <technique_common>
    <accessor source="#values" count="3" stride=”10”>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param type="float"/>
      <param name="F" type="float"/>
      <param name="X" type="float"/>
    </accessor>
  </technique_common>
</source>

<triangles count=”1”>
  <input semantic="POSITION" source="#positions" offset=”0”/>
  <input semantic="NORMAL" source="#normals" offset=”0”/>
  <input semantic="TEXCOORD" source="#texture1" offset=”0”/>
  <input semantic="TEXCOORD" source="#texture2" offset=”0”/>
  <p>1 2 3</p>
</triangles>

After reading this example, you may realize that there are very few occasions where the “skipping <param>s with blank names” behavior would be used, at least with “technique_common” COLLADA data. The parameter skipping is mainly of use for application-defined techniques that may have application-defined semantics where skipping values in the middle of a source array is useful.

An example of how this might be useful in an advanced program might be where an application has defined its own semantic that reads a vertex array's positions and a set of texture coordinates. The <float_array> in the COLLADA document could potentially contain more than one set of texture coordinates, each designed to produce an object with a different appearance. The blank-<param>-skipping behavior could be used to select which set of texture coordinates would be used when drawing primitives for different versions of the object.

Conformance testing note

Although the COLLADA conformance test process is not yet complete, at this time (April 2007) we do not plan to require basic COLLADA programs to support skipping of <param>s with blank or missing name attributes. This behavior is rarely useful for <technique_common> semantics and COLLADA data. Because application-defined techniques and semantics are considered advanced features, skipping <param>s with blank names will also be considered an advanced feature.

Detailed annotated examples

The following examples show how to use the <source>, <accessor>, and <param> elements in COLLADA.

Introducing the elements and a simple case

This example introduces the accessor-related elements and explains how each is used in the simplest case. (Example file: Accessor_1.dae)

<library_geometries>
 <geometry id="triangle-lib" name="triangle">
    <mesh>
       <!--
          This is a simple triangle mesh with 3 typical source inputs, positions, normal, and texcoords.
          There are 3 vertices, 3 normals, and 3 texture coords. 
          The first source consists of a  float_array of 9 floating point values (3 sets of vertices).
          We refer to the float_array as the "source array" below. 
       -->
       <source id="triangle-lib-positions" name="position">
          <float_array id="triangle-lib-positions-array" count="9">
                  -50 -50 0   
                    0  50 0   
                   50 -50 0
          </float_array>
          <technique_common>
             <!--
                The accessor tells the application how to read the source array data into the application. In 
                the accessor below we see that the count is 3. This means that there are 3 sets of XYZ data.
                In other words there are three sets of vertices to read. The stride is also 3, indicating that 
                the source array consists purely of positional data.
                The following chart shows the relationship between the data, the stride, and the Vertex position 
                placement within the data source.           
           
                Data:        -50, -50,   0,    0,  50,   0,   50, -50,   0    
                Stride #:      1    2    3     1    2    3     1    2    3    
                Pos          P0X, P0Y, P0Z,  P1X, P1Y, P1Z,  P2X, P2Y, P2Z, 
                         
                A useful tool to remember is that the count in the source array should always equal the 
                accessor's count * stride.
           
                In this case 3 * 3 = 9
             -->
             <accessor count="3" offset="0" source="#triangle-lib-positions-array" stride="3">
                <param name="X" type="float"></param>
                <param name="Y" type="float"></param>
                <param name="Z" type="float"></param>
             </accessor>
          </technique_common>
       </source>
       
       <!-- The second source consists of a  float_array of 9 floating point values (3 sets of normals). -->
       <source id="triangle-lib-normals" name="normal">
          <float_array id="triangle-lib-normals-array" count="9">
                     0 0 -1  
                     0 0 -1  
                     0 0 -1 
          </float_array>
  
          <technique_common>
             <!--
                The accessor in this source works just the same as in the Position Source. There are three sets
                of normals (one normal for each vertex), and each normal consists of 3 values (X,Y,Z). Again,
                the source array consists purely of normal data (the accessor stride is 3, the accessor count 
                is 3, and the total number of values in the source array is 9). 
             -->
             <accessor count="3" offset="0" source="#triangle-lib-normals-array" stride="3">
                <param name="X" type="float"></param>
                <param name="Y" type="float"></param>
                <param name="Z" type="float"></param>
             </accessor>
          </technique_common>
       </source>
       
       <!--
          The third source is a little different from the first two. It consists of a  float_array of 
          6 floating point values (3 sets of texture coords, one for each vertex), each texture coord 
          only has 2 values (U, V).
       -->
       <source id="triangle-lib-texcoords" name="texcoords">
          <float_array id="triangle-lib-texcoords-array" count="6">
                       0    0  
                     0.5  1.0 
                     1.0  0.0 
          </float_array>
          <!--
             The accessor in this source works in just the same way as the first two except that texture
             coords only require two values per vertex. So, the stride in the source array if now only 2.
             The count is 3, the stride is 2 and the source count is 6 (3*2=6).
          -->
          <technique_common>
             <accessor count="3" offset="0" source="#triangle-lib-texcoords-array" stride="2">
                <param name="U" type="float"></param>
                <param name="V" type="float"></param>
             </accessor>
          </technique_common>
       </source>
         
       <vertices id="triangle-lib-vertices">
          <input semantic="POSITION" source="#triangle-lib-positions"></input>
       </vertices>
       
       <triangles count="1" material="BlueSG">
          <!--
             Notice that the offset in each input is 0. This indicates that the indices in <p> array are 
             the same for each source. This means that vertex 0 has has normal 0 and has texcoord 0.
             Vertex 1 has normal 1 and texcoord 1, and so on.
          -->
          <input offset="0" semantic="VERTEX" source="#triangle-lib-vertices"></input>
          <input offset="0" semantic="NORMAL" source="#triangle-lib-normals"></input>
          <input offset="0" semantic="TEXCOORD" source="#triangle-lib-texcoords"></input>
          <p>0 1 2</p>
       </triangles>
       
    </mesh>
 </geometry>
</library_geometries>

Interleaved data and extracting source streams

This example introduces the concept of interleaved data and shows how the accessor can be used to extract individual source streams. (Example file: Accessor_2.dae)

<library_geometries>
 <geometry id="triangle-lib" name="triangle">
    <mesh>
       <!--
          This is a simple triangle mesh with 3 typical source inputs, positions, normal, and texcoords.
          There are 3 vertices, 3 normals, and 3 texture coords. This example introduces the concept of
          interleaving a source array with both position and normal data. The accessors are reconfigured 
          to extract the interleaved data into separate sources, see below.
          The first source consists of a  float_array of 18 floating point values that define the vertex 
          positions and normals. The order of the data is a vertex triple folowed by a normal triple, e.g.
          P0X, P0Y, P0Z, N0X, N0Y, N0Z, P1X, P1Y, P1Z, .....
       -->
       <source id="triangle-positions-normals" name="position">
          <float_array id="triangle-positions-normals-array" count="18">
                     -50 -50  0  
                       0   0 -1 
                       0  50  0  
                       0   0 -1 
                      50 -50  0
                       0   0 -1
          </float_array>
          <technique_common>
             <!--
                This accessor extracts the vertex position data. Notice that the stride has changed from 3 (as 
                used in the first example) to 6. The count of 3 is the same (i.e. positions still have XY and 
                Z components). Also, notice that the offset is 0. So, the stride is 6, meaning that there are 6 
                values per vertex - 3 for the position XYZs followed by 3 for the normal XYZs. The offset of 0 
                indicates that the position XYZ's are first within each stride.
                
                The following chart shows the relationship between the data, the stride, and the Position and 
                Normal placement within the data source.
                
                Data:        -50, -50,   0,    0,   0,  -1,    0,  50,   0     0,   0,  -1,
                Stride #:      1    2    3     4    4    6     1    2    3     4    5    6
                Pos/Norm     P0X, P0Y, P0Z,  N0X, N0Y, N0Z,  P1X, P1Y, P1Z,  N1X, N1Y, N1Z,
                
                A useful tool to remember is that the count in the source array should always equal the 
                accessor's count * stride.
                
                In this case 3 * 6 = 18
             -->
             <accessor count="3" offset="0" source="#triangle-positions-normals-array" stride="6">
                <param name="X" type="float"></param>
                <param name="Y" type="float"></param>
                <param name="Z" type="float"></param>
             </accessor>
          </technique_common>
       </source>
       
       
       <!--
          The second source does not have its own source data, it re-uses the same interleaved Position-Normals
          source as the Position accessor.
       -->
       <source id="triangle-lib-normals" name="normal">
          <technique_common>
             <!--
                This accessor re-uses the Position-Normals source. Notice that the count and stride are the same
                as those used by the Positions accessor (because it is using the same data!). The offset, however,
                is different, it is 3. This means the normal data starts at position 3 within each stride. 
                
                The chart below shows a stride of 6 data values form the position source. You can see that the 
                normal data starts at an offset of 3 into the source data.
                
                Stride #:      1    2    3     4    4    6  
                Data:        -50, -50,   0,    0,   0,  -1,    
                Pos/Norm     P0X, P0Y, P0Z,  N0X, N0Y, N0Z, 
                Offset:        0    1    2    3    4    5
                
             -->
             <accessor count="3" offset="3" source="#triangle-positions-normals-array" stride="6">
                <param name="X" type="float"></param>
                <param name="Y" type="float"></param>
                <param name="Z" type="float"></param>
             </accessor>
          </technique_common>
       </source>
       
       <!--
          The texcoord accessor has not changed. It consists of a  float_array of 
          6 floating point values (3 sets of texture coords, one for each vertex), each texture coord 
          only has 2 values (U, V).
       -->
       <source id="triangle-lib-texcoords" name="texcoords">
          <float_array id="triangle-lib-texcoords-array" count="6">
                       0    0  
                     0.5  1.0 
                     1.0  0.0 
          </float_array>
          <!--
             Texture coords only require two values per vertex. Therefore, the stride in the source array is 2.
             The accessor count is 3, the accessor stride is 2, and the source count is 6 (3*2=6).
          -->
          <technique_common>
             <accessor count="3" offset="0" source="#triangle-lib-texcoords-array" stride="2">
                <param name="U" type="float"></param>
                <param name="V" type="float"></param>
             </accessor>
          </technique_common>
       </source>
         
                             
       <vertices id="triangle-lib-vertices">
          <input semantic="POSITION" source="#triangle-positions-normals"></input>
       </vertices>
       
       <triangles count="1" material="BlueSG">
          <!--
             Notice that the offset in each input is 0. This indicates that the indices in <p> array are 
             the same for each source. This means that vertex 0 has has normal 0 and has texcoord 0.
             Vertex 1 has normal 1 and texcoord 1, and so on.  Basically, the <p> index array is used for 
             each of the sources.
          -->
          <input offset="0" semantic="VERTEX" source="#triangle-lib-vertices"></input>
          <input offset="0" semantic="NORMAL" source="#triangle-lib-normals"></input>
          <input offset="0" semantic="TEXCOORD" source="#triangle-lib-texcoords"></input>
          <p>0 1 2</p>
       </triangles>
    </mesh>
 </geometry>
</library_geometries>

Skipping parameters

This example introduces the concept of skipping params - another way to deal with an interleaved data input stream. (Example file: Accessor_3.dae)

<library_geometries>
  <geometry id="triangle-lib" name="triangle">
    <mesh>
       <!--
          This is a simple triangle mesh with 3 typical source inputs, positions, normal, and texcoords.
          There are 3 vertices, 3 normals, and 3 texture coords. This example introduces the concept of
          skipping params in the accessor to handle interleaved data.
          The first source consists of a  float_array of 18 floating point values that define the vertex 
          positions and normals. The order of the data is a vertex triple folowed by a normal triple, e.g.
          P0X, P0Y, P0Z, N0X, N0Y, N0Z, P1X, P1Y, P1Z, .....
       -->
       <source id="triangle-positions-normals" name="position">
          <float_array id="triangle-positions-normals-array" count="18">
                     -50 -50  0  
                       0   0 -1 
                       0  50  0  
                       0   0 -1 
                      50 -50  0
                       0   0 -1
          </float_array>
          <technique_common>
             <!--
                This accessor extracts the vertex position data - it is the same as in example 2.
                Notice that the stride is 6. The count of 3 is the same (i.e. positions still have XY and Z 
                components). Also, notice that the offset is 0. So, the stride is 6, meaning that there are 6 
                values per vertex - 3 for the position XYZs followed by 3 for the normal XYZs. The offset of 0 
                indicates that the position XYZ's are first within each stride.
                
                The following chart shows the relationship between the data, the stride, and the Position and 
                Normal placement within the data source.
                
                Data:        -50, -50,   0,    0,   0,  -1,    0,  50,   0     0,   0,  -1,
                Stride #:      1    2    3     4    4    6     1    2    3     4    5    6
                Pos/Norm     P0X, P0Y, P0Z,  N0X, N0Y, N0Z,  P1X, P1Y, P1Z,  N1X, N1Y, N1Z,
                
                A useful tool to remember is that the count in the source array should always equal the 
                accessor's count * stride.
           
                In this case 3 * 6 = 18
             -->
             <accessor count="3" offset="0" source="#triangle-positions-normals-array" stride="6">
                <param name="X" type="float"></param>
                <param name="Y" type="float"></param>
                <param name="Z" type="float"></param>
             </accessor>
          </technique_common>
       </source>
       
       
       <!--
          The second source does not have its own source data, it re-uses the same interleaved Position-Normals
          source as the Position accessor. 
       -->
       <source id="triangle-lib-normals" name="normal">
          <technique_common>
             <!--
                This accessor re-uses the Position-Normals source. Notice that the count and stride are the same
                as those used by the Positions accessor (because it is using the same data!). Note, the offset is set 
                to 0. So, how do we get to the get the Normal data which starts at index 3?  We do this by adding 
                3 new params to the front and by NOT giving them a name. Only params that have a name will actually 
                get read into your application.
                
                The chart below shows a stride of 6 data values form the position source. You can see that the 
                normal data starts at an offset of 3 into the source data.
                
                Stride #:      1    2    3     4    4    6  
                Data:        -50, -50,   0,    0,   0,  -1,    
                Pos/Norm     P0X, P0Y, P0Z,  N0X, N0Y, N0Z, 
                Offset:        0    1    2    3    4    5
                
             -->
             <accessor count="3" offset="0" source="#triangle-positions-normals-array" stride="6">
                <param type="float"></param>
                <param type="float"></param>
                <param type="float"></param>                 
                <param name="nX" type="float"></param>
                <param name="nY" type="float"></param>
                <param name="nZ" type="float"></param>
             </accessor>
          </technique_common>
       </source>
       
       <!--
          The texcoord accessor has not changed. It consists of a  float_array of 
          6 floating point values (3 sets of texture coords, one for each vertex), each texture coord 
          only has 2 values (U, V).
       -->
       <source id="triangle-lib-texcoords" name="texcoords">
          <float_array id="triangle-lib-texcoords-array" count="6">
                       0    0  
                     0.5  1.0 
                     1.0  0.0 
          </float_array>
          <!--
             Texture coords only require two values per vertex. Therefore, the stride in the source array is 2.
             The accessor count is 3, the accessor stride is 2, and the source count is 6 (3*2=6).
          -->
          <technique_common>
             <accessor count="3" offset="0" source="#triangle-lib-texcoords-array" stride="2">
                <param name="U" type="float"></param>
                <param name="V" type="float"></param>
             </accessor>
          </technique_common>
       </source>
         
                             
       <vertices id="triangle-lib-vertices">
          <input semantic="POSITION" source="#triangle-positions-normals"></input>
       </vertices>
       
       <triangles count="1" material="BlueSG">
          <!--
             Notice that the offset in each input is 0. This indicates that the indices in <p> array are 
             the same for each source. This means that vertex 0 has has normal 0 and has texcoord 0.
             Vertex 1 has normal 1 and texcoord 1, and so on.  Basically, the <p> index array is used for 
             each of the sources.
          -->
          <input offset="0" semantic="VERTEX" source="#triangle-lib-vertices"></input>
          <input offset="0" semantic="NORMAL" source="#triangle-lib-normals"></input>
          <input offset="0" semantic="TEXCOORD" source="#triangle-lib-texcoords"></input>
          <p>0 1 2</p>
       </triangles>
       
    </mesh>
  </geometry>
</library_geometries>