Game Development Community

dev|Pro Game Development Curriculum

Increase the number of animations per character

by Orion Elenzil · 01/18/2006 (5:19 pm) · 7 comments

This resource is very heavily based on Chris Calef's and other's discussion here.
It should have slightly better performance than the code there however,
because it strips off the file extension and the sequence name as well.

Motivation:
TGE 1.3 has a limit to the number of animations you can have per character.
The limit stems from the fact that the Names of the animation sequences must all be sent from serve to client in one buffer of about 1500 bytes.
I hear that TGE 1.4 takes care of this.

In our case, we've got about 100 animations which fall under this modification,
and by using this mod our byte-stream needs are reduced from about 60 bytes per animation to about 10,
allowing us to fit well within 1500-byte limit.

The sequences come from the player.cs file which is typically next to player.dts.

A typical sequence definition from that file looks something like this:
sequence28 = "./jump_around.dsq jump_around";

Before sending it over the wire, the engine expands "./jump_around.dsq jump_around" into something like "starter.fps/data/shapes/characters/jump_around.dsq jump_around".

This resource handles the (for us) very common situation where:
1. the animation file is a sibling of the .dts file
2. the animation file has the extension ".dsq".
3. the filename of the .dsq is the same as the name as the animation.

- this third item might not be common for everyone, but basically i highly recommend making it so if you're running into this issue.

If all three of these criteria are met then a flag is set in the bitstream to indicate it, and only the name of the animation is sent.

so instead of "starter.fps/data/shapes/characters/jump_around.dsq jump_around", we just send "jump_around".

Since a 1-bit flag is inserted in the bitstream to indicate whether this compression has been done or not,
this mod is backwards compatible with standard sequence names & locations.

tsShapeConstruct.cc
Basically packData() and unpackData() are rewritten.
Don't forget to add 'stockExtn' in front of packData().
[b]static const char* stockExtn = ".dsq";[/b]

void TSShapeConstructor::packData(BitStream* stream)
{
   Parent::packData(stream);
   stream->writeString(mShape);

   S32 count = 0;
   for (S32 b=0; b<MaxSequences; b++)
      if (mSequence[b])
         count++;
   stream->writeInt(count,NumSequenceBits);

   char stockPath[256];
   S32  stockPathLen;
   char pathBuf[256];
   char nameBuf[64];
   char* cp;
   bool isStockPath;
   bool isStockExtn;
   bool isStockName;

   dStrcpy(stockPath, mShape);
   cp = dStrrchr(stockPath, '/');
   if (cp)
      *(cp+1) = '[[60c21cf34d5c1]]';
   stockPathLen = dStrlen(stockPath);

   for (S32 i=0; i<MaxSequences; i++) {
      if (mSequence[i]) {
         dStrcpy(pathBuf, mSequence[i]);
         cp = dStrrchr(pathBuf, ' ');
         isStockPath = isStockExtn = isStockName = false;
         if (cp) {
            *cp = '[[60c21cf34d5c1]]';
            dStrcpy(nameBuf, cp + 1);
            isStockPath = !dStrnicmp(stockPath, pathBuf, stockPathLen);
            cp = dStrrchr(pathBuf, '.');
            if (cp) {
               isStockExtn = !dStricmp (cp, stockExtn);
               if (isStockExtn) {
                  *cp = '[[60c21cf34d5c1]]';
                  isStockName = !dStricmp(nameBuf, pathBuf + stockPathLen);
               }
            }
         }

         if (stream->writeFlag(isStockPath && isStockExtn && isStockName))
            stream->writeString(nameBuf);
         else
            stream->writeString(mSequence[i]);
      }
   }
}

void TSShapeConstructor::unpackData(BitStream* stream)
{
   Parent::unpackData(stream);
   mShape = stream->readSTString();

   bool  isStock;
   char  stockPath[256];
   char  pathBuf  [256];
   char* cp;

   dStrcpy(stockPath, mShape);
   cp = dStrrchr(stockPath, '/');
   if (cp)
      *(cp+1) = '[[60c21cf34d5c1]]';
   
   S32 i = 0, count = stream->readInt(NumSequenceBits);
   for (; i<count; i++) {
      isStock = stream->readFlag();
      mSequence[i] = stream->readSTString();
      if (isStock) {
         dSprintf(pathBuf, sizeof(pathBuf), "%s%s%s %s", stockPath, mSequence[i], stockExtn, mSequence[i]);
         mSequence[i] = StringTable->insert(pathBuf);
      }
   }
   while (i<MaxSequences)
      mSequence[i++] = NULL;
}

#1
01/18/2006 (11:00 pm)
This just solves the problem the TsShapeConstructor has with loading more than a certain amount of .dsq files. This can actualy be fixed without any engine coding by sticking all your animations into a small number of .dsqs. We have well over 150 animations and don't have a problem with the shape constructor. There is, however, a "bug" in the Player code that limits that amount of animations you can load.

In Player.h, find:
NumExtraActionAnims
And set it to a higher number; we have ours set at 512.
#2
01/21/2006 (12:06 pm)
i didn't realize you can put multiple animations in a single .dsq.
how do you trigger the one you want ?
- by just knowing the cut-points, or something nicer ?
#3
01/23/2006 (9:18 pm)
did 1.4 fix this issue? or is it still an issue?
#4
01/24/2006 (9:07 am)
1.4 apparently alleviated it by implementing the relative paths name thing, but you have to set a #define flag and recompile.
check out daniel eden's post in this thread.
#5
03/09/2006 (10:34 pm)
Orion, to answer yuor question about how you trigger the one you want, you just use the name of the sequence helper you used in Max, Maya or whatever. The sequence helper itself marks the begin and end times, so you don't have to know about them later. You basically just make one long timeline with a bunch of different sequence helpers pointing to various time segments then export it to a single dsq.
#6
04/21/2006 (9:25 am)
How I play one animation in my characters?
My characters have many attack animations, I want what they random this animations.
#7
01/31/2007 (6:37 am)
Hi, warning about the built-in 1.4 fix. After defining TS_IS_RELATIVE_SEQUENCES i'm getting a crash upon mission load: Run-Time Check Failure #2 - Stack around the variable 'pathedSeq' was corrupted. This is happening in debug mode. Do not know about release.