Skeleton + Morph Animation

Simple example using a skeleton together with a morph animation.

Following references were helpful:

panda3d.cvs.sourceforge.net/pand … iew=markup
ripple.egg.pz included in the Panda3D release.

Save the snippet to the file “cube.egg”
Start the animation with “pview -a cube.egg”

<CoordinateSystem> { Z-up }

<Group> MyGroup {
  <Dart> { 1 }
  <VertexPool> MyVertexPool {
    <Vertex> 0 { 1.0 1.0 -1.0
      <Dxyz> MyTarget { -1.0 -1.0 1.0 }
    }
    <Vertex> 1 { 1.0 -1.0 -1.0
      <Dxyz> MyTarget { -1.0 1.0 1.0 }
    }
    <Vertex> 2 { -1.0 -1.0 -1.0
      <Dxyz> MyTarget { 1.0 1.0 1.0 }
    }
    <Vertex> 3 { -1.0 1.0 -1.0
      <Dxyz> MyTarget { 1.0 -1.0 1.0 }
    }
    <Vertex> 4 { 1.0 1.0 1.0
      <Dxyz> MyTarget { -1.0 -1.0 -1.0 }
    }
    <Vertex> 5 { -1.0 1.0 1.0
      <Dxyz> MyTarget { 1.0 -1.0 -1.0 }
    }
    <Vertex> 6 { -1.0 -1.0 1.0
      <Dxyz> MyTarget { 1.0 1.0 -1.0 }
    }
    <Vertex> 7 { 1.0 -1.0 1.0
      <Dxyz> MyTarget { -1.0 1.0 -1.0 }
    }
    <Vertex> 8 { 1.0 1.0 -1.0
      <Dxyz> MyTarget { -1.0 -1.0 1.0 }
    }
    <Vertex> 9 { 1.0 1.0 1.0
      <Dxyz> MyTarget { -1.0 -1.0 -1.0 }
    }
    <Vertex> 10 { 1.0 -1.0 1.0
      <Dxyz> MyTarget { -1.0 1.0 -1.0 }
    }
    <Vertex> 11 { 1.0 -1.0 -1.0
      <Dxyz> MyTarget { -1.0 1.0 1.0 }
    }
    <Vertex> 12 { 1.0 -1.0 -1.0
      <Dxyz> MyTarget { -1.0 1.0 1.0 }
    }
    <Vertex> 13 { 1.0 -1.0 1.0
      <Dxyz> MyTarget { -1.0 1.0 -1.0 }
    }
    <Vertex> 14 { -1.0 -1.0 1.0
      <Dxyz> MyTarget { 1.0 1.0 -1.0 }
    }
    <Vertex> 15 { -1.0 -1.0 -1.0
      <Dxyz> MyTarget { 1.0 1.0 1.0 }
    }
    <Vertex> 16 { -1.0 -1.0 -1.0
      <Dxyz> MyTarget { 1.0 1.0 1.0 }
    }
    <Vertex> 17 { -1.0 -1.0 1.0
      <Dxyz> MyTarget { 1.0 1.0 -1.0 }
    }
    <Vertex> 18 { -1.0 1.0 1.0
      <Dxyz> MyTarget { 1.0 -1.0 -1.0 }
    }
    <Vertex> 19 { -1.0 1.0 -1.0
      <Dxyz> MyTarget { 1.0 -1.0 1.0 }
    }
    <Vertex> 20 { 1.0 1.0 1.0
      <Dxyz> MyTarget { -1.0 -1.0 -1.0 }
    }
    <Vertex> 21 { 1.0 1.0 -1.0
      <Dxyz> MyTarget { -1.0 -1.0 1.0 }
    }
    <Vertex> 22 { -1.0 1.0 -1.0
      <Dxyz> MyTarget { 1.0 -1.0 1.0 }
    }
    <Vertex> 23 { -1.0 1.0 1.0
      <Dxyz> MyTarget { 1.0 -1.0 -1.0 }
    }
  }
  <Polygon> {
    <Normal> { 0.0 0.0 -1.0 }
    <VertexRef> { 0 1 2 3 <Ref> { MyVertexPool } }
  }
  <Polygon> {
    <Normal> { 0.0 0.0 1.0 }
    <VertexRef> { 4 5 6 7 <Ref> { MyVertexPool } }
  }
  <Polygon> {
    <Normal> { 1.0 0.0 0.0 }
    <VertexRef> { 8 9 10 11 <Ref> { MyVertexPool } }
  }
  <Polygon> {
    <Normal> { 0.0 -1.0 0.0 }
    <VertexRef> { 12 13 14 15 <Ref> { MyVertexPool } }
  }
  <Polygon> {
    <Normal> { -1.0 0.0 0.0 }
    <VertexRef> { 16 17 18 19 <Ref> { MyVertexPool } }
  }
  <Polygon> {
    <Normal> { 0.0 1.0 0.0 }
    <VertexRef> { 20 21 22 23 <Ref> { MyVertexPool } }
  }
  <Joint> MyJoint {
    <Transform> {
      <Matrix4> {
        1.0 0.0 0.0 0.0
        0.0 1.0 0.0 0.0
        0.0 0.0 1.0 0.0
        0.0 0.0 0.0 1.0
      }
    }
    <VertexRef> {
      0 1 2 3 4 5 6 7 8 9 10 11 12
      13 14 15 16 17 18 19 20 21 22 23
      <Scalar> membership { 1.0 }
      <Ref> { MyVertexPool }
    }
  }
}

<Table> {
  <Bundle> MyGroup {
    <Table> morph {
      <S$Anim> MyTarget {
        <Scalar> fps { 5 }
        <V> {
          0.0
          0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
          1.0
          0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1
          0.0
        }
      }
    }

    <Table> <skeleton> {
      <Table> MyJoint {
        <Xfm$Anim> xform {
          <Scalar> fps { 5 }
          <Scalar> order { t }
          <Scalar> contents { z }
          <V> {
            0.0
            1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
            10.0
            9.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0
            0.0
          }
        }
      }
    }
  }
}
  • Every string that is prefixed with My in this example you can modify.
  • You can include more than one animation in one egg file. Advantage: You don’t have to define animations when you load an actor.
  • pview is only able to play an animation if group name = bundle name (if the animation is not in a separate file).
  • The Python Actor class is able to play any animation inside one egg file. Use: actor.loop(“MyBundleName”).
  • “” is a keyword (see characterMaker.cxx)
  • “xform” is a keyword (see e.g. animBundleMaker.cxx)
  • “morph” is a keyword (see characterMaker.cxx)
  • Python Actors have a method animPanel which may be helpful.

€: David has fixed everything I was ranting about.

It’s a good point–the eggSyntax document describes the syntactical convention of

and , but not the semantic conventions (it doesn’t mention “xform”, “morph”, and “”, for instance).

Also, ripple.egg was a little nonstandard because it originally came from a very old egg file converter. This means that it did things like, for instance, putting the morph name within the braces instead of before it. The egg loader can still understand these old files, but it does seem weird to a human browsing it. I just ran it through egg-trans to bring it up to the modern world.

Note that you can use pview -a to bring up a mini-animation control panel. You can still only view and control the one animation with it, though. If you have multiple animations in your egg file, you’ll need to use the actor’s anim panel.

David

I just added the following text to the end of eggSyntax.txt in an attempt to remedy this oversight:

ANIMATION STRUCTURE

  Unanimated models may be defined in egg files without much regard to
  any particular structure, so long as named entries like VertexPools
  and Textures appear before they are referenced.

  However, a certain rigid structural convention must be followed in
  order to properly define an animated skeleton-morph model and its
  associated animation data.

  The structure for an animated model should resemble the following:

<Group> CHARACTER_NAME {
  <Dart> { 1 }
  <Joint> JOINT_A {
    <Transform> { ... }
    <VertexRef> { ... }
    <Group> { <Polygon> ... }
    <Joint> JOINT_B {
      <Transform> { ... }
      <VertexRef> { ... }
      <Group> { <Polygon> ... }
    }
    <Joint> JOINT_C {
      <Transform> { ... }
      <VertexRef> { ... }
      <Group> { <Polygon> ... }
    }
    ...
  }
}

  The <Dart> flag is necessary to indicate that this group begins an
  animated model description.  Without the <Dart> flag, joints will be
  treated as ordinary groups, and morphs will be ignored.

  In the above, UPPERCASE NAMES represent an arbitrary name that you
  may choose.  The name of the enclosing group, CHARACTER_NAME, is
  taken as the name of the animated model.  It should generally match
  the bundle name in the associated animation tables.

  Within the <Dart> group, you may define an arbitrary hierarchy of
  <Joint> entries.  There may be as many <Joint> entries as you like,
  and they may have any nesting complexity you like.  There may be
  either one root <Joint>, or multiple roots.  However, you must
  always include at least one <Joint>, even if your animation consists
  entirely of morphs.

  Polygons may be directly attached to joints by enclosing them within
  the <Joint> group, perhaps with additional nesting <Group> entries,
  as illustrated above.  This will result in the polygon's vertices
  being hard-assigned to the joint it appears within.  Alternatively,
  you declare the polygons elsewhere in the egg file, and use
  <VertexRef> entries within the <Joint> group to associate the
  vertices with the joints.  This is the more common approach, since
  it allows for soft-assignment of vertices to multiple joints.

  It is not necessary for every joint to have vertices at all.  Every
  joint should include a transform entry, however, which defines the
  initial, resting transform of the joint.  If a transform is omitted,
  the identity transform is assumed.

  Some of the vertex definitions may include morph entries, as
  described in MORPH DESCRIPTION ENTRIES, above.  These are meaningful
  only for vertices that are assigned, either implicitly or
  explicitly, to at least one joint.

  You may have multiple versions of a particular animated model--for
  instance, multiple different LOD's, or multiple different clothing
  options.  Normally each different version is stored in a different
  egg file, but it is also possible to include multiple versions
  within the same egg file.  If the different versions are intended to
  play the same animations, they should all have the same
  CHARACTER_NAME, and their joint hierarchies should exactly match in
  structure and names.

  The structure for an animation table should resemble the following:

<Table> {
  <Bundle> CHARACTER_NAME {
    <Table> "<skeleton>" {
      <Table> JOINT_A {
        <Xfm$Anim_S$> xform {
          <Char*> order { sphrt }
          <Scalar> fps { 24 }
          <S$Anim> x { 0 0 10 10 20 ... }
          <S$Anim> y { 0 0 0 0 0 ... }
          <S$Anim> z { 20 20 20 20 20 ... }
        }
        <Table> JOINT_B {
          <Xfm$Anim_S$> xform {
            <Char*> order { sphrt }
            <Scalar> fps { 24 }
            <S$Anim> x { ... }
            <S$Anim> y { ... }
            <S$Anim> z { ... }
          }
        }
        <Table> JOINT_C {
          <Xfm$Anim_S$> xform {
            <Char*> order { sphrt }
            <Scalar> fps { 24 }
            <S$Anim> x { ... }
            <S$Anim> y { ... }
            <S$Anim> z { ... }
          }
        }
      }
    }
    <Table> morph {
      <S$Anim> MORPH_A {
        <Scalar> fps { 24 }
        <V> { 0 0 0 0.1 0.2 0.3 1 ... }
      }
      <S$Anim> MORPH_B {
        <Scalar> fps { 24 }
        <V> { ... }
      }
      <S$Anim> MORPH_C {
        <Scalar> fps { 24 }
        <V> { ... }
      }
    }
  }
}

  The <Bundle> entry begins an animation table description.  This
  entry must have at least one child: a <Table> named "<skeleton>"
  (this name is a literal keyword and must be present).  The children
  of this <Table> entry should be a hierarchy of additional <Table>
  entries, one for each joint in the model.  The joint structure and
  names defined by the <Table> hierarchy should exactly match the
  joint structure and names defined by the <Joint> hierarchy in the
  corresponding model.

  Each <Table> that corresponds to a joint should have one child, an
  <Xfm$Anim_S$> entry named "xform" (this name is a literal keyword
  and must be present).  Within this entry, there is a series of up to
  twelve <S$Anim> entries, each with a one-letter name like "x", "y",
  or "z", which define the per-frame x, y, z position of the
  corresponding joint.  There is one numeric entry for each frame, and
  all frames represent the same length of time.  You can also define
  rotation, scale, and shear.  See the full description of
  <Xfm$Anim_S$>, above.

  Within a particular animation bundle, all of the various components
  throughout the various <Tables> should define the same number of
  frames, with the exception that if any of them define exactly one
  frame value, that value is understood to be replicated the
  appropriate number of times to match the number of frames defined by
  other components.

  (Note that you may alternatively define an animation table with an
  <Xfm$Anim> entry, which defines all of the individual components in
  one big matrix instead of individually.  See the full description
  above.)

  Each joint defines its frame rate independently, with an "fps"
  scalar.  This determines the number of frames per second for the
  frame data within this table.  Typically, all joints have the same
  frame rate, but it is possible for different joints to animate at
  different speeds.

  Each joint also defines the order in which its components should be
  composed to determine the complete transform matrix, with an "order"
  scalar.  This is described in more detail above.


  If any of the vertices in the model have morphs, the top-level
  <Table> should also include a <Table> named "morph" (this name is
  also a literal keyword).  This table in turn contains a list of
  <S$Anim> entries, one for each named morph description.  Each table
  contains a list of numeric values, one per frame; as with the joint
  data, there should be the same number of numeric values in all
  tables, with the exception that just one value is understood to mean
  hold that value through the entire animation.

  The "morph" table may be omitted if there are no morphs defined in
  the model.

  There should be a separate <Bundle> definition for each different
  animation.  The <Bundle> name should match the CHARACTER_NAME used
  for the model, above.  Typically each bundle is stored in a separate
  egg file, but it is also possible to store multiple different
  animation bundles within the same egg file.  If you do this, you may
  violate the CHARACTER_NAME rule, and give each bundle a different
  name; this will become the name of the animation in the Actor
  interface.

  Although animations and models are typically stored in separate egg
  files, it is possible to store them together in one large egg file.
  The Actor interface will then make available all of the animations
  it find within the egg file, by bundle name.

Hope I have not ruined your saturday. Everything seems clear now. I have one more little question. eggSyntax says:

<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>.

But one of your examples is using:

<Char*> order { sphrt }

Even egg-trans convertes some 's to <Char*>'s.

I guess we aren’t phasing out Char* that aggressively. :slight_smile:

For whatever reason, the egg writer still writes Char* for the order and contents fields; I don’t really know why. But for this reason, almost all of our egg files use <Char*> order instead of order, and that’s what my example reflects.

It’s a good point, though. If we’re really phasing it out, we should actually stop using it.

David