Celestia/3D Models/CMOD File

From Wikibooks, open books for an open world
Jump to: navigation, search

Introduction[edit]

Celestia's internal Model object consists of a set of "materials" followed by a set of "meshes". Each "mesh" consists of a list of vertex definitions followed by several "groups". Each "group" consists of a specification of the type of "group" that it is, followed by an enumeration of previously defined vertices belonging to the group. This structure exactly corresponds to the items contained in a CMOD file.

BN description of CMOD format[edit]

The following description has been adapted from comments in Celestia's source code.

This is an approximate Backus Naur form for the contents of ASCII cmod files. For brevity, the categories <unsigned_int> and <float> aren't defined here--they have the obvious definitions.

 <modelfile>           ::= <header> <model>
              
 <header>              ::= #celmodel__ascii
              
 <model>               ::= { <material_definition> } { <mesh_definition> }
              
 <material_definition> ::= material
                           { <material_attribute> }
                           end_material
 
 <material_attribute>  ::= diffuse  <color>  |
                           specular <color>  | 
                           emissive <color>  | 
                           specpower <float> | 
                           opacity   <float> |
                           texture0 <string> |
                           texture1 <string> |
                           blend add |             (v1.5.0 and later)
                           normalmap   <string> |  (v1.5.0 and later)
                           specularmap <string> |  (v1.5.0 and later)
                           emissivemap <string>    (v1.5.0 and later)

 <color>               ::= <float> <float> <float>
              
 <string>              ::= """ { letter } """
             
 <mesh_definition>     ::= mesh
                           <vertex_description>
                           <vertex_pool>
                          { <prim_group> }
                           end_mesh
 
 <vertex_description>  ::= vertexdesc
                           { <vertex_attribute> }
                           end_vertexdesc

 <vertex_attribute>    ::= <vertex_semantic> <vertex_format>
              
 <vertex_semantic>     ::= position | normal | color0 | color1 | tangent |
                           texcoord0 | texcoord1 | texcoord2 | texcoord3
 
 <vertex_format>       ::= f1 | f2 | f3 | f4 | ub4
              
 <vertex_pool>         ::= vertices <count>
                           { <float> }
 
 <count>               ::= <unsigned_int>
              
 <prim_group>          ::= <prim_group_type> <material_index> <count>
                           { <unsigned_int> }
 
 <prim_group_type>     ::= trilist | tristrip | trifan |
                           linelist | linestrip | points | 
                           sprites    (v1.5.0 and later)
              
 <material_index>      :: <unsigned_int> | -1

Clarification of some of the elements[edit]

In Celestia v1.5.0 or later, a full Material definition would look like

material
diffuse 0.5 0.5 0.2
specular 1 1 1
specpower 30
opacity 1
texture0 "basetex.jpg"
normalmap "norm.png"
specularmap "specmask.jpg"
emissivemap "lights.jpg"
end_material

Some of the vertex attributes are

  • position - position (required)
  • texcoord0 - primary texture coordinate
  • texcoord1 ... texcoord3 - additional texture coordinates (for multitexturing)
  • color0 ... color1 - primary and secondary colors
  • tangent - surface tangents (for bump mapping)

The vertex formats are

  • f1 - one float
  • f2 - two floats (typical for texture coordinates)
  • f3 - three floats (positions and normals)
  • f4 - four floats
  • ub4 - four unsigned bytes (the usual format for colors)

The primitive types are

  • trilist
  • tristrip
  • trifan
  • linelist
  • linestrip
  • points
  • sprites (v1.5.0 and later)

Tristrips and trifans can be much more efficient than triangle lists.

Example ASCII CMOD File[edit]

#celmodel__ascii
# The above line is the 16-byte header; for binary files, it's
# #celmodel_binary

# material definitions follow--these must precede any meshes in the file
material       # index 0
emissive 0 1 0
opacity 0.5
end_material

# A material with a texture.  Texture filenames may use the wildcard
# character, which behaves exactly as it does within a .ssc file.
material        # index 1
emissive 1 1 1
texture0 "tropical-beach.*"
end_material

# There may be one or more meshes in a model file--this file happens
# to have just a single one
mesh

# The vertex description tells what attributes the vertices for this mesh
# have.  Each attribute in the description consists of a semantic and a
# data format.
vertexdesc
position f3
normal f3
texcoord0 f2
end_vertexdesc

# The vertex data--the number right after the keyword vertices is the
# number of vertices in the pool.
vertices 6
0 0 0 0 0 1 0 0
1 1 0 0 0 1 1 1
0 1 0 0 0 1 0 1
0 1 0 0 0 -1 0 1
1 1 0 0 0 -1 1 1
0 0 0 0 0 -1 0 0

# An arbitrary number of primitive groups follow

# The primitive group type is followed by a material index, then
# a count of the number of vertex indices in the group
trilist 0 3
0 1 2
trilist 1 3
3 4 5

# End of the mesh
end_mesh

# ---- end of cmod file ----

Point Sprites[edit]

Starting with Celestia v1.5.0, the CMOD format supports point sprites. These can be used for a variety of different volumetric rendering effects for phenomena such as nebula, accretion disks, volcanic plumes, or engine exhaust.

The changes to the cmod format relevant to point sprites are minor. There's a new material attribute (blend add), a new primitive type (sprites), and a new vertex attribute type (pointsize). The sprite texture is texture0 from the material definition. Here is a very basic example of a sprite CMOD with three red sprites:

#celmodel__ascii
material
diffuse 1 0 0
texture0 "gaussian.jpg"
end_material

mesh
vertexdesc
position f3
pointsize f1
end_vertexdesc

vertices 3
1 0 0 0.25
2 0 0 0.25
3 0 0 0.25

sprites 0 3
0 1 2

end_mesh

Blending[edit]

There are three options available in the CMOD format which affect how the geometry--triangles, lines, points or sprites--is blended with the background. They are normal, blend and premultiplied, with 'normal' the default.

For nebula meshes, it may be useful to specify additive blending in the material definition by adding the line 'blend add'. This is especially appropriate for emission nebula, but definitely not to be used for dark nebula like the Horsehead. Additively blended objects have the advantage of not needing to be depth sorted with respect to each other. Here's a slightly more complex sprite cmod that uses additive blending and per vertex colors so that each sprite has a different color:

#celmodel__ascii
material
diffuse 1 0 0
texture0 "gaussian.jpg"
blend add
end_material

mesh
vertexdesc
position f3
pointsize f1
color0 f3
end_vertexdesc

# row of sprites: red, green, blue
vertices 3
1 0 0 0.25  1 0 0
2 0 0 0.25  0 1 0
3 0 0 0.25  0 0 1

sprites 0 3
0 1 2

end_mesh

For point sprites, each sprite has a color and an alpha value that are multiplied by the sprite texture color and alpha values to produce the fragment color/alpha (Fr Fg Fb) and Fa. The alpha value can be thought of as the opacity, though as you'll see, it's not always treated exactly that way.

Add is the simplest mode. If the background color is (Br Bg Bb), the final color will be:

Fa * (Fr Fg Fb) + (Br Bg Bb)

Normal mode causes the background to be obscured; fragments with an alpha of 1 will completely block the background.

Fa * (Fr Fg Fb) + (1 - Fa) (Br Bg Bb)

Finally, premultiplied is somewhat like normal except it omits the multiplication by the fragment alpha. It assumes that any reduction of the color due to transparency was already done when the geometry was created, hence the name premultiplied:

(Fr Fg Fb) + (1 - Fa) (Br Bg Bb)

The advantage of premultiplied blending is that it can simulate either the add or normal blending modes with appropriately chosen alpha and color values. For example, setting alpha to zero gives the same result as additive blending. Cham: I think this is what you were talking about when you said you wanted to enable add individually for particles.

For nebula, add is good for glowing, diffuse gases where absorption isn't too much of a factor. Premultiplied would in principle let you mix emissive and absorbing particles, but there's a catch: premultiplied and normal blending aren't generally commutative. For correct results, it's necessary to sort the particles from back to front and render them in order, which isn't something that Celestia currently does, as sorting a large number of particles can dramatically reduce performance. In fact, this order dependence of blending has been a big headache in realtime 3D graphics for a long time, and there's still no completely satisfactory solution for the problem.

Using TriStrips (by Toti)[edit]

A tristrip is an OpenGL primitive that draws a set of triangles this way:

     1-----2 

     1-----2 
     \     / 
       \ / 
        3 

     1-----2 
     \     /\ 
       \ /    \ 
        3-----4 

     1-----2 
     \     /\ 
       \ /    \ 
        3-----4 
        \     / 
          \ / 
           5 


So we can store this as: 1,2,3,4,5 (5 entries) With a trilist the same three triangles must be stored this way: 1,2,3 2,3,4 3,4,5 (9 entries, spaces are only to make text legible) Basically the first two vertices define a basis. For each added vertex v a new triangle is defined. This triangle if formed by the vertices [v-2][v-1][v].

In general, for a trilist of T triangles we need 3*T entries, but for a tristrip of T triangles T+2 entries are enough. So for large meshes you are saving 2/3 of the resources. The same can be applied to linestrips:

     1---2 

     1---2 
         / 
       / 
       3 

     1---2 
         / 
       /  
       3----4 

     1---2 
         / 
       /  
       3----4 
           / 
         / 
         5 


If the above mesh is defined as a linelist: 1,2 2,3 3,4 4,5 (8 entries). If it is defined as a linestrip: 1,2,3,4,5 (5 entries) A linelist needs 2*L entries for a set of L lines. A linestrip only needs L+1 entries for the same set. For large meshes the saving is about 50% of resources.

The usual way to work with these primitives is to model the object as usual and then use an optimization utility to convert from raw triangles to tristrips, etc. I don't know of any free program/library that can do this, though. Surely Chris does.

Using Trifans (by Toti)[edit]

            1-------2 
             \     / 
              \   / 
                3 

            1-------2 
          /  \     / 
         /    \   / 
        4------ 3 

       5------ 1-------2 
        \    /  \     / 
         \  /    \   / 
           4------ 3 


Using trifans, the above mesh can be described as 1,2,3,4,5 Each tringle has the vertices [v0][v-1][v], where v0 is the first vertex of the set.

As you see, it's similar to a tristrip, but the first vertex is always the center of the trifan. You need T+2 entries to define T triangles, so it is as efficient as a tristrip (but often it can be used where a tristrip can't)

The advantage of using these primitives is that you save disk, bus and memory resources, because mesh descriptions are less voluminous.