/*** * * Copyright (c) 1998, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * ****/ // // studiomdl.c: generates a studio .mdl file from a .qc script // models/.mdl. // #pragma warning( disable : 4244 ) #pragma warning( disable : 4237 ) #pragma warning( disable : 4305 ) #include #include #include #include #include "cmdlib.h" #include "lbmlib.h" #include "scriplib.h" #include "mathlib.h" #define EXTERN #include "..\..\engine\studio.h" #include "studiomdl.h" #include "..\..\dlls\activity.h" #include "..\..\dlls\activitymap.h" void Sys_Error (char *error, ...) {}; void clip_rotations( vec3_t rot ); #define strcpyn( a, b ) strncpy( a, b, sizeof( a ) ) /* ================= ================= */ int k_memtotal; void *kalloc( int num, int size ) { // printf( "calloc( %d, %d )\n", num, size ); // printf( "%d ", num * size ); k_memtotal += num * size; return calloc( num, size ); } void kmemset( void *ptr, int value, int size ) { // printf( "kmemset( %x, %d, %d )\n", ptr, value, size ); memset( ptr, value, size ); return; } /* ================= ================= */ void ClearModel (void) { } void ExtractMotion( ) { int i, j, k; int q; // extract linear motion for (i = 0; i < numseq; i++) { if (sequence[i].numframes > 1) { // assume 0 for now. int type; vec3_t *ppos; vec3_t motion = { 0,0,0}; type = sequence[i].motiontype; ppos = sequence[i].panim[0]->pos[0]; k = sequence[i].numframes - 1; if (type & STUDIO_LX) motion[0] = ppos[k][0] - ppos[0][0]; if (type & STUDIO_LY) motion[1] = ppos[k][1] - ppos[0][1]; if (type & STUDIO_LZ) motion[2] = ppos[k][2] - ppos[0][2]; // printf("%f %f %f\n", motion[0], motion[1], motion[2] ); for (j = 0; j < sequence[i].numframes; j++) { vec3_t adj; for (k = 0; k < sequence[i].panim[0]->numbones; k++) { if (sequence[i].panim[0]->node[k].parent == -1) { ppos = sequence[i].panim[0]->pos[k]; VectorScale( motion, j * 1.0 / (sequence[i].numframes - 1), adj ); // printf(" %f %f %f\n", adj[0], adj[1], adj[2] ); for (q = 0; q < sequence[i].numblends; q++) { VectorSubtract( sequence[i].panim[q]->pos[k][j], adj, sequence[i].panim[q]->pos[k][j] ); } } } } VectorCopy( motion, sequence[i].linearmovement ); } else { VectorSubtract( sequence[i].linearmovement, sequence[i].linearmovement, sequence[i].linearmovement ); } } // extract unused motion for (i = 0; i < numseq; i++) { int type; type = sequence[i].motiontype; for (k = 0; k < sequence[i].panim[0]->numbones; k++) { if (sequence[i].panim[0]->node[k].parent == -1) { for (q = 0; q < sequence[i].numblends; q++) { float motion[6]; motion[0] = sequence[i].panim[q]->pos[k][0][0]; motion[1] = sequence[i].panim[q]->pos[k][0][1]; motion[2] = sequence[i].panim[q]->pos[k][0][2]; motion[3] = sequence[i].panim[q]->rot[k][0][0]; motion[4] = sequence[i].panim[q]->rot[k][0][1]; motion[5] = sequence[i].panim[q]->rot[k][0][2]; for (j = 0; j < sequence[i].numframes; j++) { /* if (type & STUDIO_X) sequence[i].panim[q]->pos[k][j][0] = motion[0]; if (type & STUDIO_Y) sequence[i].panim[q]->pos[k][j][1] = motion[1]; if (type & STUDIO_Z) sequence[i].panim[q]->pos[k][j][2] = motion[2]; */ if (type & STUDIO_XR) sequence[i].panim[q]->rot[k][j][0] = motion[3]; if (type & STUDIO_YR) sequence[i].panim[q]->rot[k][j][1] = motion[4]; if (type & STUDIO_ZR) sequence[i].panim[q]->rot[k][j][2] = motion[5]; } } } } } // extract auto motion for (i = 0; i < numseq; i++) { // assume 0 for now. int type; vec3_t *ppos; vec3_t *prot; vec3_t motion = { 0,0,0}; vec3_t angles = { 0,0,0}; type = sequence[i].motiontype; // printf("%f %f %f\n", motion[0], motion[1], motion[2] ); for (j = 0; j < sequence[i].numframes; j++) { ppos = sequence[i].panim[0]->pos[0]; prot = sequence[i].panim[0]->rot[0]; if (type & STUDIO_AX) motion[0] = ppos[j][0] - ppos[0][0]; if (type & STUDIO_AY) motion[1] = ppos[j][1] - ppos[0][1]; if (type & STUDIO_AZ) motion[2] = ppos[j][2] - ppos[0][2]; if (type & STUDIO_AXR) angles[0] = prot[j][0] - prot[0][0]; if (type & STUDIO_AYR) angles[1] = prot[j][1] - prot[0][1]; if (type & STUDIO_AZR) angles[2] = prot[j][2] - prot[0][2]; VectorCopy( motion, sequence[i].automovepos[j] ); VectorCopy( angles, sequence[i].automoveangle[j] ); for (k = 0; k < sequence[i].panim[0]->numbones; k++) { if (sequence[i].panim[0]->node[k].parent == -1) { // printf(" %f %f %f\n", adj[0], adj[1], adj[2] ); for (q = 0; q < sequence[i].numblends; q++) { // VectorSubtract( sequence[i].panim[q]->pos[k][j], motion, sequence[i].panim[q]->pos[k][j] ); // VectorSubtract( sequence[i].panim[q]->rot[k][j], angles, sequence[i].panim[q]->pos[k][j] ); } } } } } } void OptimizeAnimations(void) { int i, j; int n, m; int type; int q; int iError = 0; // optimize animations for (i = 0; i < numseq; i++) { sequence[i].numframes = sequence[i].panim[0]->endframe - sequence[i].panim[0]->startframe + 1; // force looping animations to be looping if (sequence[i].flags & STUDIO_LOOPING) { for (j = 0; j < sequence[i].panim[0]->numbones; j++) { for (q = 0; q < sequence[i].numblends; q++) { vec3_t *ppos = sequence[i].panim[q]->pos[j]; vec3_t *prot = sequence[i].panim[q]->rot[j]; n = 0; // sequence[i].panim[q]->startframe; m = sequence[i].numframes - 1; // sequence[i].panim[q]->endframe; type = sequence[i].motiontype; if (!(type & STUDIO_LX)) ppos[m][0] = ppos[n][0]; if (!(type & STUDIO_LY)) ppos[m][1] = ppos[n][1]; if (!(type & STUDIO_LZ)) ppos[m][2] = ppos[n][2]; prot[m][0] = prot[n][0]; prot[m][1] = prot[n][1]; prot[m][2] = prot[n][2]; } } } for (j = 0; j < sequence[i].numevents; j++) { if (sequence[i].event[j].frame < sequence[i].panim[0]->startframe) { printf( "sequence %s has event (%d) before first frame (%d)\n", sequence[i].name, sequence[i].event[j].frame, sequence[i].panim[0]->startframe ); sequence[i].event[j].frame = sequence[i].panim[0]->startframe; iError++; } if (sequence[i].event[j].frame > sequence[i].panim[0]->endframe) { printf( "sequence %s has event (%d) after last frame (%d)\n", sequence[i].name, sequence[i].event[j].frame, sequence[i].panim[0]->endframe ); sequence[i].event[j].frame = sequence[i].panim[0]->endframe; iError++; } } sequence[i].frameoffset = sequence[i].panim[0]->startframe; // printf("\n"); } /* if (iError) exit(1); */ } int findNode( char *name ) { int k; for (k = 0; k < numbones; k++) { if (strcmp( bonetable[k].name, name ) == 0) { return k; } } return -1; } void MatrixCopy (float in[3][4], float out[3][4]) { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { out[i][j] = in[i][j]; } } } void MakeTransitions( ) { int i, j, k; int iHit; // add in direct node transitions for (i = 0; i < numseq; i++) { if (sequence[i].entrynode != sequence[i].exitnode) { xnode[sequence[i].entrynode-1][sequence[i].exitnode-1] = sequence[i].exitnode; if (sequence[i].nodeflags) { xnode[sequence[i].exitnode-1][sequence[i].entrynode-1] = sequence[i].entrynode; } } if (sequence[i].entrynode > numxnodes) numxnodes = sequence[i].entrynode; } // add multi-stage transitions do { iHit = 0; for (i = 1; i <= numxnodes; i++) { for (j = 1; j <= numxnodes; j++) { // if I can't go there directly if (i != j && xnode[i-1][j-1] == 0) { for (k = 1; k < numxnodes; k++) { // but I found someone who knows how that I can get to if (xnode[k-1][j-1] > 0 && xnode[i-1][k-1] > 0) { // then go to them xnode[i-1][j-1] = -xnode[i-1][k-1]; iHit = 1; break; } } } } } // reset previous pass so the links can be used in the next pass for (i = 1; i <= numxnodes; i++) { for (j = 1; j <= numxnodes; j++) { xnode[i-1][j-1] = abs( xnode[i-1][j-1] ); } } } while (iHit); } void SimplifyModel (void) { int i, j, k; int n, m, q; vec3_t *defaultpos[MAXSTUDIOSRCBONES]; vec3_t *defaultrot[MAXSTUDIOSRCBONES]; int iError = 0; OptimizeAnimations( ); ExtractMotion( ); MakeTransitions( ); // find used bones for (i = 0; i < nummodels; i++) { for (k = 0; k < MAXSTUDIOSRCBONES; k++) { model[i]->boneref[k] = 0; } for (j = 0; j < model[i]->numverts; j++) { model[i]->boneref[model[i]->vert[j].bone] = 1; } for (k = 0; k < MAXSTUDIOSRCBONES; k++) { // tag parent bones as used if child has been used if (model[i]->boneref[k]) { n = model[i]->node[k].parent; while (n != -1 && !model[i]->boneref[n]) { model[i]->boneref[n] = 1; n = model[i]->node[n].parent; } } } } // rename model bones if needed for (i = 0; i < nummodels; i++) { for (j = 0; j < model[i]->numbones; j++) { for (k = 0; k < numrenamedbones; k++) { if (!strcmp( model[i]->node[j].name, renamedbone[k].from)) { strcpy( model[i]->node[j].name, renamedbone[k].to ); break; } } } } // union of all used bones numbones = 0; for (i = 0; i < nummodels; i++) { for (k = 0; k < MAXSTUDIOSRCBONES; k++) { model[i]->boneimap[k] = -1; } for (j = 0; j < model[i]->numbones; j++) { if (model[i]->boneref[j]) { k = findNode( model[i]->node[j].name ); if (k == -1) { // create new bone // printf("%d : %s\n", numbones, model[i]->node[j].name ); k = numbones; strcpyn( bonetable[k].name, model[i]->node[j].name ); if ((n = model[i]->node[j].parent) != -1) bonetable[k].parent = findNode( model[i]->node[n].name ); else bonetable[k].parent = -1; bonetable[k].bonecontroller = 0; bonetable[k].flags = 0; // set defaults defaultpos[k] = kalloc( MAXSTUDIOANIMATIONS, sizeof( vec3_t ) ); defaultrot[k] = kalloc( MAXSTUDIOANIMATIONS, sizeof( vec3_t ) ); for (n = 0; n < MAXSTUDIOANIMATIONS; n++) { VectorCopy( model[i]->skeleton[j].pos, defaultpos[k][n] ); VectorCopy( model[i]->skeleton[j].rot, defaultrot[k][n] ); } VectorCopy( model[i]->skeleton[j].pos, bonetable[k].pos ); VectorCopy( model[i]->skeleton[j].rot, bonetable[k].rot ); numbones++; } else { // double check parent assignments n = model[i]->node[j].parent; if (n != -1) n = findNode( model[i]->node[n].name ); m = bonetable[k].parent; if (n != m) { printf("illegal parent bone replacement in model \"%s\"\n\t\"%s\" has \"%s\", previously was \"%s\"\n", model[i]->name, model[i]->node[j].name, (n != -1) ? bonetable[n].name : "ROOT", (m != -1) ? bonetable[m].name : "ROOT" ); iError++; } } model[i]->bonemap[j] = k; model[i]->boneimap[k] = j; } } } if (iError && !(ignore_warnings)) { exit( 1 ); } if (numbones >= MAXSTUDIOBONES) { Error( "Too many bones used in model, used %d, max %d\n", numbones, MAXSTUDIOBONES ); } // rename sequence bones if needed for (i = 0; i < numseq; i++) { for (j = 0; j < sequence[i].panim[0]->numbones; j++) { for (k = 0; k < numrenamedbones; k++) { if (!strcmp( sequence[i].panim[0]->node[j].name, renamedbone[k].from)) { strcpy( sequence[i].panim[0]->node[j].name, renamedbone[k].to ); break; } } } } // map each sequences bone list to master list for (i = 0; i < numseq; i++) { for (k = 0; k < MAXSTUDIOSRCBONES; k++) { sequence[i].panim[0]->boneimap[k] = -1; } for (j = 0; j < sequence[i].panim[0]->numbones; j++) { k = findNode( sequence[i].panim[0]->node[j].name ); if (k == -1) { // printf("unknown bone \"%s\" in sequence \"%s\"\n", sequence[i].panim[0]->node[j].name, sequence[i].name ); sequence[i].panim[0]->bonemap[j] = -1; } else { char *szAnim = "ROOT"; char *szNode = "ROOT"; // whoa, check parent connections! if (sequence[i].panim[0]->node[j].parent != -1) szAnim = sequence[i].panim[0]->node[sequence[i].panim[0]->node[j].parent].name; if (bonetable[k].parent != -1) szNode = bonetable[bonetable[k].parent].name; if (strcmp(szAnim, szNode)) { printf("illegal parent bone replacement in sequence \"%s\"\n\t\"%s\" has \"%s\", reference has \"%s\"\n", sequence[i].name, sequence[i].panim[0]->node[j].name, szAnim, szNode ); iError++; } sequence[i].panim[0]->bonemap[j] = k; sequence[i].panim[0]->boneimap[k] = j; // VectorCopy( sequence[i].panim[0]->pos[j][0].org, bonetable[k].pos ); // VectorCopy( sequence[i].panim[0]->rot[j][0].org, bonetable[k].rot ); } } } if (iError && !(ignore_warnings)) { exit( 1 ); } // link bonecontrollers for (i = 0; i < numbonecontrollers; i++) { for (j = 0; j < numbones; j++) { if (stricmp( bonecontroller[i].name, bonetable[j].name) == 0) break; } if (j >= numbones) { Error("unknown bonecontroller link '%s'\n", bonecontroller[i].name ); } bonecontroller[i].bone = j; } // link attachments for (i = 0; i < numattachments; i++) { for (j = 0; j < numbones; j++) { if (stricmp( attachment[i].bonename, bonetable[j].name) == 0) break; } if (j >= numbones) { Error("unknown attachment link '%s'\n", attachment[i].bonename ); } attachment[i].bone = j; } // relink model for (i = 0; i < nummodels; i++) { for (j = 0; j < model[i]->numverts; j++) { model[i]->vert[j].bone = model[i]->bonemap[model[i]->vert[j].bone]; } for (j = 0; j < model[i]->numnorms; j++) { model[i]->normal[j].bone = model[i]->bonemap[model[i]->normal[j].bone]; } } // set hitgroups for (k = 0; k < numbones; k++) { bonetable[k].group = -9999; } for (j = 0; j < numhitgroups; j++) { for (k = 0; k < numbones; k++) { if (strcmpi( bonetable[k].name, hitgroup[j].name) == 0) { bonetable[k].group = hitgroup[j].group; break; } } if (k >= numbones) Error( "cannot find bone %s for hitgroup %d\n", hitgroup[j].name, hitgroup[j].group ); } for (k = 0; k < numbones; k++) { if (bonetable[k].group == -9999) { if (bonetable[k].parent != -1) bonetable[k].group = bonetable[bonetable[k].parent].group; else bonetable[k].group = 0; } } if (numhitboxes == 0) { // find intersection box volume for each bone for (k = 0; k < numbones; k++) { for (j = 0; j < 3; j++) { bonetable[k].bmin[j] = 0.0; bonetable[k].bmax[j] = 0.0; } } // try all the connect vertices for (i = 0; i < nummodels; i++) { vec3_t p; for (j = 0; j < model[i]->numverts; j++) { VectorCopy( model[i]->vert[j].org, p ); k = model[i]->vert[j].bone; if (p[0] < bonetable[k].bmin[0]) bonetable[k].bmin[0] = p[0]; if (p[1] < bonetable[k].bmin[1]) bonetable[k].bmin[1] = p[1]; if (p[2] < bonetable[k].bmin[2]) bonetable[k].bmin[2] = p[2]; if (p[0] > bonetable[k].bmax[0]) bonetable[k].bmax[0] = p[0]; if (p[1] > bonetable[k].bmax[1]) bonetable[k].bmax[1] = p[1]; if (p[2] > bonetable[k].bmax[2]) bonetable[k].bmax[2] = p[2]; } } // add in all your children as well for (k = 0; k < numbones; k++) { if ((j = bonetable[k].parent) != -1) { if (bonetable[k].pos[0] < bonetable[j].bmin[0]) bonetable[j].bmin[0] = bonetable[k].pos[0]; if (bonetable[k].pos[1] < bonetable[j].bmin[1]) bonetable[j].bmin[1] = bonetable[k].pos[1]; if (bonetable[k].pos[2] < bonetable[j].bmin[2]) bonetable[j].bmin[2] = bonetable[k].pos[2]; if (bonetable[k].pos[0] > bonetable[j].bmax[0]) bonetable[j].bmax[0] = bonetable[k].pos[0]; if (bonetable[k].pos[1] > bonetable[j].bmax[1]) bonetable[j].bmax[1] = bonetable[k].pos[1]; if (bonetable[k].pos[2] > bonetable[j].bmax[2]) bonetable[j].bmax[2] = bonetable[k].pos[2]; } } for (k = 0; k < numbones; k++) { if (bonetable[k].bmin[0] < bonetable[k].bmax[0] - 1 && bonetable[k].bmin[1] < bonetable[k].bmax[1] - 1 && bonetable[k].bmin[2] < bonetable[k].bmax[2] - 1) { hitbox[numhitboxes].bone = k; hitbox[numhitboxes].group = bonetable[k].group; VectorCopy( bonetable[k].bmin, hitbox[numhitboxes].bmin ); VectorCopy( bonetable[k].bmax, hitbox[numhitboxes].bmax ); if (dump_hboxes) { printf("$hbox %d \"%s\" %.2f %.2f %.2f %.2f %.2f %.2f\n", hitbox[numhitboxes].group, bonetable[hitbox[numhitboxes].bone].name, hitbox[numhitboxes].bmin[0], hitbox[numhitboxes].bmin[1], hitbox[numhitboxes].bmin[2], hitbox[numhitboxes].bmax[0], hitbox[numhitboxes].bmax[1], hitbox[numhitboxes].bmax[2] ); } numhitboxes++; } } } else { for (j = 0; j < numhitboxes; j++) { for (k = 0; k < numbones; k++) { if (strcmpi( bonetable[k].name, hitbox[j].name) == 0) { hitbox[j].bone = k; break; } } if (k >= numbones) Error( "cannot find bone %s for bbox\n", hitbox[j].name ); } } // relink animations for (i = 0; i < numseq; i++) { vec3_t *origpos[MAXSTUDIOSRCBONES]; vec3_t *origrot[MAXSTUDIOSRCBONES]; for (q = 0; q < sequence[i].numblends; q++) { // save pointers to original animations for (j = 0; j < sequence[i].panim[q]->numbones; j++) { origpos[j] = sequence[i].panim[q]->pos[j]; origrot[j] = sequence[i].panim[q]->rot[j]; } for (j = 0; j < numbones; j++) { if ((k = sequence[i].panim[0]->boneimap[j]) >= 0) { // link to original animations sequence[i].panim[q]->pos[j] = origpos[k]; sequence[i].panim[q]->rot[j] = origrot[k]; } else { // link to dummy animations sequence[i].panim[q]->pos[j] = defaultpos[j]; sequence[i].panim[q]->rot[j] = defaultrot[j]; } } } // printf("%s %f\n", sequence[i].name, sequence[i].panim[0]->pos[3][0][0] ); } // find scales for all bones for (j = 0; j < numbones; j++) { for (k = 0; k < 6; k++) { float minv, maxv, scale; if (k < 3) { minv = -128.0; maxv = 128.0; } else { minv = -Q_PI / 8.0; maxv = Q_PI / 8.0; } for (i = 0; i < numseq; i++) { for (q = 0; q < sequence[i].numblends; q++) { for (n = 0; n < sequence[i].numframes; n++) { float v; switch(k) { case 0: case 1: case 2: v = ( sequence[i].panim[q]->pos[j][n][k] - bonetable[j].pos[k] ); break; case 3: case 4: case 5: v = ( sequence[i].panim[q]->rot[j][n][k-3] - bonetable[j].rot[k-3] ); if (v >= Q_PI) v -= Q_PI * 2; if (v < -Q_PI) v += Q_PI * 2; break; } if (v < minv) minv = v; if (v > maxv) maxv = v; } } } if (minv < maxv) { if (-minv> maxv) { scale = minv / -32768.0; } else { scale = maxv / 32767; } } else { scale = 1.0 / 32.0; } switch(k) { case 0: case 1: case 2: bonetable[j].posscale[k] = scale; break; case 3: case 4: case 5: bonetable[j].rotscale[k-3] = scale; break; } // printf("%.0f ", 1.0 / scale ); } // printf("\n" ); } // find bounding box for each sequence for (i = 0; i < numseq; i++) { vec3_t bmin, bmax; // find intersection box volume for each bone for (j = 0; j < 3; j++) { bmin[j] = 9999.0; bmax[j] = -9999.0; } for (q = 0; q < sequence[i].numblends; q++) { for (n = 0; n < sequence[i].numframes; n++) { float bonetransform[MAXSTUDIOBONES][3][4]; // bone transformation matrix float bonematrix[3][4]; // local transformation matrix vec3_t pos; for (j = 0; j < numbones; j++) { vec3_t angle; // convert to degrees angle[0] = sequence[i].panim[q]->rot[j][n][0] * (180.0 / Q_PI); angle[1] = sequence[i].panim[q]->rot[j][n][1] * (180.0 / Q_PI); angle[2] = sequence[i].panim[q]->rot[j][n][2] * (180.0 / Q_PI); AngleMatrix( angle, bonematrix ); bonematrix[0][3] = sequence[i].panim[q]->pos[j][n][0]; bonematrix[1][3] = sequence[i].panim[q]->pos[j][n][1]; bonematrix[2][3] = sequence[i].panim[q]->pos[j][n][2]; if (bonetable[j].parent == -1) { MatrixCopy( bonematrix, bonetransform[j] ); } else { R_ConcatTransforms (bonetransform[bonetable[j].parent], bonematrix, bonetransform[j]); } } for (k = 0; k < nummodels; k++) { for (j = 0; j < model[k]->numverts; j++) { VectorTransform( model[k]->vert[j].org, bonetransform[model[k]->vert[j].bone], pos ); if (pos[0] < bmin[0]) bmin[0] = pos[0]; if (pos[1] < bmin[1]) bmin[1] = pos[1]; if (pos[2] < bmin[2]) bmin[2] = pos[2]; if (pos[0] > bmax[0]) bmax[0] = pos[0]; if (pos[1] > bmax[1]) bmax[1] = pos[1]; if (pos[2] > bmax[2]) bmax[2] = pos[2]; } } } } VectorCopy( bmin, sequence[i].bmin ); VectorCopy( bmax, sequence[i].bmax ); /* printf("%s : %.0f %.0f %.0f %.0f %.0f %.0f\n", sequence[i].name, bmin[0], bmax[0], bmin[1], bmax[1], bmin[2], bmax[2] ); */ // printf("%s %.2f\n", sequence[i].name, sequence[i].panim[0]->pos[9][0][0] / bonetable[9].pos[0] ); } // reduce animations { int total = 0; int changes = 0; int p; for (i = 0; i < numseq; i++) { for (q = 0; q < sequence[i].numblends; q++) { for (j = 0; j < numbones; j++) { for (k = 0; k < 6; k++) { mstudioanimvalue_t *pcount, *pvalue; float v; short value[MAXSTUDIOANIMATIONS]; mstudioanimvalue_t data[MAXSTUDIOANIMATIONS]; for (n = 0; n < sequence[i].numframes; n++) { switch(k) { case 0: case 1: case 2: value[n] = ( sequence[i].panim[q]->pos[j][n][k] - bonetable[j].pos[k] ) / bonetable[j].posscale[k]; break; case 3: case 4: case 5: v = ( sequence[i].panim[q]->rot[j][n][k-3] - bonetable[j].rot[k-3] ); if (v >= Q_PI) v -= Q_PI * 2; if (v < -Q_PI) v += Q_PI * 2; value[n] = v / bonetable[j].rotscale[k-3]; break; } } if (n == 0) Error("no animation frames: \"%s\"\n", sequence[i].name ); sequence[i].panim[q]->numanim[j][k] = 0; memset( data, 0, sizeof( data ) ); pcount = data; pvalue = pcount + 1; pcount->num.valid = 1; pcount->num.total = 1; pvalue->value = value[0]; pvalue++; for (m = 1, p = 0; m < n; m++) { if (abs(value[p] - value[m]) > 1600) { changes++; p = m; } } // this compression algorithm needs work for (m = 1; m < n; m++) { if (pcount->num.total == 255) { // too many, force a new entry pcount = pvalue; pvalue = pcount + 1; pcount->num.valid++; pvalue->value = value[m]; pvalue++; } // insert value if they're not equal, // or if we're not on a run and the run is less than 3 units else if ((value[m] != value[m-1]) || ((pcount->num.total == pcount->num.valid) && ((m < n - 1) && value[m] != value[m+1]))) { total++; if (pcount->num.total != pcount->num.valid) { //if (j == 0) printf("%d:%d ", pcount->num.valid, pcount->num.total ); pcount = pvalue; pvalue = pcount + 1; } pcount->num.valid++; pvalue->value = value[m]; pvalue++; } pcount->num.total++; } //if (j == 0) printf("%d:%d\n", pcount->num.valid, pcount->num.total ); sequence[i].panim[q]->numanim[j][k] = pvalue - data; if (sequence[i].panim[q]->numanim[j][k] == 2 && value[0] == 0) { sequence[i].panim[q]->numanim[j][k] = 0; } else { sequence[i].panim[q]->anim[j][k] = kalloc( pvalue - data, sizeof( mstudioanimvalue_t ) ); memmove( sequence[i].panim[q]->anim[j][k], data, (pvalue - data) * sizeof( mstudioanimvalue_t ) ); } // printf("%d(%d) ", sequence[i].panim[q]->numanim[j][k], n ); } // printf("\n"); } } } // printf("total %.0f changes %.0f\n", total, changes ); } // auto groups if (numseqgroups == 1 && maxseqgroupsize < 1024 * 1024) { int current = 0; numseqgroups = 2; for (i = 0; i < numseq; i++) { int accum = 0; if (sequence[i].activity == 0) { for (q = 0; q < sequence[i].numblends; q++) { for (j = 0; j < numbones; j++) { for (k = 0; k < 6; k++) { accum += sequence[i].panim[q]->numanim[j][k] * sizeof( mstudioanimvalue_t ); } } } accum += sequence[i].numblends * numbones * sizeof( mstudioanim_t ); if (current && current + accum > maxseqgroupsize) { numseqgroups++; current = accum; } else { current += accum; } // printf("%d %d %d\n", numseqgroups, current, accum ); sequence[i].seqgroup = numseqgroups - 1; } else { sequence[i].seqgroup = 0; } } } } /* ================= ================= */ int lookupControl( char *string ) { if (stricmp(string,"X")==0) return STUDIO_X; if (stricmp(string,"Y")==0) return STUDIO_Y; if (stricmp(string,"Z")==0) return STUDIO_Z; if (stricmp(string,"XR")==0) return STUDIO_XR; if (stricmp(string,"YR")==0) return STUDIO_YR; if (stricmp(string,"ZR")==0) return STUDIO_ZR; if (stricmp(string,"LX")==0) return STUDIO_LX; if (stricmp(string,"LY")==0) return STUDIO_LY; if (stricmp(string,"LZ")==0) return STUDIO_LZ; if (stricmp(string,"AX")==0) return STUDIO_AX; if (stricmp(string,"AY")==0) return STUDIO_AY; if (stricmp(string,"AZ")==0) return STUDIO_AZ; if (stricmp(string,"AXR")==0) return STUDIO_AXR; if (stricmp(string,"AYR")==0) return STUDIO_AYR; if (stricmp(string,"AZR")==0) return STUDIO_AZR; return -1; } // search case-insensitive for string2 in string char *stristr( const char *string, const char *string2 ) { int c, len; c = tolower( *string2 ); len = strlen( string2 ); while (string) { for (; *string && tolower( *string ) != c; string++); if (*string) { if (strnicmp( string, string2, len ) == 0) { break; } string++; } else { return NULL; } } return (char *)string; } /* ================= ================= */ int lookup_texture( char *texturename ) { int i; for (i = 0; i < numtextures; i++) { if (stricmp( texture[i].name, texturename ) == 0) { return i; } } strcpyn( texture[i].name, texturename ); if (stristr( texturename, "chrome" ) != NULL) { texture[i].flags = STUDIO_NF_FLATSHADE | STUDIO_NF_CHROME; } else { texture[i].flags = 0; } numtextures++; return i; } s_mesh_t *lookup_mesh( s_model_t *pmodel, char *texturename ) { int i, j; j = lookup_texture( texturename ); for (i = 0; i < pmodel->nummesh; i++) { if (pmodel->pmesh[i]->skinref == j) { return pmodel->pmesh[i]; } } if (i >= MAXSTUDIOMESHES) { Error( "too many textures in model: \"%s\"\n", pmodel->name ); } pmodel->nummesh = i + 1; pmodel->pmesh[i] = kalloc( 1, sizeof( s_mesh_t ) ); pmodel->pmesh[i]->skinref = j; return pmodel->pmesh[i]; } s_trianglevert_t *lookup_triangle( s_mesh_t *pmesh, int index ) { if (index >= pmesh->alloctris) { int start = pmesh->alloctris; pmesh->alloctris = index + 256; if (pmesh->triangle) { pmesh->triangle = realloc( pmesh->triangle, pmesh->alloctris * sizeof( *pmesh->triangle ) ); kmemset( &pmesh->triangle[start], 0, (pmesh->alloctris - start) * sizeof( *pmesh->triangle ) ); } else { pmesh->triangle = kalloc( pmesh->alloctris, sizeof( *pmesh->triangle ) ); } } return pmesh->triangle[index]; } int lookup_normal( s_model_t *pmodel, s_normal_t *pnormal ) { int i; for (i = 0; i < pmodel->numnorms; i++) { // if (VectorCompare( pmodel->normal[i].org, pnormal->org ) if (DotProduct( pmodel->normal[i].org, pnormal->org ) > normal_blend && pmodel->normal[i].bone == pnormal->bone && pmodel->normal[i].skinref == pnormal->skinref) { return i; } } if (i >= MAXSTUDIOVERTS) { Error( "too many normals in model: \"%s\"\n", pmodel->name); } VectorCopy( pnormal->org, pmodel->normal[i].org ); pmodel->normal[i].bone = pnormal->bone; pmodel->normal[i].skinref = pnormal->skinref; pmodel->numnorms = i + 1; return i; } int lookup_vertex( s_model_t *pmodel, s_vertex_t *pv ) { int i; // assume 2 digits of accuracy pv->org[0] = (int)(pv->org[0] * 100) / 100.0; pv->org[1] = (int)(pv->org[1] * 100) / 100.0; pv->org[2] = (int)(pv->org[2] * 100) / 100.0; for (i = 0; i < pmodel->numverts; i++) { if (VectorCompare( pmodel->vert[i].org, pv->org ) && pmodel->vert[i].bone == pv->bone) { return i; } } if (i >= MAXSTUDIOVERTS) { Error( "too many vertices in model: \"%s\"\n", pmodel->name); } VectorCopy( pv->org, pmodel->vert[i].org ); pmodel->vert[i].bone = pv->bone; pmodel->numverts = i + 1; return i; } void adjust_vertex( float *org ) { org[0] = (org[0] - adjust[0]); org[1] = (org[1] - adjust[1]); org[2] = (org[2] - adjust[2]); } void scale_vertex( float *org ) { float tmp = org[0]; org[0] = org[0] * scale_up; org[1] = org[1] * scale_up; org[2] = org[2] * scale_up; } /* ============ SetSkinValues Called for the base frame ============ */ void TextureCoordRanges( s_mesh_t *pmesh, s_texture_t *ptexture ) { int i, j; if (ptexture->flags & STUDIO_NF_CHROME) { ptexture->skintop = 0; ptexture->skinleft = 0; ptexture->skinwidth = (ptexture->srcwidth + 3) & ~3; ptexture->skinheight = ptexture->srcheight; for (i=0 ; inumtris ; i++) { for (j = 0; j < 3; j++) { pmesh->triangle[i][j].s = 0; pmesh->triangle[i][j].t = 0; } ptexture->max_s = 63; ptexture->min_s = 0; ptexture->max_t = 63; ptexture->min_t = 0; } return; } // clip texture coords. for (i=0 ; inumtris ; i++) { if (pmesh->triangle[i][0].u > 100.0 || pmesh->triangle[i][1].u > 100.0 || pmesh->triangle[i][2].u > 100.0) { // printf("%d : %f %f %f\n", i, pmesh->triangle[i][0].u, pmesh->triangle[i][1].u, pmesh->triangle[i][2].u ); } if (pmesh->triangle[i][0].v > 100.0 || pmesh->triangle[i][1].v > 100.0 || pmesh->triangle[i][2].v > 100.0) { // printf("%d : %f %f %f\n", i, pmesh->triangle[i][0].v, pmesh->triangle[i][1].v, pmesh->triangle[i][2].v ); } } for (i=0 ; inumtris ; i++) { for (j = 0; j < 3; j++) { if (pmesh->triangle[i][j].u > 2.0) pmesh->triangle[i][j].u = 2.0; if (pmesh->triangle[i][j].u < -1.0) pmesh->triangle[i][j].u = -1.0; if (pmesh->triangle[i][j].v > 2.0) pmesh->triangle[i][j].v = 2.0; if (pmesh->triangle[i][j].v < -1.0) pmesh->triangle[i][j].v = -1.0; } } // pack texture coords if (!clip_texcoords) { int k, n; do { float min_u = 10; float max_u = -10; float k_max_u, n_min_u; k = -1; n = -1; for (i=0 ; inumtris ; i++) { float local_min, local_max; local_min = min( pmesh->triangle[i][0].u, min( pmesh->triangle[i][1].u, pmesh->triangle[i][2].u )); local_max = max( pmesh->triangle[i][0].u, max( pmesh->triangle[i][1].u, pmesh->triangle[i][2].u )); if (local_min < min_u) { min_u = local_min; k = i; k_max_u = local_max; } if (local_max > max_u) { max_u = local_max; n = i; n_min_u = local_min; } } if (k_max_u + 1.0 < max_u) { //printf("%d %f %f\n", k, k_max_u, max_u ); for (j = 0; j < 3; j++) pmesh->triangle[k][j].u += 1.0; } else if (n_min_u - 1.0 > min_u) { //printf("%d %f %f\n", n, n_min_u, min_u ); for (j = 0; j < 3; j++) pmesh->triangle[n][j].u -= 1.0; } else { break; } } while (1); do { float min_v = 10; float max_v = -10; float k_max_v, n_min_v; k = -1; n = -1; for (i=0 ; inumtris ; i++) { float local_min, local_max; local_min = min( pmesh->triangle[i][0].v, min( pmesh->triangle[i][1].v, pmesh->triangle[i][2].v )); local_max = max( pmesh->triangle[i][0].v, max( pmesh->triangle[i][1].v, pmesh->triangle[i][2].v )); if (local_min < min_v) { min_v = local_min; k = i; k_max_v = local_max; } if (local_max > max_v) { max_v = local_max; n = i; n_min_v = local_min; } } if (k_max_v + 1.0 < max_v) { //printf("%d %f %f\n", k, k_max_v, max_v ); for (j = 0; j < 3; j++) pmesh->triangle[k][j].v += 1.0; } else if (n_min_v - 1.0 > min_v) { //printf("%d %f %f\n", n, n_min_v, min_v ); for (j = 0; j < 3; j++) pmesh->triangle[n][j].v -= 1.0; } else { break; } } while (1); } else { for (i=0 ; inumtris ; i++) { for (j = 0; j < 3; j++) { if (pmesh->triangle[i][j].u < 0) pmesh->triangle[i][j].u = 0; if (pmesh->triangle[i][j].u > 1) pmesh->triangle[i][j].u = 1; if (pmesh->triangle[i][j].v < 0) pmesh->triangle[i][j].v = 0; if (pmesh->triangle[i][j].v > 1) pmesh->triangle[i][j].v = 1; } } } // convert to pixel coordinates for (i=0 ; inumtris ; i++) { for (j = 0; j < 3; j++) { // FIXME losing texture coord resultion! pmesh->triangle[i][j].s = pmesh->triangle[i][j].u * (ptexture->srcwidth - 1); pmesh->triangle[i][j].t = pmesh->triangle[i][j].v * (ptexture->srcheight - 1); } } // find the range if (!clip_texcoords) { for (i=0 ; inumtris ; i++) { for (j = 0; j < 3; j++) { ptexture->max_s = max( pmesh->triangle[i][j].s, ptexture->max_s ); ptexture->min_s = min( pmesh->triangle[i][j].s, ptexture->min_s ); ptexture->max_t = max( pmesh->triangle[i][j].t, ptexture->max_t ); ptexture->min_t = min( pmesh->triangle[i][j].t, ptexture->min_t ); } } } else { ptexture->max_s = ptexture->srcwidth-1; ptexture->min_s = 0; ptexture->max_t = ptexture->srcheight-1; ptexture->min_t = 0; } //printf("%d %d : ", ptexture->srcwidth, ptexture->srcheight ); //printf("%.0f %.0f %.0f %.0f\n", ptexture->min_s, ptexture->max_s, ptexture->min_t, ptexture->max_t ); } void ResetTextureCoordRanges( s_mesh_t *pmesh, s_texture_t *ptexture ) { int i, j; // adjust top, left edge for (i=0 ; inumtris ; i++) { for (j = 0; j < 3; j++) { pmesh->triangle[i][j].s -= ptexture->min_s; // quake wants t inverted pmesh->triangle[i][j].t = (ptexture->max_t - ptexture->min_t) - (pmesh->triangle[i][j].t - ptexture->min_t); } } } /* =============== Grab_Skin =============== */ void Grab_BMP ( char *filename, s_texture_t *ptexture ) { int result; if (result = LoadBMP(filename, &ptexture->ppicture, (byte **)&ptexture->ppal )) { Error("error %d reading BMP image \"%s\"\n", result, filename ); } ptexture->srcwidth = bmhd.w; ptexture->srcheight = bmhd.h; } void ResizeTexture( s_texture_t *ptexture ) { int i, j, s, t; byte *pdest; int srcadjwidth; // make the width a multiple of 4; some hardware requires this, and it ensures // dword alignment for each scan ptexture->skintop = ptexture->min_t; ptexture->skinleft = ptexture->min_s; ptexture->skinwidth = (int)((ptexture->max_s - ptexture->min_s) + 1 + 3) & ~3; ptexture->skinheight = (int)(ptexture->max_t - ptexture->min_t) + 1; ptexture->size = ptexture->skinwidth * ptexture->skinheight + 256 * 3; printf ("BMP %s [%d %d] (%.0f%%) %6d bytes\n", ptexture->name, ptexture->skinwidth, ptexture->skinheight, ((ptexture->skinwidth * ptexture->skinheight) / (float)(ptexture->srcwidth * ptexture->srcheight)) * 100.0, ptexture->size ); if (ptexture->size > 640 * 480) { printf("%.0f %.0f %.0f %.0f\n", ptexture->min_s, ptexture->max_s, ptexture->min_t, ptexture->max_t ); Error("texture too large\n"); } pdest = malloc( ptexture->size ); ptexture->pdata = pdest; // data is saved as a multiple of 4 srcadjwidth = (ptexture->srcwidth + 3) & ~3; // move the picture data to the model area, replicating missing data, deleting unused data. for (i = 0, t = ptexture->srcheight - ptexture->skinheight - ptexture->skintop + 10 * ptexture->srcheight; i < ptexture->skinheight; i++, t++) { while (t >= ptexture->srcheight) t -= ptexture->srcheight; while (t < 0) t += ptexture->srcheight; for (j = 0, s = ptexture->skinleft + 10 * ptexture->srcwidth; j < ptexture->skinwidth; j++, s++) { while (s >= ptexture->srcwidth) s -= ptexture->srcwidth; *(pdest++) = *(ptexture->ppicture + s + t * srcadjwidth); } } // TODO: process the texture and flag it if fullbright or transparent are used. // TODO: only save as many palette entries as are actually used. if (gamma != 1.8) { // gamma correct the monster textures to a gamma of 1.8 float g; byte *psrc = (byte *)ptexture->ppal; g = gamma / 1.8; for (i = 0; i < 768; i++) { pdest[i] = pow( psrc[i] / 255.0, g ) * 255; } } else { memcpy( pdest, ptexture->ppal, 256 * sizeof( rgb_t ) ); } free( ptexture->ppicture ); free( ptexture->ppal ); } void Grab_Skin ( s_texture_t *ptexture ) { char file1[1024]; int time1; sprintf (file1, "%s/%s", cdpartial, ptexture->name); ExpandPathAndArchive (file1); if (cdtextureset) { int i; for (i = 0; i < cdtextureset; i++) { sprintf (file1, "%s/%s", cdtexture[i], ptexture->name); time1 = FileTime (file1); if (time1 != -1) break; } if (time1 == -1) Error( "%s not found", file1); } else { sprintf (file1, "%s/%s", cddir, ptexture->name); } if (stricmp( ".bmp", &file1[strlen(file1)-4]) == 0) { Grab_BMP( file1, ptexture ); } else { Error("unknown graphics type: \"%s\"\n", file1 ); } } void SetSkinValues( ) { int i, j; int index; for (i = 0; i < numtextures; i++) { Grab_Skin ( &texture[i] ); texture[i].max_s = -9999999; texture[i].min_s = 9999999; texture[i].max_t = -9999999; texture[i].min_t = 9999999; } for (i = 0; i < nummodels; i++) { for (j = 0; j < model[i]->nummesh; j++) { TextureCoordRanges( model[i]->pmesh[j], &texture[model[i]->pmesh[j]->skinref] ); } } for (i = 0; i < numtextures; i++) { if (texture[i].max_s < texture[i].min_s ) { // must be a replacement texture if (texture[i].flags & STUDIO_NF_CHROME) { texture[i].max_s = 63; texture[i].min_s = 0; texture[i].max_t = 63; texture[i].min_t = 0; } else { texture[i].max_s = texture[texture[i].parent].max_s; texture[i].min_s = texture[texture[i].parent].min_s; texture[i].max_t = texture[texture[i].parent].max_t; texture[i].min_t = texture[texture[i].parent].min_t; } } ResizeTexture( &texture[i] ); } for (i = 0; i < nummodels; i++) { for (j = 0; j < model[i]->nummesh; j++) { ResetTextureCoordRanges( model[i]->pmesh[j], &texture[model[i]->pmesh[j]->skinref] ); } } // build texture groups for (i = 0; i < MAXSTUDIOSKINS; i++) { for (j = 0; j < MAXSTUDIOSKINS; j++) { skinref[i][j] = j; } } index = 0; for (i = 0; i < numtexturelayers[0]; i++) { for (j = 0; j < numtexturereps[0]; j++) { skinref[i][texturegroup[0][0][j]] = texturegroup[0][i][j]; } } if (i != 0) { numskinfamilies = i; } else { numskinfamilies = 1; numskinref = numtextures; } // printf ("width: %i height: %i\n",width, height); /* printf ("adjusted width: %i height: %i top : %i left: %i\n", pmesh->skinwidth, pmesh->skinheight, pmesh->skintop, pmesh->skinleft ); */ } /* ================= ================= */ char filename[1024]; FILE *input; char line[1024]; int linecount; void Build_Reference( s_model_t *pmodel) { int i, parent; float angle[3]; for (i = 0; i < pmodel->numbones; i++) { float m[3][4]; vec3_t p; // convert to degrees angle[0] = pmodel->skeleton[i].rot[0] * (180.0 / Q_PI); angle[1] = pmodel->skeleton[i].rot[1] * (180.0 / Q_PI); angle[2] = pmodel->skeleton[i].rot[2] * (180.0 / Q_PI); parent = pmodel->node[i].parent; if (parent == -1) { // scale the done pos. // calc rotational matrices AngleMatrix( angle, bonefixup[i].m ); AngleIMatrix( angle, bonefixup[i].im ); VectorCopy( pmodel->skeleton[i].pos, bonefixup[i].worldorg ); } else { // calc compound rotational matrices // FIXME : Hey, it's orthogical so inv(A) == transpose(A) AngleMatrix( angle, m ); R_ConcatTransforms( bonefixup[parent].m, m, bonefixup[i].m ); AngleIMatrix( angle, m ); R_ConcatTransforms( m, bonefixup[parent].im, bonefixup[i].im ); // calc true world coord. VectorTransform(pmodel->skeleton[i].pos, bonefixup[parent].m, p ); VectorAdd( p, bonefixup[parent].worldorg, bonefixup[i].worldorg ); } // printf("%3d %f %f %f\n", i, bonefixup[i].worldorg[0], bonefixup[i].worldorg[1], bonefixup[i].worldorg[2] ); /* AngleMatrix( angle, m ); printf("%8.4f %8.4f %8.4f\n", m[0][0], m[1][0], m[2][0] ); printf("%8.4f %8.4f %8.4f\n", m[0][1], m[1][1], m[2][1] ); printf("%8.4f %8.4f %8.4f\n", m[0][2], m[1][2], m[2][2] ); */ } } void Grab_Triangles( s_model_t *pmodel ) { int i, j; int tcount = 0; int ncount = 0; vec3_t vmin, vmax; vmin[0] = vmin[1] = vmin[2] = 99999; vmax[0] = vmax[1] = vmax[2] = -99999; Build_Reference( pmodel ); // // load the base triangles // while (1) { if (fgets( line, sizeof( line ), input ) != NULL) { s_mesh_t *pmesh; char texturename[64]; s_trianglevert_t *ptriv; int bone; vec3_t vert[3]; vec3_t norm[3]; linecount++; // check for end if (strcmp( "end\n", line ) == 0) return; // strip off trailing smag strcpy( texturename, line ); for (i = strlen( texturename ) - 1; i >= 0 && ! isgraph( texturename[i] ); i--) ; texturename[i + 1] = '\0'; // funky texture overrides for (i = 0; i < numrep; i++) { if (sourcetexture[i][0] == '\0') { strcpy( texturename, defaulttexture[i] ); break; } if (stricmp( texturename, sourcetexture[i]) == 0) { strcpy( texturename, defaulttexture[i] ); break; } } if (texturename[0] == '\0') { // weird model problem, skip them fgets( line, sizeof( line ), input ); fgets( line, sizeof( line ), input ); fgets( line, sizeof( line ), input ); linecount += 3; continue; } pmesh = lookup_mesh( pmodel, texturename ); for (j = 0; j < 3; j++) { if (flip_triangles) // quake wants them in the reverse order ptriv = lookup_triangle( pmesh, pmesh->numtris ) + 2 - j; else ptriv = lookup_triangle( pmesh, pmesh->numtris ) + j; if (fgets( line, sizeof( line ), input ) != NULL) { s_vertex_t p; vec3_t tmp; s_normal_t normal; linecount++; if (sscanf( line, "%d %f %f %f %f %f %f %f %f", &bone, &p.org[0], &p.org[1], &p.org[2], &normal.org[0], &normal.org[1], &normal.org[2], &ptriv->u, &ptriv->v ) == 9) { if (bone < 0 || bone >= pmodel->numbones) { fprintf( stderr, "bogus bone index\n" ); fprintf(stderr, "%d %s :\n%s", linecount, filename, line ); exit(1); } /* if (ptriv->u > 2.0) { printf("%d %f\n", linecount, ptriv->u ); } */ VectorCopy( p.org, vert[j] ); VectorCopy( normal.org, norm[j] ); p.bone = bone; normal.bone = bone; normal.skinref = pmesh->skinref; if (p.org[2] < vmin[2]) vmin[2] = p.org[2]; adjust_vertex( p.org ); scale_vertex( p.org ); // move vertex position to object space. VectorSubtract( p.org, bonefixup[p.bone].worldorg, tmp ); VectorTransform(tmp, bonefixup[p.bone].im, p.org ); // move normal to object space. VectorCopy( normal.org, tmp ); VectorTransform(tmp, bonefixup[p.bone].im, normal.org ); VectorNormalize( normal.org ); ptriv->normindex = lookup_normal( pmodel, &normal ); ptriv->vertindex = lookup_vertex( pmodel, &p ); // tag bone as being used // pmodel->bone[bone].ref = 1; } else { Error("%s: error on line %d: %s", filename, linecount, line ); } } } if (tag_reversed || tag_normals) { // check triangle direction if (DotProduct( norm[0], norm[1] ) < 0.0 || DotProduct( norm[1], norm[2] ) < 0.0 || DotProduct( norm[2], norm[0] ) < 0.0 ) { ncount++; if (tag_normals) { // steal the triangle and make it white s_trianglevert_t *ptriv2; pmesh = lookup_mesh( pmodel, "..\\white.bmp" ); ptriv2 = lookup_triangle( pmesh, pmesh->numtris ); ptriv2[0] = ptriv[0]; ptriv2[1] = ptriv[1]; ptriv2[2] = ptriv[2]; } } else { vec3_t a1, a2, sn; float x, y, z; VectorSubtract( vert[1], vert[0], a1 ); VectorSubtract( vert[2], vert[0], a2 ); CrossProduct( a1, a2, sn ); VectorNormalize( sn ); x = DotProduct( sn, norm[0] ); y = DotProduct( sn, norm[1] ); z = DotProduct( sn, norm[2] ); if (x < 0.0 || y < 0.0 || z < 0.0) { if (tag_reversed) { // steal the triangle and make it white s_trianglevert_t *ptriv2; printf("triangle reversed (%f %f %f)\n", DotProduct( norm[0], norm[1] ), DotProduct( norm[1], norm[2] ), DotProduct( norm[2], norm[0] ) ); pmesh = lookup_mesh( pmodel, "..\\white.bmp" ); ptriv2 = lookup_triangle( pmesh, pmesh->numtris ); ptriv2[0] = ptriv[0]; ptriv2[1] = ptriv[1]; ptriv2[2] = ptriv[2]; } } } } pmodel->trimesh[tcount] = pmesh; pmodel->trimap[tcount] = pmesh->numtris++; tcount++; } else { break; } } if (ncount) printf("%d triangles with misdirected normals\n", ncount ); if (vmin[2] != 0.0) { printf("lowest vector at %f\n", vmin[2] ); } } void Grab_Skeleton( s_node_t *pnodes, s_bone_t *pbones ) { float x, y, z, xr, yr, zr; char cmd[1024]; int index; while (fgets( line, sizeof( line ), input ) != NULL) { linecount++; if (sscanf( line, "%d %f %f %f %f %f %f", &index, &x, &y, &z, &xr, &yr, &zr ) == 7) { pbones[index].pos[0] = x; pbones[index].pos[1] = y; pbones[index].pos[2] = z; scale_vertex( pbones[index].pos ); if (pnodes[index].mirrored) VectorScale( pbones[index].pos, -1.0, pbones[index].pos ); pbones[index].rot[0] = xr; pbones[index].rot[1] = yr; pbones[index].rot[2] = zr; clip_rotations( pbones[index].rot ); } else if (sscanf( line, "%s %d", cmd, &index )) { if (strcmp( cmd, "time" ) == 0) { // pbones = pnode->bones[index] = kalloc(1, sizeof( s_bones_t )); } else if (strcmp( cmd, "end") == 0) { return; } } } } int Grab_Nodes( s_node_t *pnodes ) { int index; char name[1024]; int parent; int numbones = 0; int i; while (fgets( line, sizeof( line ), input ) != NULL) { linecount++; if (sscanf( line, "%d \"%[^\"]\" %d", &index, name, &parent ) == 3) { // check for duplicated bones /* if (strlen(pnodes[index].name) != 0) { Error( "bone \"%s\" exists more than once\n", name ); } */ strcpyn( pnodes[index].name, name ); pnodes[index].parent = parent; numbones = index; // check for mirrored bones; for (i = 0; i < nummirrored; i++) { if (strcmp( name, mirrored[i] ) == 0) pnodes[index].mirrored = 1; } if ((! pnodes[index].mirrored) && parent != -1) { pnodes[index].mirrored = pnodes[pnodes[index].parent].mirrored; } } else { return numbones + 1; } } Error( "Unexpected EOF at line %d\n", linecount ); return 0; } void Grab_Studio ( s_model_t *pmodel ) { int time1; char cmd[1024]; int option; sprintf (filename, "%s/%s.smd", cddir, pmodel->name); time1 = FileTime (filename); if (time1 == -1) Error ("%s doesn't exist", filename); printf ("grabbing %s\n", filename); if ((input = fopen(filename, "r")) == 0) { fprintf(stderr,"reader: could not open file '%s'\n", filename); } linecount = 0; while (fgets( line, sizeof( line ), input ) != NULL) { linecount++; sscanf( line, "%s %d", cmd, &option ); if (strcmp( cmd, "version" ) == 0) { if (option != 1) { Error("bad version\n"); } } else if (strcmp( cmd, "nodes" ) == 0) { pmodel->numbones = Grab_Nodes( pmodel->node ); } else if (strcmp( cmd, "skeleton" ) == 0) { Grab_Skeleton( pmodel->node, pmodel->skeleton ); } else if (strcmp( cmd, "triangles" ) == 0) { Grab_Triangles( pmodel ); } else { printf("unknown studio command\n" ); } } fclose( input ); } void clip_rotations( vec3_t rot ) { int j; // clip everything to : -Q_PI <= x < Q_PI for (j = 0; j < 3; j++) { while (rot[j] >= Q_PI) rot[j] -= Q_PI*2; while (rot[j] < -Q_PI) rot[j] += Q_PI*2; } } /* ================= Cmd_Eyeposition ================= */ void Cmd_Eyeposition (void) { // rotate points into frame of reference so model points down the positive x // axis GetToken (false); eyeposition[1] = atof (token); GetToken (false); eyeposition[0] = -atof (token); GetToken (false); eyeposition[2] = atof (token); } /* ================= Cmd_Flags ================= */ void Cmd_Flags (void) { GetToken (false); gflags = atoi (token); } /* ================= Cmd_Modelname ================= */ void Cmd_Modelname (void) { GetToken (false); strcpyn (outname, token); } /* =============== =============== */ void Option_Studio( ) { if (!GetToken (false)) return; model[nummodels] = kalloc( 1, sizeof( s_model_t ) ); bodypart[numbodyparts].pmodel[bodypart[numbodyparts].nummodels] = model[nummodels]; strcpyn( model[nummodels]->name, token ); flip_triangles = 1; scale_up = default_scale; while (TokenAvailable()) { GetToken(false); if (stricmp( "reverse", token ) == 0) { flip_triangles = 0; } else if (stricmp( "scale", token ) == 0) { GetToken(false); scale_up = atof( token ); } } Grab_Studio( model[nummodels] ); bodypart[numbodyparts].nummodels++; nummodels++; scale_up = default_scale; } int Option_Blank( ) { model[nummodels] = kalloc( 1, sizeof( s_model_t ) ); bodypart[numbodyparts].pmodel[bodypart[numbodyparts].nummodels] = model[nummodels]; strcpyn( model[nummodels]->name, "blank" ); bodypart[numbodyparts].nummodels++; nummodels++; return 0; } void Cmd_Bodygroup( ) { int is_started = 0; if (!GetToken(false)) return; if (numbodyparts == 0) { bodypart[numbodyparts].base = 1; } else { bodypart[numbodyparts].base = bodypart[numbodyparts-1].base * bodypart[numbodyparts-1].nummodels; } strcpyn( bodypart[numbodyparts].name, token ); do { GetToken (true); if (endofscript) return; else if (token[0] == '{') is_started = 1; else if (token[0] == '}') break; else if (stricmp("studio", token ) == 0) Option_Studio( ); else if (stricmp("blank", token ) == 0) Option_Blank( ); } while (1); numbodyparts++; return; } void Cmd_Body( ) { int is_started = 0; if (!GetToken(false)) return; if (numbodyparts == 0) { bodypart[numbodyparts].base = 1; } else { bodypart[numbodyparts].base = bodypart[numbodyparts-1].base * bodypart[numbodyparts-1].nummodels; } strcpyn(bodypart[numbodyparts].name, token ); Option_Studio( ); numbodyparts++; } /* =============== =============== */ void Grab_Animation( s_animation_t *panim) { vec3_t pos; vec3_t rot; char cmd[1024]; int index; int t = -99999999; float cz, sz; int start = 99999; int end = 0; for (index = 0; index < panim->numbones; index++) { panim->pos[index] = kalloc( MAXSTUDIOANIMATIONS, sizeof( vec3_t ) ); panim->rot[index] = kalloc( MAXSTUDIOANIMATIONS, sizeof( vec3_t ) ); } cz = cos( zrotation ); sz = sin( zrotation ); while (fgets( line, sizeof( line ), input ) != NULL) { linecount++; if (sscanf( line, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &rot[0], &rot[1], &rot[2] ) == 7) { if (t >= panim->startframe && t <= panim->endframe) { if (panim->node[index].parent == -1) { adjust_vertex( pos ); panim->pos[index][t][0] = cz * pos[0] - sz * pos[1]; panim->pos[index][t][1] = sz * pos[0] + cz * pos[1]; panim->pos[index][t][2] = pos[2]; // rotate model rot[2] += zrotation; } else { VectorCopy( pos, panim->pos[index][t] ); } if (t > end) end = t; if (t < start) start = t; if (panim->node[index].mirrored) VectorScale( panim->pos[index][t], -1.0, panim->pos[index][t] ); scale_vertex( panim->pos[index][t] ); clip_rotations( rot ); VectorCopy( rot, panim->rot[index][t] ); } } else if (sscanf( line, "%s %d", cmd, &index )) { if (strcmp( cmd, "time" ) == 0) { t = index; } else if (strcmp( cmd, "end") == 0) { panim->startframe = start; panim->endframe = end; return; } else { Error( "Error(%d) : %s", linecount, line ); } } else { Error( "Error(%d) : %s", linecount, line ); } } Error( "unexpected EOF: %s\n", panim->name ); } void Shift_Animation( s_animation_t *panim) { int j; int size; size = (panim->endframe - panim->startframe + 1) * sizeof( vec3_t ); // shift for (j = 0; j < panim->numbones; j++) { vec3_t *ppos; vec3_t *prot; k_memtotal -= MAXSTUDIOANIMATIONS * sizeof( vec3_t ) * 2; k_memtotal += size * 2; ppos = kalloc( 1, size ); prot = kalloc( 1, size ); memmove( ppos, &panim->pos[j][panim->startframe], size ); memmove( prot, &panim->rot[j][panim->startframe], size ); free( panim->pos[j] ); free( panim->rot[j] ); panim->pos[j] = ppos; panim->rot[j] = prot; } } void Option_Animation ( char *name, s_animation_t *panim ) { int time1; char cmd[1024]; int option; strcpyn( panim->name, name ); sprintf (filename, "%s/%s.smd", cddir, panim->name); time1 = FileTime (filename); if (time1 == -1) Error ("%s doesn't exist", filename); printf ("grabbing %s\n", filename); if ((input = fopen(filename, "r")) == 0) { fprintf(stderr,"reader: could not open file '%s'\n", filename); Error(0); } linecount = 0; while (fgets( line, sizeof( line ), input ) != NULL) { linecount++; sscanf( line, "%s %d", cmd, &option ); if (strcmp( cmd, "version" ) == 0) { if (option != 1) { Error("bad version\n"); } } else if (strcmp( cmd, "nodes" ) == 0) { panim->numbones = Grab_Nodes( panim->node ); } else if (strcmp( cmd, "skeleton" ) == 0) { Grab_Animation( panim ); Shift_Animation( panim ); } else { printf("unknown studio command : %s\n", cmd ); while (fgets( line, sizeof( line ), input ) != NULL) { linecount++; if (strncmp(line,"end",3)==0) break; } } } fclose( input ); } int Option_Deform ( s_sequence_t *psequence ) { return 0; } /* =============== =============== */ int Option_Motion ( s_sequence_t *psequence ) { while (TokenAvailable()) { GetToken (false); psequence->motiontype |= lookupControl( token ); } return 0; } int Option_Event ( s_sequence_t *psequence ) { int event; if (psequence->numevents + 1 >= MAXSTUDIOEVENTS) { printf("too many events\n"); exit(0); } GetToken (false); event = atoi( token ); psequence->event[psequence->numevents].event = event; GetToken( false ); psequence->event[psequence->numevents].frame = atoi( token ); psequence->numevents++; // option token if (TokenAvailable()) { GetToken( false ); if (token[0] == '}') // opps, hit the end return 1; // found an option strcpy( psequence->event[psequence->numevents-1].options, token ); } return 0; } int Option_Fps ( s_sequence_t *psequence ) { GetToken (false); psequence->fps = atof( token ); return 0; } int Option_AddPivot ( s_sequence_t *psequence ) { if (psequence->numpivots + 1 >= MAXSTUDIOPIVOTS) { printf("too many pivot points\n"); exit(0); } GetToken (false); psequence->pivot[psequence->numpivots].index = atoi( token ); GetToken (false); psequence->pivot[psequence->numpivots].start = atoi( token ); GetToken (false); psequence->pivot[psequence->numpivots].end = atoi( token ); psequence->numpivots++; return 0; } /* ================= Option_Origin ================= */ void Cmd_Origin (void) { GetToken (false); defaultadjust[0] = atof (token); GetToken (false); defaultadjust[1] = atof (token); GetToken (false); defaultadjust[2] = atof (token); if (TokenAvailable()) { GetToken (false); defaultzrotation = (atof( token ) + 90) * (Q_PI / 180.0); } } void Option_Origin (void) { GetToken (false); adjust[0] = atof (token); GetToken (false); adjust[1] = atof (token); GetToken (false); adjust[2] = atof (token); } void Option_Rotate(void ) { GetToken (false); zrotation = (atof( token ) + 90) * (Q_PI / 180.0); } /* ================= ================= */ void Cmd_ScaleUp (void) { GetToken (false); default_scale = scale_up = atof (token); } void Option_ScaleUp (void) { GetToken (false); scale_up = atof (token); } /* ================= ================= */ int Cmd_SequenceGroup( ) { GetToken (false); strcpyn( sequencegroup[numseqgroups].label, token ); numseqgroups++; return 0; } int Cmd_SequenceGroupSize( ) { GetToken (false); maxseqgroupsize = 1024 * atoi( token ); return 0; } int lookupActivity( char *szActivity ) { int i; for (i = 0; activity_map[i].name; i++) { if (stricmp( szActivity, activity_map[i].name ) == 0) return activity_map[i].type; } // match ACT_# if (strnicmp( szActivity, "ACT_", 4 ) == 0) { return atoi( &szActivity[4] ); } return 0; } int Cmd_Sequence( ) { int depth = 0; char smdfilename[4][1024]; int i; int numblends = 0; int start = 0; int end = MAXSTUDIOANIMATIONS - 1; if (!GetToken(false)) return 0; strcpyn( sequence[numseq].name, token ); VectorCopy( defaultadjust, adjust ); scale_up = default_scale; zrotation = defaultzrotation; sequence[numseq].fps = 30.0; sequence[numseq].seqgroup = numseqgroups - 1; sequence[numseq].blendstart[0] = 0.0; sequence[numseq].blendend[0] = 1.0; while (1) { if (depth > 0) { if(!GetToken(true)) { break; } } else { if (!TokenAvailable()) { break; } else { GetToken (false); } } if (endofscript) { if (depth != 0) { printf("missing }\n" ); exit(1); } return 1; } if (stricmp("{", token ) == 0) { depth++; } else if (stricmp("}", token ) == 0) { depth--; } else if (stricmp("deform", token ) == 0) { Option_Deform( &sequence[numseq] ); } else if (stricmp("event", token ) == 0) { depth -= Option_Event( &sequence[numseq] ); } else if (stricmp("pivot", token ) == 0) { Option_AddPivot( &sequence[numseq] ); } else if (stricmp("fps", token ) == 0) { Option_Fps( &sequence[numseq] ); } else if (stricmp("origin", token ) == 0) { Option_Origin( ); } else if (stricmp("rotate", token ) == 0) { Option_Rotate( ); } else if (stricmp("scale", token ) == 0) { Option_ScaleUp( ); } else if (strnicmp("loop", token, 4 ) == 0) { sequence[numseq].flags |= STUDIO_LOOPING; } else if (strnicmp("frame", token, 5 ) == 0) { GetToken( false ); start = atoi( token ); GetToken( false ); end = atoi( token ); } else if (strnicmp("blend", token, 5 ) == 0) { GetToken( false ); sequence[numseq].blendtype[0] = lookupControl( token ); GetToken( false ); sequence[numseq].blendstart[0] = atof( token ); GetToken( false ); sequence[numseq].blendend[0] = atof( token ); } else if (strnicmp("node", token, 4 ) == 0) { GetToken( false ); sequence[numseq].entrynode = sequence[numseq].exitnode = atoi( token ); } else if (strnicmp("transition", token, 4 ) == 0) { GetToken( false ); sequence[numseq].entrynode = atoi( token ); GetToken( false ); sequence[numseq].exitnode = atoi( token ); } else if (strnicmp("rtransition", token, 4 ) == 0) { GetToken( false ); sequence[numseq].entrynode = atoi( token ); GetToken( false ); sequence[numseq].exitnode = atoi( token ); sequence[numseq].nodeflags |= 1; } else if (lookupControl( token ) != -1) { sequence[numseq].motiontype |= lookupControl( token ); } else if (stricmp("animation", token ) == 0) { GetToken(false); strcpyn( smdfilename[numblends++], token ); } else if ((i = lookupActivity( token )) != 0) { sequence[numseq].activity = i; GetToken( false ); sequence[numseq].actweight = atoi( token ); } else { strcpyn( smdfilename[numblends++], token ); } if (depth < 0) { printf("missing {\n"); exit(1); } }; if (numblends == 0) { printf("no animations found\n"); exit(1); } for (i = 0; i < numblends; i++) { panimation[numani] = kalloc( 1, sizeof( s_animation_t ) ); sequence[numseq].panim[i] = panimation[numani]; sequence[numseq].panim[i]->startframe = start; sequence[numseq].panim[i]->endframe = end; sequence[numseq].panim[i]->flags = 0; Option_Animation( smdfilename[i], panimation[numani] ); numani++; } sequence[numseq].numblends = numblends; numseq++; return 0; } /* ================= ================= */ int Cmd_Root (void) { if (GetToken (false)) { strcpyn( pivotname[0], token ); return 0; } return 1; } int Cmd_Pivot (void) { if (GetToken (false)) { int index = atoi(token); if (GetToken(false)) { strcpyn( pivotname[index], token ); return 0; } } return 1; } int Cmd_Controller (void) { if (GetToken (false)) { if (!strcmpi("mouth",token)) { bonecontroller[numbonecontrollers].index = 4; } else { bonecontroller[numbonecontrollers].index = atoi(token); } if (GetToken(false)) { strcpyn( bonecontroller[numbonecontrollers].name, token ); GetToken(false); if ((bonecontroller[numbonecontrollers].type = lookupControl(token)) == -1) { printf("unknown bonecontroller type '%s'\n", token ); return 0; } GetToken(false); bonecontroller[numbonecontrollers].start = atof( token ); GetToken(false); bonecontroller[numbonecontrollers].end = atof( token ); if (bonecontroller[numbonecontrollers].type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) { if (((int)(bonecontroller[numbonecontrollers].start + 360) % 360) == ((int)(bonecontroller[numbonecontrollers].end + 360) % 360)) { bonecontroller[numbonecontrollers].type |= STUDIO_RLOOP; } } numbonecontrollers++; } } return 1; } /* ================= ================= */ void Cmd_BBox (void) { GetToken (false); bbox[0][0] = atof( token ); GetToken (false); bbox[0][1] = atof( token ); GetToken (false); bbox[0][2] = atof( token ); GetToken (false); bbox[1][0] = atof( token ); GetToken (false); bbox[1][1] = atof( token ); GetToken (false); bbox[1][2] = atof( token ); } /* ================= ================= */ void Cmd_CBox (void) { GetToken (false); cbox[0][0] = atof( token ); GetToken (false); cbox[0][1] = atof( token ); GetToken (false); cbox[0][2] = atof( token ); GetToken (false); cbox[1][0] = atof( token ); GetToken (false); cbox[1][1] = atof( token ); GetToken (false); cbox[1][2] = atof( token ); } /* ================= ================= */ void Cmd_Mirror (void) { GetToken (false); strcpyn( mirrored[nummirrored++], token ); } /* ================= ================= */ void Cmd_Gamma (void) { GetToken (false); gamma = atof( token ); } /* ================= ================= */ int Cmd_TextureGroup( ) { int i; int depth = 0; int index = 0; int group = 0; if (numtextures == 0) Error( "texturegroups must follow model loading\n"); if (!GetToken(false)) return 0; if (numskinref == 0) numskinref = numtextures; while (1) { if(!GetToken(true)) { break; } if (endofscript) { if (depth != 0) { Error("missing }\n" ); } return 1; } if (token[0] == '{') { depth++; } else if (token[0] == '}') { depth--; if (depth == 0) break; group++; index = 0; } else if (depth == 2) { i = lookup_texture( token ); texturegroup[numtexturegroups][group][index] = i; if (group != 0) texture[i].parent = texturegroup[numtexturegroups][0][index]; index++; numtexturereps[numtexturegroups] = index; numtexturelayers[numtexturegroups] = group + 1; } } numtexturegroups++; return 0; } /* ================= ================= */ int Cmd_Hitgroup( ) { GetToken (false); hitgroup[numhitgroups].group = atoi( token ); GetToken (false); strcpyn( hitgroup[numhitgroups].name, token ); numhitgroups++; return 0; } int Cmd_Hitbox( ) { GetToken (false); hitbox[numhitboxes].group = atoi( token ); GetToken (false); strcpyn( hitbox[numhitboxes].name, token ); GetToken (false); hitbox[numhitboxes].bmin[0] = atof( token ); GetToken (false); hitbox[numhitboxes].bmin[1] = atof( token ); GetToken (false); hitbox[numhitboxes].bmin[2] = atof( token ); GetToken (false); hitbox[numhitboxes].bmax[0] = atof( token ); GetToken (false); hitbox[numhitboxes].bmax[1] = atof( token ); GetToken (false); hitbox[numhitboxes].bmax[2] = atof( token ); numhitboxes++; return 0; } /* ================= ================= */ int Cmd_Attachment( ) { // index GetToken (false); attachment[numattachments].index = atoi( token ); // bone name GetToken (false); strcpyn( attachment[numattachments].bonename, token ); // position GetToken (false); attachment[numattachments].org[0] = atof( token ); GetToken (false); attachment[numattachments].org[1] = atof( token ); GetToken (false); attachment[numattachments].org[2] = atof( token ); if (TokenAvailable()) GetToken (false); if (TokenAvailable()) GetToken (false); numattachments++; return 0; } /* ================= ================= */ void Cmd_Renamebone( ) { // from GetToken (false); strcpy( renamedbone[numrenamedbones].from, token ); // to GetToken (false); strcpy( renamedbone[numrenamedbones].to, token ); numrenamedbones++; } /* =============== ParseScript =============== */ void ParseScript (void) { while (1) { do { // look for a line starting with a $ command GetToken (true); if (endofscript) return; if (token[0] == '$') break; while (TokenAvailable()) GetToken (false); } while (1); if (!strcmp (token, "$modelname")) { Cmd_Modelname (); } else if (!strcmp (token, "$cd")) { if (cdset) Error ("Two $cd in one model"); cdset = true; GetToken (false); strcpy (cdpartial, token); strcpy (cddir, ExpandPath(token)); } else if (!strcmp (token, "$cdtexture")) { while (TokenAvailable()) { GetToken (false); strcpy (cdtexture[cdtextureset], ExpandPath(token)); cdtextureset++; } } else if (!strcmp (token, "$scale")) { Cmd_ScaleUp (); } else if (!strcmp (token, "$root")) { Cmd_Root (); } else if (!strcmp (token, "$pivot")) { Cmd_Pivot (); } else if (!strcmp (token, "$controller")) { Cmd_Controller (); } else if (!strcmp (token, "$body")) { Cmd_Body(); } else if (!strcmp (token, "$bodygroup")) { Cmd_Bodygroup(); } else if (!strcmp (token, "$sequence")) { Cmd_Sequence (); } else if (!strcmp (token, "$sequencegroup")) { Cmd_SequenceGroup (); } else if (!strcmp (token, "$sequencegroupsize")) { Cmd_SequenceGroupSize (); } else if (!strcmp (token, "$eyeposition")) { Cmd_Eyeposition (); } else if (!strcmp (token, "$origin")) { Cmd_Origin (); } else if (!strcmp (token, "$bbox")) { Cmd_BBox (); } else if (!strcmp (token, "$cbox")) { Cmd_CBox (); } else if (!strcmp (token, "$mirrorbone")) { Cmd_Mirror (); } else if (!strcmp (token, "$gamma")) { Cmd_Gamma (); } else if (!strcmp (token, "$flags")) { Cmd_Flags (); } else if (!strcmp (token, "$texturegroup")) { Cmd_TextureGroup (); } else if (!strcmp (token, "$hgroup")) { Cmd_Hitgroup (); } else if (!strcmp (token, "$hbox")) { Cmd_Hitbox (); } else if (!strcmp (token, "$attachment")) { Cmd_Attachment (); } else if (!strcmp (token, "$externaltextures")) { split_textures = 1; } else if (!strcmp (token, "$cliptotextures")) { clip_texcoords = 1; } else if (!strcmp (token, "$renamebone")) { Cmd_Renamebone (); } else { Error ("bad command %s\n", token); } } } /* ============== main ============== */ int main (int argc, char **argv) { int i; char path[1024]; default_scale = 1.0; defaultzrotation = Q_PI / 2; numrep = 0; tag_reversed = 0; tag_normals = 0; flip_triangles = 1; maxseqgroupsize = 1024 * 1024; normal_blend = cos( 2.0 * (Q_PI / 180.0)); gamma = 1.8; if (argc == 1) Error ("usage: studiomdl [-t texture] -r(tag reversed) -n(tag bad normals) -f(flip all triangles) [-a normal_blend_angle] -h(dump hboxes) -i(ignore warnings) [-g max_sequencegroup_size(K)] file.qc"); for (i = 1; i < argc - 1; i++) { if (argv[i][0] == '-') { switch( argv[i][1] ) { case 't': i++; strcpy ( defaulttexture[numrep], argv[i]); if (i < argc - 2 && argv[i + 1][0] != '-') { i++; strcpy ( sourcetexture[numrep], argv[i]); printf ("Replaceing %s with %s\n", sourcetexture[numrep], defaulttexture[numrep] ); } printf ("Using default texture: %s\n", defaulttexture); numrep++; break; case 'r': tag_reversed = 1; break; case 'n': tag_normals = 1; break; case 'f': flip_triangles = 0; break; case 'a': i++; normal_blend = cos( atof( argv[i] ) * (Q_PI / 180.0)); break; case 'h': dump_hboxes = 1; break; case 'g': i++; maxseqgroupsize = 1024 * atoi( argv[i] ); break; case 'i': ignore_warnings = 1; break; } } } strcpy( sequencegroup[numseqgroups].label, "default" ); numseqgroups = 1; // // load the script // strcpy (path, argv[i]); DefaultExtension (path, ".qc"); // SetQdirFromPath (path); LoadScriptFile (path); // // parse it // ClearModel (); strcpy (outname, argv[i]); ParseScript (); SetSkinValues (); SimplifyModel (); WriteFile (); return 0; }