/* * RsdAnim Animation Library v3 *================================ * New stuff from version 2: * o New format "Type2" RsdANIM MIMe data support. * This is a block based format with the following advantages over the original * format: * 1) Smaller. The original format had an entry for every vertex for every keyframe * (taking up num_vertices*8*n_keyframes of memory for an animation). The Type2 * format only has entries for vertices that move (allowing a little padding for * speed), using much less memory. * 2) Faster. As the GPU no longer does larger numbers of {X,Y,Z}+={0,0,0} type ops * you can do much more animation in the same space of time. * Unfortunately you don't get something for nothing, so this format is a lot more * compilcated than the old format, and the library code is correspondingly more complex. * The programming interface is the same though (old code will build with the new library). * Type2 animation data will be auto-detected in the library, so you don't have to do * anything differently. */ #include #include #include #include #include #include "rsdanim.h" //void ForceMessage(char *msg); #define ForceMessage(X) printf(X) /* * Initialisation * --- * Call this once for every object you want to animate. It preserves a copy of your baseframe * vertices so you can restore them at any point later on... * PARAMETERS: * obj : pointer to the object (already linked & stuff) * mime : pointer to the MIME_MOVEMENT structure generated by RsdAnim (this holds the animation sequence) * mh : pointer to a MIME_HANDLE for InitMIMe to fill in for you */ TMD_OBJECT *InitMIMe(GsDOBJ2 *obj, MIME_MOVEMENT *mime, MIME_HANDLE *mh) { TMD_OBJECT *o=(TMD_OBJECT*)obj->tmd; if(!strncmp((char *)mime,"RSDANIM MIMe TYPE2",18)) { char buf[200]; // type 2 specific handler stuff mh->type=2; mh->md.segments=(MIME_SEGMENT*)(((unsigned char *)mime)+24); sprintf(buf,"InitMIMe(): type=2, num_segments=%d\n",*(((unsigned long*)mime)+5)); ForceMessage(buf); }else{ // type 1 specific handler stuff mh->type=1; mh->md.movement=mime; } mh->vectors=(SVECTOR*)o->vert_top; mh->n_vert=o->n_vert; mh->frame=0; mh->base_vectors=malloc(sizeof(SVECTOR)*o->n_vert); mh->step=0; memcpy(mh->base_vectors,mh->vectors,sizeof(SVECTOR)*o->n_vert); return o; } /* * Extended Initialise *---- * If you've got an object doing several movements (say, animating a human body which can walk or * jump or punch, etc) use InitMIMe() for the first movement and InitMIMeX() for all the rest * this simply avoids making repeated copies of the baseframe vertices (you only need the one, and * all movements of an object can reset to baseframe from the one copy). * PARAMETERS: * obj : pointer to the object (already linked & stuff) * mime : pointer to the MIME_MOVEMENT structure generated by RsdAnim * mh : pointer to a MIME_HANDLE for InitMIMe to fill in for you * existing : pointer to a MIME_HANDLE for a movement on off this object already filled in by InitMIMe() */ TMD_OBJECT *InitMIMeX(GsDOBJ2 *obj, MIME_MOVEMENT *mime, MIME_HANDLE *mh, MIME_HANDLE *existing) { TMD_OBJECT *o=(TMD_OBJECT*)obj->tmd; if(!strncmp((char *)mime,"RSDANIM MIMe TYPE2",18)) { // type 2 specific handler stuff mh->type=2; mh->md.segments=(MIME_SEGMENT*)(((unsigned char *)mime)+24); }else{ // type 1 specific handler stuff mh->type=1; mh->md.movement=mime; } mh->vectors=(SVECTOR*)o->vert_top; mh->n_vert=o->n_vert; mh->frame=0; mh->base_vectors=existing->base_vectors; mh->step=0; return o; } /* * Release memory allocated to a MIME_HANDLE *---- * PARAMETERS: * mh : pointer to the handle to release */ void ReleaseMIMe(MIME_HANDLE *mh) { free(mh->base_vectors); mh->base_vectors=NULL; } /* * Link Binary MIMe file (converts offsets to pointers for a loaded file) *---- * PARAMETERS: * m : pointer to a Binary MIMe file in memory (a .MIM file) * Returns: * pointer to MIMe info */ MIME_MOVEMENT *LinkMIMe(unsigned long *m) { int f; char buf[200]; if(!strncmp((char *)m,"RSDANIM MIMe TYPE2",18)) { // Link type 2 MIMe file int ns,bn; MIME_SEGMENT *ms=(MIME_SEGMENT*)(&m[6]); MIME_BLOCK *blk; unsigned char *b0; // number of segments (leave last one alone as it's the array terminator) ns=m[5]-1; sprintf(buf,"LinkMIMe(): type=2 num_segments=%d",ns); ForceMessage(buf); // base of the MIME_BLOCK area b0=((unsigned char*)m)+(ns+1)*sizeof(MIME_SEGMENT)+24; for(f=0; fnum_v - 1)); sprintf(buf,"LinkMIMe(): linking segment=%d, block=%d, blk@0x%lx={\n next=%lx\n start_v=%d\n num_v=%d\n scaler=%d\n dummy=%d\n};\n blocksize(bytes)=%d", f,bn,blk,blk->next,blk->start_v,blk->num_v, blk->scaler,blk->dummy,sizeof(MIME_BLOCK)+(sizeof(SVECTOR)*(blk->num_v - 1))); ForceMessage(buf); // check the magic number in the next pointer if((unsigned long)blk->next==0xDEADC001) { // if it was ok, we can point the next pointer to the following block blk->next=(MIME_BLOCK*)b0; blk=(MIME_BLOCK*)b0; }else{ // check for list termination if(blk->next==NULL) { blk=NULL; sprintf(buf,"LinkMIMe(): reached end of block list for segment %d",f); ForceMessage(buf); }else{ sprintf(buf,"ERROR:LinkMIMe(): invalid MIME_BLOCK magic number=%lx",blk->next); ForceMessage(buf); return NULL; } } bn++; }while(blk); } }else{ // Link type 1 MIMe file unsigned long b=(unsigned long)m; for(f=0; m[f]!=0; f+=2) m[f]=m[f]+b; } return (MIME_MOVEMENT*)m; } /* * Reset Animation to baseframe *---- * PARAMETERS: * mh : pointer to a valid MIME_HANDLE */ void ResetMIMe(MIME_HANDLE *mh) { mh->frame=mh->step=0; memcpy(mh->vectors,mh->base_vectors,sizeof(SVECTOR)*mh->n_vert); } /* * Type1 MIMe animation player * If you only use old style Type1 animation data (all animation from RsdANIM 1.51 or older, * or the Type1 format output option from RsdANIM1.6 and greater) you can call this function direct * instead of calling DoMIMe() */ int DoMIMeType1(MIME_HANDLE *mh) { if(mh->frame++>mh->md.movement[mh->step].frames) { mh->step++; mh->frame=0; } if(mh->md.movement[mh->step].mime_data==NULL) { mh->step--; return 0; } gteMIMefunc(mh->vectors,mh->md.movement[mh->step].mime_data,mh->n_vert,mh->md.movement[mh->step].scaler); return 1; } /* * Type2 MIMe animation player * If you only use Type2 animation data (RsdANIM 1.6 and greater) you can call this function direct * instead of calling DoMIMe() */ int DoMIMeType2(MIME_HANDLE *mh) { MIME_BLOCK *blk; // Update the frames counter for this animation segment if(mh->frame++>mh->md.segments[mh->step].frames) { // if we've finished the segment, step to the next one and reset the frames counter mh->step++; mh->frame=0; } // If the next segment is the termination segment, then we stop and return 0 if((mh->md.segments[mh->step].block_list==NULL)&&(mh->md.segments[mh->step].frames==0)) { mh->step--; return 0; } // Go along the MIMe blocks list and apply each block individually for(blk=mh->md.segments[mh->step].block_list; blk; blk=blk->next) { gteMIMefunc(mh->vectors+blk->start_v,blk->mime_data,blk->num_v,blk->scaler); } return 1; } /* * Generic Animation Player. *------ * Each time DoMIMe() is called, the model will step one frame through the animation sequence. * Simply call this once per frame in your display loop and GsSortObject4() the object as normal. * Fully compatible with the DoMIMe() function in previous versions of the library and also with * Type2 MIMe files from RsdANIM v1.6 and higher. * PARAMETERS: * mh : pointer to a valid MIME_HANDLE * RETURNS: * 0 : animation sequence finished * 1 : animation sequence still running */ int DoMIMe(MIME_HANDLE *mh) { int rtn; switch(mh->type) { case 1: rtn=DoMIMeType1(mh); break; case 2: rtn=DoMIMeType2(mh); break; } return rtn; }