gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Skeletal Animation Help Needed

#167402 - evilpuppy - Tue Mar 10, 2009 10:48 pm

I can?t find any info or examples on skeletal animation. I have seen a few projects use it so I know it?s possible. Any help would be appreciated.
_________________
If no one knows you did it that means it's legal.

#167434 - Holland - Thu Mar 12, 2009 12:01 am

actually, i'm going to second this. i was curious what sort of animation the DS is capable of. do most people do rigid skinning or smooth skinning or what?

#167435 - TwentySeven - Thu Mar 12, 2009 1:11 am

We went with meshes updated with fresh vertex positions and normals for every keyframe.

Our models are stripped and fairly low poly (250 tris or so), so the number of vertexes per frame is pretty slim, only a kb or so.

On top of that, interpolation between vertex points is MUCH cheaper then a matrix multiply per vertex, and because the normals don't change very much between keyframes, they typically don't even need interpolating.

#167437 - sajiimori - Thu Mar 12, 2009 2:21 am

We do skeletal animation with only 1 bone influencing each vertex. We don't do any per-vertex operations.

The render pipeline for a single character goes something like this:

1. Set up a material.
1. Load as many matrices as you can onto the hardware matrix stack, for body parts that use that material.
2. DMA a display list to the FIFO. The display list includes commands to switch between the matrices you loaded earlier, so you can render many body parts in a single DMA, as long as they all use the same material.
3. Set up a new material and repeat.

Characters with 1 material and less than 31 bones can typically render in a single DMA. The CPU is only responsible for setting up the material and loading the matrices, the latter being the larger CPU hit by far, but it's not that much work if your animation data contains all the matrices for each frame.

It gets slower if you want to blend between multiple animations, or between two frames in a single animation (or both), but I'd imagine this approach is much faster than what TwentySeven described, because the CPU doesn't touch any vertices, and the animation data is much more compact.

My last DS game could have 6-7 characters on-screen, each with 400-450 polys (half of which are backfaces, of course) and 30-40 bones, at a consistent 30fps.

#167440 - TwentySeven - Thu Mar 12, 2009 4:43 am

Blending between keyframes per vertexes is only done if you're interpolating, which is mostly optional. We pretty much only use it for the important stuff, most enemys and world props don't get or need it.

If you don't interpolate, theres no cpu overhead other then doing the draw calls.

Not to get into a pissing contest, but I can max out the renderer with models for about 30% cpu usage, 60hz. (this is 12-13 models at ~250 faces each)

Theres just not that much cpu work involved.

Code:

//Eats a model and modelanim and draws it
//assumes the matrixes,textures and whatnot are already set
void DrawModel(Model_t *model, Animation_t *modelanim, int frame)
{
   int16 pointcount;
   int j,r;
   int16 *strip;

   int16 point;
   int16 offset;
   uvpoint_t *data;

   if (!model || !modelanim) return;

   strip = (int16*)model->data;
   
   //Debugout("mfc: %d %d",modelanim->framecount,frame);
   frame = frame % modelanim->framecount;
   offset = modelanim->vertcount * frame;
   

   for (j=0;j<model->stripcount;j++)
   {
      glBegin(GL_TRIANGLE_STRIP);

      glColor3f(1 ,1,1);

      //First 2 bytes are the point count this strip
      pointcount= *(strip);         
      
      //Index into the first index into this, followed by 2 uv pairs
      data = (uvpoint_t*)((byte*)strip+2);
   
      //Renders a triangle strip one point at a time
      //point is the "index" of the vert
      for (r=0;r<pointcount;r++)
      {
         point = data->index;
         GFX_NORMAL = modelanim->verts[offset+point].normal;
         GFX_TEX_COORD = (TEXTURE_PACK(data->uvindex0 , data->uvindex1));
         
         glVertex3v16  (modelanim->verts[offset+point].vert[0]  , modelanim->verts[offset+point].vert[1] , modelanim->verts[offset+point].vert[2]   );            
         data++;
      }

      glEnd();

      //Move strip along by 2 bytes + 6 bytes per point entry
      strip= (int16*) ( (byte*)strip +2 +(6*pointcount) );
   }

}

#167442 - DiscoStew - Thu Mar 12, 2009 6:02 am

If you are going for something extremely simple, you can just make an "object" out of multiple meshes, and each mesh is attached to one specific bone within the group in the skeleton. That way, when drawing, you only need to load the matrix for each bone once, then draw the meshes associated with that bone. It can be done into a single display list if you order it correctly.

If you are planning on rigid-skinned, then there is more work to be done as others have shown. The entire object can be a single mesh (or more), and vertices are assigned to each bone rather than full polygones/meshes to each bone. It can still be set into a single display list, but expect calls to load specific bones multiple times since not all polygons will be set to a single bone. If you organize the polygons efficiently, you can reduce that number.

Strips work well for these modes, as well as display lists.

Smooth-skinned objects means less automation, and more CPU consumption if you ask me, because it can't generally be done with just display lists, since smooth-skinned objects allow more than 1 bone influence per vertex. Now, even if you had a single display list, and filled in the display list with updated positions of each vertex manually, you still would be doing a lot of manual processing. I would recommend going with rigid skinning and/or anything else that doesn't require so much from the DS's CPU.
_________________
DS - It's all about DiscoStew

#167443 - sajiimori - Thu Mar 12, 2009 7:55 am

Oh yeah TwentySeven, that is fast -- I thought you were doing something with each vertex, my mistake. It sounds like the classic speed-vs-size tradeoff (with added complications if you consider the time spent on card reads, but that totally depends on the situation).

#167718 - kiniou - Mon Mar 23, 2009 5:51 pm

Hello sajiimori,

I am working on a export script for Blender which actually exports binary CallList and I'm looking to extend it to support bones animation.

The 4 steps you describe earlier are what I'm trying to do but I need some help with the part 2:
sajiimori wrote:

2. DMA a display list to the FIFO. The display list includes commands to switch between the matrices you loaded earlier, so you can render many body parts in a single DMA, as long as they all use the same material.


What FIFO commands must be used to do the matrix switch?
_________________
It's time to code...