THE PHILOSOPHY OF EGG FILES (vs. bam files)

Egg files are used by Panda3D to describe many properties of a scene:
simple geometry, including special effects and collision surfaces,
characters including skeletons, morphs, and multiple-joint
assignments, and character animation tables.  

Egg files are designed to be the lingua franca of model manipulation
for Panda tools.  A number of utilities are provided that read and
write egg files, for instance to convert to or from some other
modeling format, or to apply a transform or optimize vertices.  The
egg file philosophy is to describe objects in an abstract way that
facilitates easy manipulation; thus, the format doesn't (usually)
include information such as polygon connectivity or triangle meshes.
Egg files are furthermore designed to be human-readable to help a
developer diagnose (and sometimes repair) problems.  Also, the egg
syntax is always intended to be backward compatible with previous
versions, so that as the egg syntax is extended, old egg files will
continue to remain valid.

This is a different philosophy than Panda's bam file format, which is
a binary representation of a model and/or animation that is designed
to be loaded quickly and efficiently, and is strictly tied to a
particular version of Panda.  The data in a bam file closely mirrors
the actual Panda structures that are used for rendering.  Although an
effort is made to keep bam files backward compatible, occasionally
this is not possible and we must introduce a new bam file major
version.

Where egg files are used for model conversion and manipulation of
models, bam files are strictly used for loading models into Panda.
Although you can load an egg file directly, a bam file will be loaded
much more quickly.

Egg files might be generated by outside sources, and thus it makes
sense to document its syntax here.  Bam files, on the other hand,
should only be generated by Panda3D, usually by the program egg2bam.
The exact specification of the bam file format, if you should need it,
is documented within the Panda3D code itself.


GENERAL EGG SYNTAX

Egg files consist of a series of sequential and hierarchically-nested
entries.  In general, the syntax of each entry is:

<Entry-type> name { contents }

Where the name is optional (and in many cases, ignored anyway) and the
syntax of the contents is determined by the entry-type.  The name (and
strings in general) may be either quoted with double quotes or
unquoted.  Newlines are treated like any other whitespace, and case is
not significant.  The angle brackets are literally a part of the entry
keyword.  (Square brackets and ellipses in this document are used to
indicate optional pieces, and are not literally part of the syntax.)

The name field is always syntactically allowed between an entry
keyword and its opening brace, even if it will be ignored.  In the
syntax lines given below, the name is not shown if it will be ignored.

Comments may be delimited using either the C++-style // ... or the
C-style /* ... */.  C comments do not nest.  There is also a <Comment>
entry type, of the form:

<Comment> { text }

<Comment> entries are slightly different, in that tools which read and
write egg files will preserve the text within <Comment> entries, but
they may not preserve comments delimited by // or /* */.  Special
characters and keywords within a <Comment> entry should be quoted;
it's safest to quote the entire comment.



LOCAL INFORMATION ENTRIES

These nodes contain information relevant to the current level of
nesting only.

<Scalar> name { value }
<Char*> name { value }

  Scalars can appear in various contexts.  They are always optional,
  and specify some attribute value relevant to the current context.
  The scalar name is the name of the attribute; different attribute
  names are meaningful in different contexts.  The value is either a
  numeric or a (quoted or unquoted) string value; the interpretation
  as a number or as a string depends on the nature of the named
  attribute.  Because of a syntactic accident with the way the egg
  syntax evolved, <Scalar> and <Char*> are lexically the same and both
  can represent either a string or a number.  <Char*> is being phased
  out; it is suggested that new egg files use only <Scalar>.



GLOBAL INFORMATION ENTRIES

These nodes contain information relevant to the file as a whole.  They
can be nested along with geometry nodes, but this nesting is
irrelevant and the only significant placement rule is that they should
appear before they are referenced.


<CoordinateSystem> { string }

  This entry indicates the coordinate system used in the egg file; the
  egg loader will automatically make a conversion if necessary.  The
  following strings are valid: Y-up, Z-up, Y-up-right, Z-up-right,
  Y-up-left, or Z-up-left.  (Y-up is the same as Y-up-right, and Z-up
  is the same as Z-up-right.)

  By convention, this entry should only appear at the beginning of the
  file, although it is technically allowed anywhere.  It is an error
  to include more than one coordinate system entry in the same file.
  If it is omitted, Y-up is assumed.


<Texture> name { filename [scalars] }

  This describes a texture file that can be referenced later with
  <TRef> { name }.  It is not necessary to make a <Texture> entry for
  each texture to be used; a texture may also be referenced directly
  by the geometry via an abbreviated inline <Texture> entry, but a
  separate <Texture> entry is the only way to specify anything other
  than the default texture attributes.

  If the filename is a relative path, the current egg file's directory
  is searched first, and then the texture-path and model-path are
  searched.

  The following attributes are presently implemented for textures:

  <Scalar> alpha-file { alpha-filename }

    If this scalar is present, the texture file's alpha channel is
    read in from the named image file (which should contain a
    grayscale image), and the two images are combined into a single
    two- or four-channel image internally.  This is useful for loading
    alpha channels along with image file formats like JPEG that don't
    traditionally support alpha channels.

  <Scalar> alpha-file-channel { channel }

    This defines the channel that should be extracted from the file
    named by alpha-file to determine the alpha channel for the
    resulting channel.  The default is 0, which means the grayscale
    combination of r, g, b.  Otherwise, this should be the 1-based
    channel number, for instance 1, 2, or 3 for r, g, or b,
    respectively, or 4 for the alpha channel of a four-component
    image.

  <Scalar> format { format-definition }

    This defines the load format of the image file.  The
    format-definition is one of:

    RGBA, RGBM, RGBA12, RGBA8, RGBA4,
    RGB, RGB12, RGB8, RGB5, RGB332,
    LUMINANCE_ALPHA,
    RED, GREEN, BLUE, ALPHA, LUMINANCE

    The formats whose names end in digits specifically request a
    particular texel width.  RGB12 and RGBA12 specify 48-bit texels
    with or without alpha; RGB8 and RGBA8 specify 32-bit texels, and
    RGB5 and RGBA4 specify 16-bit texels.  RGB332 specifies 8-bit
    texels.

    The remaining formats are generic and specify only the semantic
    meaning of the channels.  The size of the texels is determined by
    the width of the components in the image file.  RGBA is the most
    general; RGB is the same, but without any alpha channel.  RGBM is
    like RGBA, except that it requests only one bit of alpha, if the
    graphics card can provide that, to leave more room for the RGB
    components, which is especially important for older 16-bit
    graphics cards (the "M" stands for "mask", as in a cutout).

    The number of components of the image file should match the format
    specified; if it does not, the egg loader will attempt to provide
    the closest match that does.

  <Scalar> wrap { repeat-definition }
  <Scalar> wrapu { repeat-definition }
  <Scalar> wrapv { repeat-definition }
  <Scalar> wrapw { repeat-definition }

    This defines the behavior of the texture image outside of the
    normal (u,v) range 0.0 - 1.0.  It is "REPEAT" to repeat the
    texture to infinity, "CLAMP" not to.  The wrapping behavior may be
    specified independently for each axis via "wrapu" and "wrapv", or
    it may be specified for both simultaneously via "wrap".

    Although less often used, for 3-d textures wrapw may also be
    specified, and it behaves similarly to wrapu and wrapv.

    There are other legal values in addtional to REPEAT and CLAMP.
    The full list is:

      CLAMP
      REPEAT
      MIRROR
      MIRROR_ONCE
      BORDER_COLOR

  <Scalar> borderr { red-value }
  <Scalar> borderg { green-value }
  <Scalar> borderb { blue-value }
  <Scalar> bordera { alpha-value }

    These define the "border color" of the texture, which is
    particularly important when one of the wrap modes, above, is
    BORDER_COLOR.

  <Scalar> type { texture-type }

    This may be one of the following attributes:

      1D
      2D
      3D
      CUBE_MAP

    The default is "2D", which specifies a normal, 2-d texture.  If
    any of the other types is specified instead, a texture image of
    the corresponding type is loaded.

    If 3D or CUBE_MAP is specified, then a series of texture images
    must be loaded to make up the complete texture; in this case, the
    texture filename is expected to include a sequence of one or more
    hash mark ("#") characters, which will be filled in with the
    sequence number.  The first image in the sequence must be numbered
    0, and there must be no gaps in the sequence.  In this case, a
    separate alpha-file designation is ignored; the alpha channel, if
    present, must be included in the same image with the color
    channel(s).

  <Scalar> read-mipmaps { flag }

    If this flag is nonzero, then pre-generated mipmap levels will be
    loaded along with the texture.  In this case, the filename should
    contain a sequence of one or more hash mark ("#") characters,
    which will be filled in with the mipmap level number; the texture
    filename thus determines a series of images, one for each mipmap
    level.  The base texture image is mipmap level 0.

    If this flag is specified in conjunction with a 3D or cube map
    texture (as specified above), then the filename should contain two
    hash mark sequences, separated by a character such as an
    underscore, hyphen, or dot.  The first sequence will be filled in
    with the mipmap level index, and the second sequence will be
    filled in with the 3D sequence or cube map face.

  <Scalar> minfilter { filter-type }
  <Scalar> magfilter { filter-type }
  <Scalar> magfilteralpha { filter-type }
  <Scalar> magfiltercolor { filter-type }

    This specifies the type of filter applied when minimizing or
    maximizing.  Filter-type may be one of:

      NEAREST
      LINEAR
      NEAREST_MIPMAP_NEAREST
      LINEAR_MIPMAP_NEAREST
      NEAREST_MIPMAP_LINEAR
      LINEAR_MIPMAP_LINEAR

    There are also some additional filter types that are supported for
    historical reasons, but each of those additional types maps to one
    of the above.  New egg files should use only the above filter
    types.

  <Scalar> envtype { environment-type }

    This specifies the type of texture environment to create; i.e. it
    controls the way in which textures apply to models.
    Environment-type may be one of:

      MODULATE
      DECAL
      BLEND
      REPLACE
      ADD
      BLEND_COLOR_SCALE

    The default environment type is MODULATE, which means the texture
    color is multiplied with the base polygon (or vertex) color.  This
    is the most common texture environment by far.  Other environment
    types are more esoteric and are especially useful in the presence
    of multitexture.  See howto.use_multitexture.txt for more on these.

  <Scalar> combine-rgb { combine-mode }
  <Scalar> combine-alpha { combine-mode }
  <Scalar> combine-rgb-source0 { combine-source }
  <Scalar> combine-rgb-operand0 { combine-operand }
  <Scalar> combine-rgb-source1 { combine-source }
  <Scalar> combine-rgb-operand1 { combine-operand }
  <Scalar> combine-rgb-source2 { combine-source }
  <Scalar> combine-rgb-operand2 { combine-operand }
  <Scalar> combine-alpha-source0 { combine-source }
  <Scalar> combine-alpha-operand0 { combine-operand }
  <Scalar> combine-alpha-source1 { combine-source }
  <Scalar> combine-alpha-operand1 { combine-operand }
  <Scalar> combine-alpha-source2 { combine-source }
  <Scalar> combine-alpha-operand2 { combine-operand }

    These options replace the envtype and specify the texture combiner
    mode, which is usually used for multitexturing.  This specifies
    how the texture combines with the base color and/or the other
    textures applied previously.  You must specify both an rgb and an
    alpha combine mode.  Some combine-modes use one source/operand
    pair, and some use all three; most use just two.

    combine-mode may be one of:

      REPLACE
      MODULATE
      ADD
      ADD-SIGNED
      INTERPOLATE
      SUBTRACT
      DOT3-RGB
      DOT3-RGBA

    combine-source may be one of:

      TEXTURE
      CONSTANT
      PRIMARY-COLOR
      PREVIOUS
      CONSTANT_COLOR_SCALE
      LAST_SAVED_RESULT

    combine-operand may be one of:

      SRC-COLOR
      ONE-MINUS-SRC-COLOR
      SRC-ALPHA
      ONE-MINUS-SRC-ALPHA

    The default values if any of these are omitted are:
      <Scalar> combine-rgb { modulate }
      <Scalar> combine-alpha { modulate }
      <Scalar> combine-rgb-source0 { previous }
      <Scalar> combine-rgb-operand0 { src-color }
      <Scalar> combine-rgb-source1 { texture }
      <Scalar> combine-rgb-operand1 { src-color }
      <Scalar> combine-rgb-source2 { constant }
      <Scalar> combine-rgb-operand2 { src-alpha }
      <Scalar> combine-alpha-source0 { previous }
      <Scalar> combine-alpha-operand0 { src-alpha }
      <Scalar> combine-alpha-source1 { texture }
      <Scalar> combine-alpha-operand1 { src-alpha }
      <Scalar> combine-alpha-source2 { constant }
      <Scalar> combine-alpha-operand2 { src-alpha }

  <Scalar> saved-result { flag }
 
    If flag is nonzero, then it indicates that this particular texture
    stage will be supplied as the "last_saved_result" source for any
    future texture stages.

  <Scalar> tex-gen { mode }

    This specifies that texture coordinates for the primitives that
    reference this texture should be dynamically computed at runtime,
    for instance to apply a reflection map or some other effect.  The
    valid values for mode are:

      EYE_SPHERE_MAP (or SPHERE_MAP)
      WORLD_CUBE_MAP
      EYE_CUBE_MAP (or CUBE_MAP)
      WORLD_NORMAL
      EYE_NORMAL
      WORLD_POSITION
      EYE_POSITION
      POINT_SPRITE

  <Scalar> stage-name { name }

    Specifies the name of the TextureStage object that is created to
    render this texture.  If this is omitted, a custom TextureStage is
    created for this texture if it is required (e.g. because some
    other multitexturing parameter has been specified), or the system
    default TextureStage is used if multitexturing is not required.

  <Scalar> priority { priority-value }

    Specifies an integer sort value to rank this texture in priority
    among other textures that are applied to the same geometry.  This
    is only used to eliminate low-priority textures in case more
    textures are requested for a particular piece of geometry than the
    graphics hardware can render.

  <Scalar> blendr { red-value }
  <Scalar> blendg { green-value }
  <Scalar> blendb { blue-value }
  <Scalar> blenda { alpha-value }

    Specifies a four-component color that is applied with the color in
    case the envtype, above, is "blend", or one of the combine-sources
    is "constant".

  <Scalar> uv-name { name }

    Specifies the name of the texture coordinates that are to be
    associated with this texture.  If this is omitted, the default
    texture coordinates are used.

  <Scalar> rgb-scale { scale }
  <Scalar> alpha-scale { scale }

    Specifies an additional scale factor that will scale the r, g, b
    (or a) components after the texture has been applied.  This is
    only used when a combine mode is in effect.  The only legal values
    are 1, 2, or 4.

  <Scalar> alpha { alpha-type }

    This specifies whether and what type of transparency will be
    performed.  Alpha-type may be one of:

      OFF
      ON
      BLEND
      BLEND_NO_OCCLUDE
      MS
      MS_MASK

    If alpha-type is OFF, it means not to enable transparency, even if
    the image contains an alpha channel or the format is RGBA.  If
    alpha-type is ON, it means to enable the default transparency,
    even if the image filename does not contain an alpha channel.  If
    alpha-type is any of the other options, it specifies the type of
    transparency to be enabled.

  <Scalar> bin { bin-name }

    This specifies the bin name order of all polygons with this
    texture applied, in the absence of a bin name specified on the
    polygon itself.  See the description for bin under polygon
    attributes.

  <Scalar> draw_order { number }

    This specifies the fixed drawing order of all polygons with this
    texture applied, in the absence of a drawing order specified on
    the polygon itself.  See the description for draw_order under
    polygon attributes.

  <Transform> { transform-definition }

    This specifies a 2-d or 3-d transformation that is applied to the
    UV's of a surface to generate the texture coordinates.

    The transform syntax is similar to that for groups, except it may
    define either a 2-d 3x3 matrix or a 3-d 4x4 matrix.  (You should
    use the two-dimensional forms if the UV's are two-dimensional, and
    the three-dimensional forms if the UV's are three-dimensional.)

    A two-dimensional transform may be any sequence of zero or more of
    the following.  Transformations are post multiplied in the order
    they are encountered to produce a net transformation matrix.
    Rotations are counterclockwise about the origin in degrees.
    Matrices, when specified explicitly, are row-major.

      <Translate> { x y }
      <Rotate> { degrees }
      <Scale> { x y }
      <Scale> { s }

      <Matrix3> {
        00 01 02
        10 11 12
        20 21 22
      }

    A three-dimensional transform may be any sequence of zero or more
    of the following.  See the description under <Group>, below, for
    more information.

      <Translate> { x y z }
      <RotX> { degrees }
      <RotY> { degrees }
      <RotZ> { degrees }
      <Rotate> { degrees x y z }
      <Scale> { x y z }
      <Scale> { s }

      <Matrix4> {
        00 01 02 03
        10 11 12 13
        20 21 22 23
        30 31 32 33
      }


<Material> name { [scalars] }

  This defines a set of material attributes that may later be
  referenced with <MRef> { name }.

  The following attributes may appear within the material block:

  <Scalar> diffr { number }
  <Scalar> diffg { number }
  <Scalar> diffb { number }
  <Scalar> diffa { number }

  <Scalar> ambr { number }
  <Scalar> ambg { number }
  <Scalar> ambb { number }
  <Scalar> amba { number }

  <Scalar> emitr { number }
  <Scalar> emitg { number }
  <Scalar> emitb { number }
  <Scalar> emita { number }

  <Scalar> specr { number }
  <Scalar> specg { number }
  <Scalar> specb { number }
  <Scalar> speca { number }

  <Scalar> shininess { number }
  <Scalar> local { flag }

  These properties collectively define a "material" that controls the
  lighting effects that are applied to a surface; a material is only
  in effect in the presence of lighting.

  The four color groups, diff*, amb*, emit*, and spec* specify the
  diffuse, ambient, emission, and specular components of the lighting
  equation, respectively.  Any of them may be omitted; the omitted
  component(s) take their color from the native color of the
  primitive, otherwise the primitive color is replaced with the
  material color.

  The shininess property controls the size of the specular highlight,
  and the value ranges from 0 to 128.  A larger value creates a
  smaller highlight (creating the appearance of a shinier surface).


<VertexPool> name { vertices }

  A vertex pool is a set of vertices.  All geometry is created by
  referring to vertices by number in a particular vertex pool.  There
  may be one or several vertex pools in an egg file, but all vertices
  that make up a single polygon must come from the same vertex pool.
  The body of a <VertexPool> entry is simply a list of one or more
  <Vertex> entries, as follows:


  <Vertex> number { x [y [z [w]]] [attributes] }

    A <Vertex> entry is only valid within a vertex pool definition.
    The number is the index by which this vertex will be referenced.
    It is optional; if it is omitted, the vertices are implicitly
    numbered consecutively beginning at one.  If the number is
    supplied, the vertices need not be consecutive.

    Normally, vertices are three-dimensional (with coordinates x, y,
    and z); however, in certain cases vertices may have fewer or more
    dimensions, up to four.  This is particularly true of vertices
    used as control vertices of NURBS curves and surfaces.  If more
    coordinates are supplied than needed, the extra coordinates are
    ignored; if fewer are supplied than needed, the missing
    coordinates are assumed to be 0.

    The vertex's coordinates are always given in world space,
    regardless of any transforms before the vertex pool or before the
    referencing geometry.  If the vertex is referenced by geometry
    under a transform, the egg loader will do an inverse transform to
    move the vertex into the proper coordinate space without changing
    its position in world space.  One exception is geometry under an
    <Instance> node; in this case the vertex coordinates are given in
    the space of the <Instance> node.  (Another exception is a
    <DynamicVertexPool>; see below.)

    In neither case does it make a difference whether the vertex pool
    is itself declared under a transform or an <Instance> node.  The
    only deciding factor is whether the geometry that *uses* the
    vertex pool appears under an <Instance> node.  It is possible for
    a single vertex to be interpreted in different coordinate spaces
    by different polygons.

    While each vertex must at least have a position, it may also have
    a color, normal, pair of UV coordinates, and/or a set of morph
    offsets.  Furthermore, the color, normal, and UV coordinates may
    themselves have morph offsets.  Thus, the [attributes] in the
    syntax line above may be replaced with zero or more of the
    following entries:



    <Dxyz> target { x y z }

    This specifies the offset of this vertex for the named morph
    target.  See the "MORPH DESCRIPTION ENTRIES" header, below.


    <Normal> { x y z [morph-list] }

    This specifies the surface normal of the vertex.  If omitted, the
    vertex will have no normal.  Normals may also be morphed;
    morph-list here is thus an optional list of <DNormal> entries,
    similar to the above.


    <RGBA> { r g b a [morph-list] }

    This specifies the four-valued color of the vertex.  Each
    component is in the range 0.0 to 1.0.  A vertex color, if
    specified for all vertices of the polygon, overrides the polygon's
    color.  If neither color is given, the default is white
    (1 1 1 1).  The morph-list is an optional list of <DRGBA> entries.


    <UV> [name] { u v [w] [tangent] [binormal] [morph-list] }

    This gives the texture coordinates of the vertex.  This must be
    specified if a texture is to be mapped onto this geometry.  

    The texture coordinates are usually two-dimensional, with two
    component values (u v), but they may also be three-dimensional,
    with three component values (u v w).  (Arguably, it should be
    called <UVW> instead of <UV> in the three-dimensional case, but
    it's not.)

    As before, morph-list is an optional list of <DUV> entries.

    Unlike the other kinds of attributes, there may be multiple sets
    of UV's on each vertex, each with a unique name; this provides
    support for multitexturing.  The name may be omitted to specify
    the default UV's.

    The UV's also support an optional tangent and binormal.  These
    values are based on the vertex normal and the UV coordinates of
    connected vertices, and are used to render normal maps and similar
    lighting effects.  They are defined within the <UV> entry because
    there may be a different set of tangents and binormals for each
    different UV coordinate set.  If present, they have the expected
    syntax:

    <UV> [name] { u v [w] <Tangent> { x y z } <Binormal> { x y z } }


<DynamicVertexPool> name { vertices }

  A dynamic vertex pool is similar to a vertex pool in most respects,
  except that each vertex might be animated by substituting in values
  from a <VertexAnim> table.  Also, the vertices defined within a
  dynamic vertex pool are always given in local coordinates, instead
  of world coordinates.

  The presence of a dynamic vertex pool makes sense only within a
  character model, and a single dynamic vertex pool may not span
  multiple characters.  Each dynamic vertex pool creates a DynVerts
  object within the character by the same name; this name is used
  later when matching up the corresponding <VertexAnim>.

  At the present time, the DynamicVertexPool is not implemented in
  Panda3D.
  



GEOMETRY ENTRIES

<Polygon> name { 
    [attributes] 
    <VertexRef> { 
        indices 
        <Ref> { pool-name } 
    } 
}

  A polygon consists of a sequence of vertices from a single vertex
  pool.  Vertices are identified by pool-name and index number within
  the pool; indices is a list of vertex numbers within the given
  vertex pool.  Vertices are listed in counterclockwise order.
  Although the vertices must all come from the same vertex pool, they
  may have been assigned to arbitrarily many different joints
  regardless of joint connectivity (there is no "straddle-polygon"
  limitation).  See Joints, below.

  The polygon syntax is quite verbose, and there isn't any way to
  specify a set of attributes that applies to a group of polygons--the
  attributes list must be repeated for each polygon.  This is why egg
  files tend to be very large.

  The following attributes may be specified for polygons:

  <TRef> { texture-name }

    This refers to a named <Texture> entry given earlier.  It applies
    the given texture to the polygon.  This requires that all the
    polygon's vertices have been assigned texture coordinates.

    This attribute may be repeated multiple times to specify
    multitexture.  In this case, each named texture is applied to the
    polygon, in the order specified.


  <Texture> { filename }

    This is another way to apply a texture to a polygon.  The
    <Texture> entry is defined "inline" to the polygon, instead of
    referring to a <Texture> entry given earlier.  There is no way to
    specify texture attributes given this form.

    There's no advantage to this syntax for texture mapping.  It's
    supported only because it's required by some older egg files.


  <MRef> { material-name }

    This applies the material properties defined in the earlier
    <Material> entry to the polygon.


  <Normal> { x y z [morph-list] }

    This defines a polygon surface normal.  The polygon normal will be
    used unless all vertices also have a normal.  If no normal is
    defined, none will be supplied.  The polygon normal, like the
    vertex normal, may be morphed by specifying a series of <DNormal>
    entries.  

    The polygon normal is used only for lighting and environment
    mapping calculations, and is not related to the implicit normal
    calculated for CollisionPolygons.


  <RGBA> { r g b a [morph-list] }

    This defines the polygon's color, which will be used unless all
    vertices also have a color.  If no color is defined, the default
    is white (1 1 1 1).  The color may be morphed with a series of
    <DRGBA> entries.


  <BFace> { boolean-value }

    This defines whether the polygon will be rendered double-sided
    (i.e. its back face will be visible).  By default, this option is
    disabled, and polygons are one-sided; specifying a nonzero value
    disables backface culling for this particular polygon and allows
    it to be viewed from either side.
    

  <Scalar> bin { bin-name }

    It is sometimes important to control the order in which objects
    are rendered, particularly when transparency is in use.  In Panda,
    this is achieved via the use of named bins and, within certain
    kinds of bins, sometimes an explicit draw_order is also used (see
    below).

    In the normal (state-sorting) mode, Panda renders its geometry by
    first grouping into one or more named bins, and then rendering the
    bins in a specified order.  The programmer is free to define any
    number of bins, named whatever he/she desires.

    This scalar specifies which bin this particular polygon is to be
    rendered within.  If no bin scalar is given, or if the name given
    does not match any of the known bins, the polygon will be assigned
    to the default bin, which renders all opaque geometry sorted by
    state, followed by all transparent geometry sorted back-to-front.

    See also draw_order, below.


  <Scalar> draw_order { number }

    This works in conjunction with bin, above, to further refine the
    order in which this polygon is drawn, relative to other geometry
    in the same bin.  If (and only if) the bin type named in the bin
    scalar is a CullBinFixed, this draw_order is used to define the
    fixed order that all geometry in the same will be rendered, from
    smaller numbers to larger numbers.

    If the draw_order scalar is specified but no bin scalar is
    specified, the default is a bin named "fixed", which is a
    CullBinFixed object that always exists by default.


  <Scalar> visibility { hidden | normal }

    If the visibility of a primitive is set to "hidden", the primitive
    is not generated as a normally visible primitive.  If the
    Config.prc variable egg-suppress-hidden is set to true, the
    primitive is not converted at all; otherwise, it is converted as a
    "stashed" node.

    This, like the other rendering flags alpha, draw_order, and bin,
    may be specified at the group level, within the primitive level,
    or even within a texture.


<PointLight> name { 
    [attributes] 
    <VertexRef> { 
        indices 
        <Ref> { pool-name } 
    } 
}

  A PointLight is a set of single points.  One point is drawn for each
  vertex listed in the <VertexRef>.  Normals, textures, and colors may
  be specified for PointLights, as well as draw_order, plus one
  additional attribute valid only for PointLights and Lines:

  <Scalar> thick { number }

    This specifies the size of the PointLight (or the width of a
    line), in pixels, when it is rendered.  This may be a
    floating-point number, but the fractional part is meaningful only
    when antialiasing is in effect.  The default is 1.0.

  <Scalar> perspective { boolean-value }

    If this is specified, then the thickness, above, is to interpreted
    as a size in 3-d spatial units, rather than a size in pixels, and
    the point should be scaled according to its distance from the
    viewer normally.



<Line> name { 
    [attributes] 
    <VertexRef> { 
        indices 
        <Ref> { pool-name } 
    } 
    [component attributes]
}

  A Line is a connected set of line segments.  The listed N vertices
  define a series of N-1 line segments, drawn between vertex 0 and
  vertex 1, vertex 1 and vertex 2, etc.  The line is not implicitly
  closed; if you wish to represent a loop, you must repeat vertex 0 at
  the end.  As with a PointLight, normals, textures, colors,
  draw_order, and the "thick" attribute are all valid (but not
  "perspective").  Also, since a Line (with more than two vertices) is
  made up of multiple line segments, it may contain a number of
  <Component> entries, to set a different color and/or normal for each
  line segment, as in TriangleStrip, below.


<TriangleStrip> name { 
    [attributes] 
    <VertexRef> { 
        indices 
        <Ref> { pool-name } 
    }
    [component attributes]
}

  A triangle strip is only rarely encountered in an egg file; it is
  normally generated automatically only during load time, when
  connected triangles are automatically meshed for loading, and even
  then it exists only momentarily.  Since a triangle strip is a
  rendering optimization only and adds no useful scene information
  over a loose collection of triangles, its usage is contrary to the
  general egg philosophy of representing a scene in the abstract.
  Nevertheless, the syntax exists, primarily to allow inspection of
  the meshing results when needed.  You can also add custom
  TriangleStrip entries to force a particular mesh arrangement.

  A triangle strip is defined as a series of connected triangles.
  After the first three vertices, which define the first triangle,
  each new vertex defines one additional triangle, by alternating up
  and down.

  It is possible for the individual triangles of a triangle strip to
  have a separate normal and/or color.  If so, a <Component> entry
  should be given for each so-modified triangle:
  
  <Component> index {
    <RGBA> { r g b a [morph-list] }
    <Normal> { x y z [morph-list] }
  }

  Where index ranges from 0 to the number of components defined by the
  triangle strip (less 1).  Note that the component attribute list
  must always follow the vertex list.


<TriangleFan> name { 
    [attributes] 
    <VertexRef> { 
        indices 
        <Ref> { pool-name } 
    }
    [component attributes]
}

  A triangle fan is similar to a triangle strip, except all of the
  connected triangles share the same vertex, which is the first
  vertex.  See <TriangleStrip>, above.



PARAMETRIC DESCRIPTION ENTRIES

The following entries define parametric curves and surfaces.
Generally, Panda supports these only in the abstract; they're not
geometry in the true sense but do exist in the scene graph and may
have specific meaning to the application.  However, Panda can create
visible representations of these parametrics to aid visualization.

These entries might also have meaning to external tools outside of an
interactive Panda session, such as egg-qtess, which can be used to
convert NURBS surfaces to polygons at different levels of resolution.

In general, dynamic attributes such as morphs and joint assignment are
legal for the control vertices of the following parametrics, but Panda
itself doesn't support them and will always create static curves and
surfaces.  External tools like egg-qtess, however, may respect them.

<NURBSCurve> {
    [attributes] 

    <Order> { order }
    <Knots> { knot-list }
    <VertexRef> { indices <Ref> { pool-name } }
}

  A NURBS curve is a general parametric curve.  It is often used to
  represent a motion path, e.g. for a camera or an object.

  The order is equal to the degree of the polynomial basis plus 1.  It
  must be an integer in the range [1,4].

  The number of vertices must be equal to the number of knots minus the
  order.

  Each control vertex of a NURBS is defined in homogeneous space with
  four coordinates x y z w (to convert to 3-space, divide x, y, and z
  by w).  The last coordinate is always the homogeneous coordinate; if
  only three coordinates are given, it specifies a curve in two
  dimensions plus a homogeneous coordinate (x y w).

  The following attributes may be defined:

  <Scalar> type { curve-type }

    This defines the semanting meaning of this curve, either XYZ, HPR,
    or T.  If the type is XYZ, the curve will automatically be
    transformed between Y-up and Z-up if necessary; otherwise, it will
    be left alone.

  <Scalar> subdiv { num-segments }

    If this scalar is given and nonzero, Panda will create a visible
    representation of the curve when the scene is loaded.  The number
    represents the number of line segments to draw to approximate the
    curve.

  <RGBA> { r g b a [morph-list] }

    This specifies the color of the overall curve.


  NURBS control vertices may also be given color and/or morph
  attributes, but <Normal> and <UV> entries do not apply to NURBS
  vertices.



<NURBSSurface> name {
    [attributes] 

    <Order> { u-order v-order }
    <U-knots> { u-knot-list }
    <V-knots> { v-knot-list }

    <VertexRef> { 
        indices 
        <Ref> { pool-name } 
    } 
}

  A NURBS surface is an extension of a NURBS curve into two parametric
  dimensions, u and v.  NURBS surfaces may be given the same set of
  attributes assigned to polygons, except for normals: <TRef>,
  <Texture>, <MRef>, <RGBA>, and draw_order are all valid attributes
  for NURBS.  NURBS vertices, similarly, may be colored or morphed,
  but <Normal> and <UV> entries do not apply to NURBS vertices.  The
  attributes may also include <NURBSCurve> and <Trim> entries; see
  below.

  To have Panda create a visualization of a NURBS surface, the
  following two attributes should be defined as well:

  <Scalar> U-subdiv { u-num-segments }
  <Scalar> V-subdiv { v-num-segments }

    These define the number of subdivisions to make in the U and V
    directions to represent the surface.  A uniform subdivision is
    always made, and trim curves are not respected (though they will
    be drawn in if the trim curves themselves also have a subiv
    parameter).  This is only intended as a cheesy visualization.


  The same sort of restrictions on order and knots applies to NURBS
  surfaces as do to NURBS curves.  The order and knot description may
  be different in each dimension.

  The surface must have u-num * v-num vertices, where u-num is the
  number of u-knots minus the u-order, and v-num is the number of
  v-knots minus the v-order.  All vertices must come from the same
  vertex pool.  The nth (zero-based) index number defines control
  vertex (u, v) of the surface, where n = (v * u-num) + u.  Thus, it
  is the u coordinate which changes faster.

  As with the NURBS curve, each control vertex is defined in
  homogeneous space with four coordinates x y z w.


  A NURBS may also contain curves on its surface.  These are one or
  more nested <NURBSCurve> entries included with the attributes; these
  curves are defined in the two-dimensional parametric space of the
  surface.  Thus, these curve vertices should have only two dimensions
  plus the homogeneous coordinate: u v w.  A curve-on-surface has no
  intrinsic meaning to the surface, unless it is defined within a
  <Trim> entry, below.

  Finally, a NURBS may be trimmed by one or more trim curves.  These
  are special curves on the surface which exclude certain areas from
  the NURBS surface definition.  The inside is specified using two
  rules: an odd winding rule that states that the inside consists of
  all regions for which an infinite ray from any point in the region
  will intersect the trim curve an odd number of times, and a curve
  orientation rule that states that the inside consists of the regions
  to the left as the curve is traced.

  Each trim curve contains one or more loops, and each loop contains
  one or more NURBS curves.  The curves of a loop connect in a
  head-to-tail fashion and must be explicitly closed.

  The trim curve syntax is as follows:

  <Trim> {
    <Loop> {
      <NURBSCurve> {
        <Order> { order }
	<Knots> { knot-list }

	<VertexRef> { indices <Ref> { pool-name } }
      }
      [ <NURBSCurve> { ... } ... ]
    }
    [ <Loop> { ... } ... ]
  }

  Although the egg syntax supports trim curves, there are at present
  no egg processing tools that respect them.  For instance, egg-qtess
  ignores trim curves and always tesselates the entire NURBS surface.


MORPH DESCRIPTION ENTRIES

Morphs are linear interpolations of attribute values at run time,
according to values read from an animation table.  In general, vertex
positions, surface normals, texture coordinates, and colors may be
morphed.

A morph target is defined by giving a net morph offset for a series of
vertex or polygon attributes; this offset is the value that will be
added to the attribute when the morph target has the value 1.0.  At
run time, the morph target's value may be animated to any scalar value
(but generally between 0.0 and 1.0); the corresponding fraction of the
offset is added to the attribute each frame.

There is no explicit morph target definition; a morph target exists
solely as the set of all offsets that share the same target name.  The
target name may be any arbitrary string; like any name in an egg file,
it should be quoted if it contains special characters.

The following types of morph offsets may be defined, within their
corresponding attribute entries:

<Dxyz> target { x y z }

  A position delta, valid within a <Vertex> entry or a <CV> entry.
  The given offset vector, scaled by the morph target's value, is
  added to the vertex or CV position each frame.


<DNormal> target { x y z }

  A normal delta, similar to the position delta, valid within a
  <Normal> entry (for vertex or polygon normals).  The given offset
  vector, scaled by the morph target's value, is added to the normal
  vector each frame.  The resulting vector may not be automatically
  normalized to unit length.


<DUV> target { u v [w] }

  A texture-coordinate delta, valid within a <UV> entry (within a
  <Vertex> entry).  The offset vector should be 2-valued if the
  enclosing UV is 2-valued, or 3-valued if the enclosing UV is
  3-valued.  The given offset vector, scaled by the morph target's
  value, is added to the vertex's texture coordinates each frame.


<DRGBA> target { r g b a }

  A color delta, valid within an <RGBA> entry (for vertex or polygon
  colors).  The given 4-valued offset vector, scaled by the morph
  target's value, is added to the color value each frame.




GROUPING ENTRIES

<Group> name { group-body }

  A <Group> node is the primary means of providing structure to the
  egg file.  Groups can contain vertex pools and polygons, as well as
  other groups.  The egg loader translates <Group> nodes directly into
  PandaNodes in the scene graph (although the egg loader reserves the
  right to arbitrarily remove nodes that it deems unimportant--see the
  <Model> flag, below to avoid this).  In addition, the following
  entries can be given specifically within a <Group> node to specify
  attributes of the group:

  GROUP BINARY ATTRIBUTES
  
  These attributes may be either on or off; they are off by default.
  They are turned on by specifying a non-zero "boolean-value".

  <DCS> { boolean-value }

    DCS stands for Dynamic Coordinate System.  This indicates that
    show code will expect to be able to read the transform set on this
    node at run time, and may need to modify the transform further.
    This is a special case of <Model>, below.

  <DCS> { dcs-type }

    This is another syntax for the <DCS> flag.  The dcs-type string
    should be one of either "local" or "net", which specifies the kind
    of preserve_transform flag that will be set on the corresponding
    ModelNode.  If the string is "local", it indicates that the local
    transform on this node (as well as the net transform) will not be
    affected by any flattening operation and will be preserved through
    the entire model loading process.  If the string is "net", then
    only the net transform will be preserved; the local transform may
    be adjusted in the event of a flatten operation.

  <Model> { boolean-value }

    This indicates that the show code might need a pointer to this
    particular group.  This creates a ModelNode at the corresponding
    level, which is guaranteed not to be removed by any flatten
    operation.  However, its transform might still be changed, but see
    also the <DCS> flag, above.


  <Dart> { boolean-value }

    This indicates that this group begins an animated character.  A
    Character node, which is the fundamental animatable object of
    Panda's high-level Actor class, will be created for this group.

    This flag should always be present within the <Group> entry at the
    top of any hierarchy of <Joint>'s and/or geometry with morphed
    vertices; joints and morphs appearing outside of a hierarchy
    identified with a <Dart> flag are undefined.

  <Switch> { boolean-value }

    This attribute indicates that the child nodes of this group
    represent a series of animation frames that should be
    consecutively displayed.  In the absence of an "fps" scalar for
    the group (see below), the egg loader creates a SwitchNode, and it
    the responsibility of the show code to perform the switching.  If
    an fps scalar is defined and is nonzero, the egg loader creates a
    SequenceNode instead, which automatically cycles through its
    children.


  GROUP SCALARS

  <Scalar> fps { frame-rate }

    This specifies the rate of animation for a SequenceNode (created
    when the Switch flag is specified, see above).  A value of zero
    indicates a SwitchNode should be created instead.

  <Scalar> bin { bin-name }

    This specifies the bin name for all polygons at or below this node
    that do not explicitly set their own bin.  See the description of
    bin for geometry attributes, above.

  <Scalar> draw_order { number }

    This specifies the drawing order for all polygons at or below this
    node that do not explicitly set their own drawing order.  See the
    description of draw_order for geometry attributes, above.

  <Scalar> visibility { hidden | normal }

    If the visibility of a group is set to "hidden", the primitives
    nested within that group are not generated as a normally visible
    primitive.  If the Config.prc variable egg-suppress-hidden is set
    to true, the primitives are not converted at all; otherwise, they
    are converted as a "stashed" node.

  <Scalar> decal { boolean-value }

    If this is present and boolean-value is non-zero, it indicates
    that the geometry *below* this level is coplanar with the geometry
    *at* this level, and the geometry below is to be drawn as a decal
    onto the geometry at this level.  This means the geometry below
    this level will be rendered "on top of" this geometry, but without
    the Z-fighting artifacts one might expect without the use of the
    decal flag.

  <Scalar> decalbase { boolean-value }

    This can optionally be used with the "decal" scalar, above.  If
    present, it should be applied to a sibling of one or more nodes
    with the "decal" scalar on.  It indicates which of the sibling
    nodes should be treated as the base of the decal.  In the absence
    of this scalar, the parent of all decal nodes is used as the decal
    base.  This scalar is useful when the modeling package is unable
    to parent geometry nodes to other geometry nodes.

  <Scalar> collide-mask { value }
  <Scalar> from-collide-mask { value }
  <Scalar> into-collide-mask { value }

      Sets the CollideMasks on the collision nodes and geometry nodes
      created at or below this group to the indicated values.  These
      are bits that indicate which objects can collide with which
      other objects.  Setting "collide-mask" is equivalent to setting
      both "from-collide-mask" and "into-collide-mask" to the same
      value.

      The value may be an ordinary decimal integer, or a hex number in
      the form 0x000, or a binary number in the form 0b000.

  <Scalar> blend { mode }

    Specifies that a special blend mode should be applied geometry at
    this level and below.  The available options are none, add,
    subtract, inv-subtract, min, and max.  See ColorBlendAttrib.

  <Scalar> blendop-a { mode }
  <Scalar> blendop-b { mode }

    If blend mode, above, is not none, this specifies the A and B
    operands to the blend equation.  Common options are zero, one,
    incoming-color, one-minus-incoming-color.  See ColorBlendAttrib
    for the complete list of available options.  The default is "one".

  <Scalar> blendr { red-value }
  <Scalar> blendg { green-value }
  <Scalar> blendb { blue-value }
  <Scalar> blenda { alpha-value }

    If blend mode, above, is not none, and one of the blend operands
    is constant-color or a related option, this defines the constant
    color that will be used.


  OTHER GROUP ATTRIBUTES

  <Billboard> { type }

    This entry indicates that all geometry defined at or below this
    group level is part of a billboard that will rotate to face the
    camera.  Type is either "axis" or "point", describing the type of
    rotation.

    Billboards rotate about their local axis.  In the case of a Y-up
    file, the billboards rotate about the Y axis; in a Z-up file, they
    rotate about the Z axis.  Point-rotation billboards rotate about
    the origin.

    There is an implicit <Instance> around billboard geometry.  This
    means that the geometry within a billboard is not specified in
    world coordinates, but in the local billboard space.  Thus, a
    vertex drawn at point 0,0,0 will appear to be at the pivot point
    of the billboard, not at the origin of the scene.

  <SwitchCondition> {
     <Distance> { 
        in out [fade] <Vertex> { x y z }
     }
  }

    The subtree beginning at this node and below represents a single
    level of detail for a particular model.  Sibling nodes represent
    the additional levels of detail.  The geometry at this node will
    be visible when the point (x, y, z) is closer than "in" units, but
    further than "out" units, from the camera.  "fade" is presently
    ignored.


  <Tag> key { value }

    This attribute defines the indicated tag (as a key/value pair),
    retrievable via NodePath::get_tag() and related interfaces, on
    this node.


  <Collide> name { type [flags] }

    This entry indicates that geometry defined at this group level is
    actually an invisible collision surface, and is not true geometry.
    The geometry is used to define the extents of the collision
    surface.  If there is no geometry defined at this level, then a
    child is searched for with the same collision type specified, and
    its geometry is used to define the extent of the collision
    surface (unless the "descend" flag is given; see below).

    Valid types so far are:

    Plane
    
      The geometry represents an infinite plane.  The first polygon
      found in the group will define the plane.

    Polygon

      The geometry represents a single polygon.  The first polygon is
      used.

    Polyset

      The geometry represents a complex shape made up of several
      polygons.  This collision type should not be overused, as it
      provides the least optimization benefit.

    Sphere

      The geometry represents a sphere.  The vertices in the group are
      averaged together to determine the sphere's center and radius.

    InvSphere

      The geometry represents an inverse sphere.  This is the same as
      Sphere, with the normal inverted, so that the solid part of an
      inverse sphere is the entire world outside of it.  Note that an
      inverse sphere is in infinitely large solid with a finite hole
      cut into it.

    Tube

      The geometry represents a tube.  This is a cylinder-like shape
      with hemispherical endcaps; it is sometimes called a capsule or
      a lozenge in other packages.  The smallest tube shape that will
      fit around the vertices is used.


    The flags may be any zero or more of:

    event

      Throws the name of the <Collide> entry, or the name of the
      surface if the <Collide> entry has no name, as an event whenever
      an avatar strikes the solid.  This is the default if the
      <Collide> entry has a name.

    intangible

      Rather than being a solid collision surface, the defined surface
      represents a boundary.  The name of the surface will be thrown
      as an event when an avatar crosses into the interior, and
      name-out will be thrown when an avater exits.

    descend

      Instead of creating only one collision object of the given type,
      each group descended from this node that contains geometry will
      define a new collision object of the given type.  The event
      name, if any, will also be inherited from the top node and
      shared among all the collision objects.

    keep
 
      Don't discard the visible geometry after using it to define a
      collision surface; create both an invisible collision surface
      and the visible geometry.

    level

      Stores a special effective normal with the collision solid that
      points up, regardless of the actual shape or orientation of the
      solid.  This can be used to allow an avatar to stand on a
      sloping surface without having a tendency to slide downward.


  <ObjectType> { type }

    This is a short form to indicate one of several pre-canned sets of
    attributes.  Type may be any word, and a Config definition will be
    searched for by the name "egg-object-type-word", where "word" is
    the type word.  This definition may contain any arbitrary egg
    syntax to be parsed in at this group level.

    A number of predefined ObjectType definitions are provided:

    barrier

      This is equivalent to <Collide> { Polyset descend }.  The
      geometry defined at this root and below defines an invisible
      collision solid.

    trigger

      This is equivalent to <Collide> { Polyset descend intangible }.
      The geometry defined at this root and below defines an invisible
      trigger surface.

    sphere

      Equivalent to <Collide> { Sphere descend }.  The geometry is
      replaced with the smallest collision sphere that will enclose
      it.  Typically you model a sphere in polygons and put this flag
      on it to create a collision sphere of the same size.

    tube

      Equivalent to <Collide> { Tube descend }.  As in sphere, above,
      but the geometry is replaced with a collision tube (a capsule).
      Typically you will model a capsule or a cylinder in polygons.

    bubble

      Equivalent to <Collide> { Sphere keep descend }.  A collision
      bubble is placed around the geometry, which is otherwise
      unchanged.

    ghost

      Equivalent to <Scalar> collide-mask { 0 }.  It means that the
      geometry beginning at this node and below should never be
      collided with--characters will pass through it.

    backstage

      This has no equivalent; it is treated as a special case.  It
      means that the geometry at this node and below should not be
      translated.  This will normally be used on scale references and
      other modeling tools.

    There may also be additional predefined egg object types not
    listed here; see the *.pp files that are installed into the etc
    directory for a complete list.

  <Transform> { transform-definition }

    This specifies a matrix transform at this group level.  This
    defines a local coordinate space for this group and its
    descendents.  Vertices are still specified in world coordinates
    (in a vertex pool), but any geometry assigned to this group will
    be inverse transformed to move its vertices to the local space.

    The transform definition may be any sequence of zero or more of
    the following.  Transformations are post multiplied in the order
    they are encountered to produce a net transformation matrix.
    Rotations are defined as a counterclockwise angle in degrees about
    a particular axis, either implicit (about the x, y, or z axis), or
    arbitrary.  Matrices, when specified explicitly, are row-major.

      <Translate> { x y z }
      <RotX> { degrees }
      <RotY> { degrees }
      <RotZ> { degrees }
      <Rotate> { degrees x y z }
      <Scale> { x y z }
      <Scale> { s }

      <Matrix4> {
        00 01 02 03
        10 11 12 13
        20 21 22 23
        30 31 32 33
      }

    Note that the <Transform> block should always define a 3-d
    transform when it appears within the body of a <Group>, while it
    may define either a 2-d or a 3-d transform when it appears within
    the body of a <Texture>.  See <Texture>, above.


  <VertexRef> { indices <Ref> { pool-name } }

    This moves geometry created from the named vertices into the
    current group, regardless of the group in which the geometry is
    actually defined.  See the <Joint> description, below.


<Instance> name { group-body }

  An <Instance> node is exactly like a <Group> node, except that
  vertices referenced by geometry created under the <Instance> node
  are not assumed to be given in world coordinates, but are instead
  given in the local space of the <Instance> node itself (including
  any transforms given to the node).

  In other words, geometry under an <Instance> node is defined in
  local coordinates.  In principle, similar geometry can be created
  under several different <Instance> nodes, and thus can be positioned
  in a different place in the scene each instance.  This doesn't
  necessarily imply the use of shared geometry in the Performer scene
  graph, but see the <Ref> syntax, below.

  This is particularly useful in conjunction with a <File> entry, to
  load external file references at places other than the origin.


  A special syntax of <Instance> entries does actually create shared
  geometry in the scene graph.  However, please note that this special
  syntax is not supported by Panda3D at this time.  It is documented
  here against the day that it will be supported.

  The syntax is:

<Instance> name {
  <Ref> { group-name } 
  [ <Ref> { group-name } ... ]
}

  In this case, the referenced group name will appear as a duplicate
  instance in this part of the tree.  Local transforms can be applied
  and are relative to the reference group's transform.


<Joint> name { [transform] [ref-list] [joint-list] }

  A joint is a highly specialized kind of grouping node.  A tree of
  joints is used to specify the skeletal structure of an animated
  character.

  A joint may only contain one of three things.  It may contain a
  <Transform> entry, as above, which defines the joint's unanimated
  (rest) position; it may contain lists of assigned vertices or CV's;
  and it may contain other joints.

  A tree of <Joint> nodes only makes sense within a character
  definition, which is created by applying the <DART> flag to a group.
  See <DART>, above.

  The vertex assignment is crucial.  This is how the geometry of a
  character is made to move with the joints.  The character's geometry
  is actually defined outside the joint tree, and each vertex must be
  assigned to one or more joints within the tree.

  This is done with zero or more <VertexRef> entries per joint, as the
  following:

  <VertexRef> { indices [<Scalar> membership { m }] <Ref> { pool-name } }

  This is syntactically similar to the way vertices are assigned to
  polygons.  Each <VertexRef> entry can assign vertices from only one
  vertex pool (but there may be many <VertexRef> entries per joint).
  Indices is a list of vertex numbers from the specied vertex pool, in
  an arbitrary order.

  The membership scalar is optional.  If specified, it is a value
  between 0.0 and 1.0 that indicates the fraction of dominance this
  joint has over the vertices.  This is used to implement
  soft-skinning, so that each vertex may have partial ownership in
  several joints.

  The <VertexRef> entry may also be given to ordinary <Group> nodes.
  In this case, it treats the geometry as if it was parented under the
  group in the first place.  Non-total membership assignments are
  meaningless.


<Bundle> name { table-list }
<Table> name { table-body }

  A table is a set of animated values for joints.  A tree of tables
  with the same structure as the corresponding tree of joints must be
  defined for each character to be animated.  Such a tree is placed
  under a <Bundle> node, which provides a handle within Panda to the
  tree as a whole.

  Bundles may only contain tables; tables may contain more tables,
  bundles, or any one of the following (<Scalar> entries are optional,
  and default as shown):

  <S$Anim> name { 
      <Scalar> fps { 24 }
      <V> { values }
  }

    This is a table of scalar values, one per frame.  This may be
    applied to a morph slider, for instance.


  <Xfm$Anim> name {
      <Scalar> fps { 24 }
      <Scalar> order { srpht }
      <Scalar> contents { ijkabcrphxyz }
      <V> { values }
  }

    This is a table of matrix transforms, one per frame, such as may
    be applied to a joint.  The "contents" string consists of a subset
    of the letters "ijkabcrphxyz", where each letter corresponds to a
    column of the table; <V> is a list of numbers of length(contents)
    * num_frames.  Each letter of the contents string corresponds to a
    type of transformation:

      i, j, k - scale in x, y, z directions, respectively
      a, b, c - shear in xy, xz, and yz planes, respectively
      r, p, h - rotate by pitch, heading, roll
      x, y, z - translate in x, y, z directions

    The net transformation matrix specified by each row of the table
    is defined as the net effect of each of the individual columns'
    transform, according to the corresponding letter in the contents
    string.  The order the transforms are applied is defined by the
    order string:

      s       - all scale and shear transforms
      r, p, h - individual rotate transforms
      t       - all translation transforms


  <Xfm$Anim_S$> name {
      <Scalar> fps { 24 }
      <Scalar> order { srpht }
      <S$Anim> i { ... }
      <S$Anim> j { ... }
      ...
  }

    This is a variant on the <Xfm$Anim> entry, where each column of
    the table is entered as a separate <S$Anim> table.  This syntax
    reflects an attempt to simplify the description by not requiring
    repetition of values for columns that did not change value during
    an animation sequence.


  <VertexAnim> name { 
      <Scalar> width { table-width }
      <Scalar> fps { 24 }
      <V> { values }
  }

    This is a table of vertex positions, normals, texture coordinates,
    or colors.  These values will be subsituted at runtime for the
    corresponding values in a <DynamicVertexPool>.  The name of the
    table should be "coords", "norms", "texCoords", or "colors",
    according to the type of values defined.  The number table-width
    is the number of floats in each row of the table.  In the case of
    a coords or norms table, this must be 3 times the number of
    vertices in the corresponding dynamic vertex pool.  (For texCoords
    and colors, this number must be 2 times and 4 times, respectively.)



MISCELLANEOUS

<File> { filename }

  This includes a copy of the referenced egg file at the current
  point.  This is usually placed under an <Instance> node, so that the
  current transform will apply to the geometry in the external file.
  The extension ".egg" is implied if it is omitted.

  As with texture filenames, the filename may be a relative path, in
  which case the current egg file's directory is searched first, and
  then the model-path is searched.

