#include "globals.h" #include "misc_utils.h" #include "skm_model.h" #include #include "quaternions.h" #include #include #include //rendering info typedef struct { float v[3]; } vertex_t; typedef struct { float v[2]; } texcoord_t; extern vertex_t *varray_vertex; extern vertex_t *varray_normal; extern texcoord_t *varray_texcoord; int textureforimage(const char *name); void R_Mesh_ResizeCheck(int numverts); void R_DrawMesh( unsigned int numverts, unsigned int numtris, unsigned int *indices, const char *shadername ); void R_DrawLine(float origin[3], float origin2[3], float color[3], int depthtest); void SKM_GenerateAnimationScenes( mskmodel_t *model ); mskmodel_t *modelSKM; //tmp model holder void Str_StripExtension ( const char *in, char *out) { while (*in && *in != '.') *out++ = *in++; *out = 0; } void Str_FixedExtension (char *filename, const char *extension) { Str_StripExtension( filename, filename ); sprintf(filename, "%s.%s", filename, extension); } void SKM_freemodel(void) { int i, j; //SKP mallocs for( i = 0; i < (int)modelSKM->numframes; i++ ) free( modelSKM->frames[i].boneposes ); free( modelSKM->frames ); free( modelSKM->bones ); //SKM mallocs for( i = 0; i < (int)modelSKM->nummeshes; i++ ) { for( j = 0; j < (int)modelSKM->meshes[i].numverts; j++ ) free( modelSKM->meshes[i].vertexes[j].verts ); free( modelSKM->meshes[i].vertexes ); free( modelSKM->meshes[i].stcoords ); free( modelSKM->meshes[i].indexes ); free( modelSKM->meshes[i].references ); } free( modelSKM->meshes ); free( modelSKM ); } //======================== //Save Skeletal Poses file //======================== void SKM_saveSKPfile(mskmodel_t *model) { int i, j, k; int pos_filesize; int pos_offsets; int pos_bones; int pos_frames; int pos_framebones; int restoreposition; int filesize; // create filehandle putinit (SKM_MAX_FILESIZE); // write header //--------------- // ID string putstring (SKMHEADER, strlen(SKMHEADER)); // model type putlong (SKM_MODELTYPE); // filesize pos_filesize = putgetposition (); putlong (0); putlong (model->numbones); putlong (model->numframes); // offsets to things pos_offsets = putgetposition (); putlong (0); putlong (0); // write bones //--------------- pos_bones = putgetposition (); for (i = 0; i < (int)model->numbones; i++) { putstring (model->bones[i].name, SKM_MAX_NAME); putlong (model->bones[i].parent); putlong (model->bones[i].flags); } // write frames //--------------- pos_frames = putgetposition (); // skip over the frame structs, they will be filled in later putsetposition (pos_frames + model->numframes * (SKM_MAX_NAME + 4)); for (i = 0; i < (int)model->numframes; i++) { pos_framebones = putgetposition (); for (j = 0; j < (int)model->numbones; j++) { for (k = 0; k < 4; k++) putfloat (model->frames[i].boneposes[j].quat[k]); for (k = 0; k < 3; k++) putfloat (model->frames[i].boneposes[j].origin[k]); } // now we actually write the frame header restoreposition = putgetposition (); putsetposition (pos_frames + i * (SKM_MAX_NAME + 4)); putstring (model->frames[i].name, SKM_MAX_NAME); putlong (pos_framebones); putsetposition (restoreposition); } // Finish: fill header data //--------------- filesize = putgetposition (); putsetposition (pos_offsets); putlong (pos_bones); putlong (pos_frames); putsetposition (pos_filesize); putlong (filesize); putsetposition (filesize); // write the file putwritefile ("data/out.skp", filesize); printf ("wrote SKP file\n"); } //======================== //Save Skeletal Model file //======================== void SKM_saveSKMfile(mskmodel_t *model) { int i, j; int k, l; int pos_filesize; int pos_ofs_meshes; int pos_meshes; int pos_verts; int pos_texcoords; int pos_index; int pos_references; int filesize; int restoreposition; mskvertex_t *poutskmvert; mskbonevert_t *poutbonevert; vec2 *poutstcoord; index_t *pouttris; unsigned int *poutreferences; // create filehandle putinit (SKM_MAX_FILESIZE); // write header //--------------- // ID string putstring (SKMHEADER, strlen(SKMHEADER)); // model type putlong (SKM_MODELTYPE); // filesize pos_filesize = putgetposition (); putlong (0); // numbers of things putlong (model->numbones); putlong (model->nummeshes); // offsets to things pos_ofs_meshes = putgetposition (); putlong (0); // store the meshes pos_meshes = putgetposition (); // skip over the mesh structs, they will be filled in later putsetposition (pos_meshes + model->nummeshes * (SKM_MAX_NAME*2 + 28)); //mesh cycle for (i = 0; i < (int)model->nummeshes; i++) { //write vertices (ofs_verts) //--------------- pos_verts = putgetposition (); poutskmvert = model->meshes[i].vertexes; for( j = 0; j < (int)model->meshes[i].numverts; j++, poutskmvert++ ) { putlong(poutskmvert->numbones); poutbonevert = model->meshes[i].vertexes[j].verts; for( l = 0; l < (int)poutskmvert->numbones; l++, poutbonevert++ ) { for( k = 0; k < 3; k++ ) putfloat(poutbonevert->origin[k]); putfloat(poutbonevert->influence); for( k = 0; k < 3; k++ ) putfloat(poutbonevert->normal[k]); putlong(poutbonevert->bonenum); } } //write stcoords (ofs_stcoords) //--------------- pos_texcoords = putgetposition (); poutstcoord = model->meshes[i].stcoords; for( j = 0; j < (int)model->meshes[i].numverts; j++ ) { putfloat(poutstcoord[j][0]); putfloat(poutstcoord[j][1]); } //write indices (ofs_indices) //--------------- pos_index = putgetposition (); pouttris = model->meshes[i].indexes; for( j = 0; j < (int)model->meshes[i].numtris; j++, pouttris += 3 ) { putlong(pouttris[0]); putlong(pouttris[1]); putlong(pouttris[2]); } //write references (ofs_references) //--------------- pos_references = putgetposition (); poutreferences = model->meshes[i].references; for( j = 0; j < (int)model->meshes[i].numreferences; j++, poutreferences++ ) putlong(*poutreferences); //write mesh header //--------------- restoreposition = putgetposition (); putsetposition (pos_meshes + i * (SKM_MAX_NAME*2 + 28)); putstring (model->meshes[i].shadername, SKM_MAX_NAME); putstring (model->meshes[i].name, SKM_MAX_NAME); putlong (model->meshes[i].numverts); putlong (model->meshes[i].numtris); putlong (model->meshes[i].numreferences); putlong (pos_verts); putlong (pos_texcoords); putlong (pos_index); putlong (pos_references); putsetposition (restoreposition); } //finish: SKM file header filesize = putgetposition (); putsetposition (pos_ofs_meshes); putlong (pos_meshes); putsetposition (pos_filesize); putlong (filesize); putsetposition (filesize); //restore position putwritefile ("data/out.skm", filesize); printf ("wrote SKM file\n"); } //======================== //Load Skeletal Poses file //======================== int SKM_ReadSKPfile(char *name, mskmodel_t *poutmodel) { unsigned char *buffer = NULL; char filename[1024]; int filesize = 0; int i, j, k; dskpheader_t *pinmodel; dskpbone_t *pinbone; dskpframe_t *pinframe; dskpbonepose_t *pinbonepose; mskbone_t *poutbone; mskframe_t *poutframe; mskbonepose_t *poutbonepose; strcpy( filename, name ); Str_FixedExtension( filename, "skp" ); buffer = loadfile( filename, &filesize ); if( !buffer ) return 0; pinmodel = ( dskpheader_t * )buffer; printf ( "----------------------\n"); printf ( "%s pinmodel type: %i\n", filename, pinmodel->type); printf ( "%s pinmodel filesize: %i (MAX: %i)\n", filename, pinmodel->filesize, SKM_MAX_FILESIZE); printf ( "%s pinmodel numbones: %i\n", filename, pinmodel->num_bones); if( LittleLong (pinmodel->type) != SKM_MODELTYPE ) printf ( "%s has wrong type number (%i should be %i)\n", filename, LittleLong (pinmodel->type), SKM_MODELTYPE ); if( LittleLong (pinmodel->type) != (int)pinmodel->type || LittleLong (pinmodel->type) > SKM_MAX_BONES) { printf ( "%s num_bones: Invalid value\n", filename ); } poutmodel->numframes = LittleLong ( pinmodel->num_frames ); printf("numframes:pout:%i pin:%i\n", poutmodel->numframes, LittleLong ( pinmodel->num_frames )); if( poutmodel->numframes <= 0 ) printf("%s has no frames", filename ); else if ( poutmodel->numframes > SKM_MAX_FRAMES ) printf("%s has too many frames", filename ); //copy numbones (at QF it is set up at loading the model) poutmodel->numbones = LittleLong ( pinmodel->num_bones ); printf("NumBones(SKP): %i\n",(int)poutmodel->numbones); pinbone = ( dskpbone_t * )( ( unsigned char * )pinmodel + LittleLong ( pinmodel->ofs_bones ) ); poutbone = poutmodel->bones = malloc ( sizeof(mskbone_t) * poutmodel->numbones ); for( i = 0; i < (int)poutmodel->numbones; i++, pinbone++, poutbone++ ) { strncpy( poutbone->name, pinbone->name, SKM_MAX_NAME ); poutbone->flags = LittleLong ( pinbone->flags ); poutbone->parent = LittleLong ( pinbone->parent ); } pinframe = ( dskpframe_t * )( ( unsigned char * )pinmodel + LittleLong ( pinmodel->ofs_frames ) ); poutframe = poutmodel->frames = malloc ( sizeof(mskframe_t) * poutmodel->numframes ); for( i = 0; i < (int)poutmodel->numframes; i++, pinframe++, poutframe++ ) { poutbone = poutmodel->bones; // frames names are ignored at QFusion, but not here strncpy( poutframe->name, pinframe->name, SKM_MAX_NAME ); pinbonepose = ( dskpbonepose_t * )( ( unsigned char * )pinmodel + LittleLong (pinframe->ofs_bonepositions) ); poutbonepose = poutframe->boneposes = malloc ( sizeof(mskbonepose_t) * poutmodel->numbones ); for( j = 0; j < (int)poutmodel->numbones; j++, poutbone++, pinbonepose++, poutbonepose++ ) { for( k = 0; k < 4; k++ ) poutbonepose->quat[k] = LittleFloat ( pinbonepose->quat[k] ); for( k = 0; k < 3; k++ ) poutbonepose->origin[k] = LittleFloat ( pinbonepose->origin[k] ); } } free(buffer); return 1; } //======================== //Load Skeletal Model file //======================== mskmodel_t *SKM_ReadSKMfile( char* name ) { unsigned char *buffer; char filename[1024]; int i, j, l, k; int filesize=0; dskmheader_t *pinmodel; dskmmesh_t *pinmesh; dskmvertex_t *pinskmvert; dskmbonevert_t *pinbonevert; dskmcoord_t *pinstcoord; index_t *pintris; unsigned int *pinreferences; mskmodel_t *poutmodel; mskmesh_t *poutmesh; mskvertex_t *poutskmvert; mskbonevert_t *poutbonevert; vec2 *poutstcoord; index_t *pouttris; unsigned int *poutreferences; strcpy( filename, name ); Str_FixedExtension( filename, "skm" ); printf( "Loading %s\n", filename ); buffer = loadfile( filename, &filesize ); if( !buffer || filesize <= 0 ) return NULL; poutmodel = malloc( sizeof(mskmodel_t) ); pinmodel = ( dskmheader_t * )buffer; printf("%s: Type:%i\n", filename, LittleLong (pinmodel->type)); if( LittleLong (pinmodel->type) != SKM_MODELTYPE ) { printf( "%s has wrong type number (%i should be %i)", filename, LittleLong (pinmodel->type), SKM_MODELTYPE ); } printf("%s: Filesize:%i\n", filename, LittleLong (pinmodel->filesize)); if( LittleLong (pinmodel->filesize) > SKM_MAX_FILESIZE ) { printf( "%s has has wrong filesize (%i should be less than %i)", filename, LittleLong (pinmodel->filesize), SKM_MAX_FILESIZE ); } poutmodel = malloc( sizeof(mskmodel_t) ); poutmodel->nummeshes = LittleLong( pinmodel->num_meshes ); if( poutmodel->nummeshes <= 0 ) printf("%s has no meshes", filename ); else if( poutmodel->nummeshes > SKM_MAX_MESHES ) printf("%s has too many meshes", filename ); poutmodel->numbones = LittleLong( pinmodel->num_bones ); if( poutmodel->numbones <= 0 ) printf("%s has no bones", filename ); else if( poutmodel->numbones > SKM_MAX_BONES ) printf("%s has too many bones", filename ); pinmesh = ( dskmmesh_t * )( ( unsigned char * )pinmodel + LittleLong ( pinmodel->ofs_meshes ) ); poutmesh = poutmodel->meshes = malloc ( sizeof(mskmesh_t) * poutmodel->nummeshes ); for( i = 0; i < (int)poutmodel->nummeshes; i++, pinmesh++, poutmesh++ ) { poutmesh->numverts = LittleLong ( pinmesh->num_verts ); if( poutmesh->numverts <= 0 ) printf("mesh %i in model %s has no vertexes", i, filename ); else if( poutmesh->numverts > SKM_MAX_VERTS ) printf("mesh %i in model %s has too many vertexes", i, filename ); poutmesh->numtris = LittleLong ( pinmesh->num_tris ); if( poutmesh->numtris <= 0 ) printf("mesh %i in model %s has no indices", i, filename ); else if( poutmesh->numtris > SKM_MAX_TRIS ) printf("mesh %i in model %s has too many indices", i, filename ); poutmesh->numreferences = LittleLong ( pinmesh->num_references ); if( poutmesh->numreferences <= 0 ) printf("mesh %i in model %s has no bone references", i, filename ); else if( poutmesh->numreferences > SKM_MAX_BONES ) printf("mesh %i in model %s has too many bone references", i, filename ); strncpy( poutmesh->name, pinmesh->meshname, sizeof(poutmesh->name) );//SKM_MAX_NAME //shadername is not stored in QF (it registers the actual shader) strncpy( poutmesh->shadername, pinmesh->shadername, SKM_MAX_NAME ); poutmesh->skin.shader = NULL; pinskmvert = ( dskmvertex_t * )( ( unsigned char * )pinmodel + LittleLong( pinmesh->ofs_verts ) ); poutskmvert = poutmesh->vertexes = malloc( sizeof(mskvertex_t) * poutmesh->numverts ); for( j = 0; j < (int)poutmesh->numverts; j++, poutskmvert++/*, pinskmvert++*/ ) {//JAL: pinskmvert++ by me poutskmvert->numbones = LittleLong( pinskmvert->numbones ); pinbonevert = ( dskmbonevert_t * )( ( unsigned char * )pinskmvert + sizeof(poutskmvert->numbones) ); poutbonevert = poutskmvert->verts = malloc( sizeof(mskbonevert_t) * poutskmvert->numbones ); for( l = 0; l < (int)poutskmvert->numbones; l++, pinbonevert++, poutbonevert++ ) { for( k = 0; k < 3; k++ ) { poutbonevert->origin[k] = LittleFloat( pinbonevert->origin[k] ); poutbonevert->normal[k] = LittleFloat( pinbonevert->normal[k] ); } poutbonevert->influence = LittleFloat( pinbonevert->influence ); poutbonevert->bonenum = LittleLong( pinbonevert->bonenum ); } pinskmvert = ( dskmvertex_t * )( ( unsigned char * )pinbonevert ); //JAL: moved to pinskmvert++ } pinstcoord = ( dskmcoord_t * )( ( unsigned char * )pinmodel + LittleLong (pinmesh->ofs_texcoords) ); poutstcoord = poutmesh->stcoords = malloc( poutmesh->numverts * sizeof(vec2) );//jalfixme vec2 for( j = 0; j < (int)poutmesh->numverts; j++, pinstcoord++/*, poutstcoord++*/ ) { poutstcoord[j][0] = LittleFloat( pinstcoord->st[0] ); poutstcoord[j][1] = LittleFloat( pinstcoord->st[1] ); } pintris = ( index_t * )( ( unsigned char * )pinmodel + LittleLong (pinmesh->ofs_indices) ); pouttris = poutmesh->indexes = malloc( sizeof(index_t) * poutmesh->numtris * 3 ); for( j = 0; j < (int)poutmesh->numtris; j++, pintris += 3, pouttris += 3 ) { pouttris[0] = (index_t)LittleLong( pintris[0] ); pouttris[1] = (index_t)LittleLong( pintris[1] ); pouttris[2] = (index_t)LittleLong( pintris[2] ); } pinreferences = ( index_t *)( ( unsigned char * )pinmodel + LittleLong (pinmesh->ofs_references) ); poutreferences = poutmesh->references = malloc( sizeof(unsigned int) * poutmesh->numreferences ); for( j = 0; j < (int)poutmesh->numreferences; j++, pinreferences++, poutreferences++ ) { *poutreferences = LittleLong( *pinreferences ); } } free(buffer); return poutmodel; } void SKM_precacheimages( mskmodel_t *model ) { int i; mskmesh_t *mesh; for (i = 0, mesh = model->meshes;i < (int)model->nummeshes;i++, mesh++) textureforimage(mesh->shadername); } //======================== //Load SKM Skeletal Model //======================== int SKM_LoadModel( char* filename ) { mskmodel_t *model; model = SKM_ReadSKMfile( filename ); if( !model ) return 0; if( !SKM_ReadSKPfile(filename, model) ) //add frames boneposes return 0; //SKM_precacheimages(model); modelSKM = model; //static handler for the viewer // Set up for animations SKM_GenerateAnimationScenes( modelSKM ); return 1; } //=============================================== // // SKM drawing code // //=============================================== static mskbonepose_t oldboneposescache[SKM_MAX_BONES]; //old frame pose cache static mskbonepose_t curboneposescache[SKM_MAX_BONES]; //current frame pose cache static mskbonepose_t lerpboneposescache[SKM_MAX_BONES]; //lerped pose cache //======================== // SKM_TransformBoneposes //======================== void SKM_TransformBoneposes( mskmodel_t *model, mskbonepose_t *boneposes, mskbonepose_t *sourceboneposes ) { int j; mskbonepose_t temppose; for( j = 0; j < (int)model->numbones; j++ ) { if( model->bones[j].parent >= 0 ) { memcpy( &temppose, &sourceboneposes[j], sizeof(mskbonepose_t)); Quat_ConcatTransforms ( boneposes[model->bones[j].parent].quat, boneposes[model->bones[j].parent].origin, temppose.quat, temppose.origin, boneposes[j].quat, boneposes[j].origin ); } else memcpy( &boneposes[j], &sourceboneposes[j], sizeof(mskbonepose_t)); } } //======================== // SKM_LerpBoneposes //======================== void SKM_LerpBoneposes( mskmodel_t *model, mskbonepose_t *boneposes, mskbonepose_t *oldboneposes, mskbonepose_t *lerpboneposes, float frontlerp ) { int i; mskbonepose_t *bonepose, *oldbonepose, *lerpbpose; //run all bones for( i = 0; i < (int)model->numbones; i++ ) { bonepose = boneposes + i; oldbonepose = oldboneposes + i; lerpbpose = lerpboneposes + i; // lerp Quat_Lerp( oldbonepose->quat, bonepose->quat, frontlerp, lerpbpose->quat ); lerpbpose->origin[0] = oldbonepose->origin[0] + (bonepose->origin[0] - oldbonepose->origin[0]) * frontlerp; lerpbpose->origin[1] = oldbonepose->origin[1] + (bonepose->origin[1] - oldbonepose->origin[1]) * frontlerp; lerpbpose->origin[2] = oldbonepose->origin[2] + (bonepose->origin[2] - oldbonepose->origin[2]) * frontlerp; } } //======================== // SKM_DrawSkeleton //======================== void SKM_DrawSkeleton(mskmodel_t *model, mskbonepose_t *boneposes) { int j; float color[3]; color[RED] = 0.4f;color[GREEN] = 0.5f;color[BLUE] = 0.7f; for( j = 0; j < (int)model->numbones; j++) { if (model->bones[j].parent >= 0) { R_DrawLine( boneposes[model->bones[j].parent].origin, boneposes[j].origin, color, 0 ); } } } //======================== // SKM_DrawModel //======================== void SKM_DrawModel( mskmodel_t *model, mskbonepose_t *bonepose) { int meshnum; int j, k, l; float poseaxis[3][3]; mskbonepose_t *pose; mskvertex_t *skmverts; mskbonevert_t *boneverts; mskmesh_t *mesh; for (meshnum = 0, mesh = model->meshes; meshnum < (int)model->nummeshes; meshnum++, mesh++) { R_Mesh_ResizeCheck( mesh->numverts ); for( j = 0, skmverts = mesh->vertexes; j < (int)mesh->numverts; j++, skmverts++ ) { varray_vertex[j].v[0] = varray_normal[j].v[0] = 0; varray_vertex[j].v[1] = varray_normal[j].v[1] = 0; varray_vertex[j].v[2] = varray_normal[j].v[2] = 0; for( l = 0, boneverts = skmverts->verts; l < (int)skmverts->numbones; l++, boneverts++ ) { pose = bonepose + boneverts->bonenum; Quat_Matrix( pose->quat, poseaxis ); for( k = 0; k < 3; k++ ) { varray_vertex[j].v[k] += boneverts->origin[0] * poseaxis[k][0] + boneverts->origin[1] * poseaxis[k][1] + boneverts->origin[2] * poseaxis[k][2] + boneverts->influence * pose->origin[k]; varray_normal[j].v[k] += boneverts->normal[0] * poseaxis[k][0] + boneverts->normal[1] * poseaxis[k][1] + boneverts->normal[2] * poseaxis[k][2]; } } // VectorNormalizeFast( varray_normal[j] ); varray_texcoord[j].v[0] = mesh->stcoords[j][0]; varray_texcoord[j].v[1] = mesh->stcoords[j][1]; } R_DrawMesh( mesh->numverts, mesh->numtris, mesh->indexes, mesh->shadername ); } } //======================== // SKM_DrawFrameLerp //======================== void SKM_DrawFrameLerp( mskmodel_t *model, int showmodel, int showskeleton, int curframe, int oldframe, float frontlerp ) { //bild frame poses into the boneposes caches SKM_TransformBoneposes( model, curboneposescache, model->frames[curframe].boneposes ); SKM_TransformBoneposes( model, oldboneposescache, model->frames[oldframe].boneposes ); SKM_LerpBoneposes( model, curboneposescache, oldboneposescache, lerpboneposescache, frontlerp ); if( showmodel ) SKM_DrawModel( model, lerpboneposescache ); if( showskeleton ) SKM_DrawSkeleton( model, lerpboneposescache ); } //======================== // SKM_Draw //======================== void SKM_Draw( int showmodel, int showskeleton, int curframe, int oldframe, float lerp ) { if( !modelSKM ) { printf( "model SKM not loaded\n" ); return; } SKM_DrawFrameLerp( modelSKM, showmodel, showskeleton, curframe, oldframe, lerp ); } //=============================================== // // Viewer stuff, animations and such // //=============================================== #include "animations.h" void SKM_GenerateAnimationScenes( mskmodel_t *model ) { int i; int num_animations; for ( i=0; i<(int)model->numframes; i++ ) { num_animations = SMViewer_AssignFrameToAnim( i, model->frames[i].name ); } SMViewer_PrintAnimations(); }