//======================================================================= // Copyright XashXT Group 2007 © // studio_utils.c - common compiler funcs //======================================================================= #include "mdllib.h" typedef struct { int type; char *name; } activity_map_t; // studio activity map conversion int used[MAXSTUDIOTRIANGLES]; // the command list holds counts and s/t values short commands[MAXSTUDIOTRIANGLES * 13]; // that are valid for every frame int stripverts[MAXSTUDIOTRIANGLES+2]; int striptris[MAXSTUDIOTRIANGLES+2]; int neighbortri[MAXSTUDIOTRIANGLES][3]; int neighboredge[MAXSTUDIOTRIANGLES][3]; s_trianglevert_t (*triangles)[3]; enum ai_activity { ACT_RESET = 0, // Set m_Activity to this invalid value to force a reset to m_IdealActivity ACT_IDLE = 1, ACT_GUARD, ACT_WALK, ACT_RUN, ACT_FLY, // Fly (and flap if appropriate) ACT_SWIM, ACT_HOP, // vertical jump ACT_LEAP, // long forward jump ACT_FALL, ACT_LAND, ACT_STRAFE_LEFT, ACT_STRAFE_RIGHT, ACT_ROLL_LEFT, // tuck and roll, left ACT_ROLL_RIGHT, // tuck and roll, right ACT_TURN_LEFT, // turn quickly left (stationary) ACT_TURN_RIGHT, // turn quickly right (stationary) ACT_CROUCH, // the act of crouching down from a standing position ACT_CROUCHIDLE, // holding body in crouched position (loops) ACT_STAND, // the act of standing from a crouched position ACT_USE, ACT_SIGNAL1, ACT_SIGNAL2, ACT_SIGNAL3, ACT_TWITCH, ACT_COWER, ACT_SMALL_FLINCH, ACT_BIG_FLINCH, ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK2, ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK2, ACT_RELOAD, ACT_ARM, // pull out gun, for instance ACT_DISARM, // reholster gun ACT_EAT, // monster chowing on a large food item (loop) ACT_DIESIMPLE, ACT_DIEBACKWARD, ACT_DIEFORWARD, ACT_DIEVIOLENT, ACT_BARNACLE_HIT, // barnacle tongue hits a monster ACT_BARNACLE_PULL, // barnacle is lifting the monster ( loop ) ACT_BARNACLE_CHOMP, // barnacle latches on to the monster ACT_BARNACLE_CHEW, // barnacle is holding the monster in its mouth ( loop ) ACT_SLEEP, ACT_INSPECT_FLOOR, // for active idles, look at something on or near the floor ACT_INSPECT_WALL, // for active idles, look at something directly ahead of you ACT_IDLE_ANGRY, // alternate idle animation in which the monster is clearly agitated. (loop) ACT_WALK_HURT, // limp (loop) ACT_RUN_HURT, // limp (loop) ACT_HOVER, // Idle while in flight ACT_GLIDE, // Fly (don't flap) ACT_FLY_LEFT, // Turn left in flight ACT_FLY_RIGHT, // Turn right in flight ACT_DETECT_SCENT, // this means the monster smells a scent carried by the air ACT_SNIFF, // this is the act of actually sniffing an item in front of the monster ACT_BITE, // some large monsters can eat small things in one bite. This plays one time, EAT loops. ACT_THREAT_DISPLAY, // without attacking, monster demonstrates that it is angry. (Yell, stick out chest, etc ) ACT_FEAR_DISPLAY, // monster just saw something that it is afraid of ACT_EXCITED, // for some reason, monster is excited. Sees something he really likes to eat, or whatever ACT_SPECIAL_ATTACK1,// very monster specific special attacks. ACT_SPECIAL_ATTACK2, ACT_COMBAT_IDLE, // agitated idle. ACT_WALK_SCARED, ACT_RUN_SCARED, ACT_VICTORY_DANCE, // killed a player, do a victory dance. ACT_DIE_HEADSHOT, // die, hit in head. ACT_DIE_CHESTSHOT, // die, hit in chest ACT_DIE_GUTSHOT, // die, hit in gut ACT_DIE_BACKSHOT, // die, hit in back ACT_FLINCH_HEAD, ACT_FLINCH_CHEST, ACT_FLINCH_STOMACH, ACT_FLINCH_LEFTARM, ACT_FLINCH_RIGHTARM, ACT_FLINCH_LEFTLEG, ACT_FLINCH_RIGHTLEG, ACT_VM_NONE, // weapon viewmodel animations ACT_VM_DEPLOY, // deploy ACT_VM_DEPLOY_EMPTY,// deploy empty weapon ACT_VM_HOLSTER, // holster empty weapon ACT_VM_HOLSTER_EMPTY, ACT_VM_IDLE1, ACT_VM_IDLE2, ACT_VM_IDLE3, ACT_VM_RANGE_ATTACK1, ACT_VM_RANGE_ATTACK2, ACT_VM_RANGE_ATTACK3, ACT_VM_MELEE_ATTACK1, ACT_VM_MELEE_ATTACK2, ACT_VM_MELEE_ATTACK3, ACT_VM_SHOOT_EMPTY, ACT_VM_START_RELOAD, ACT_VM_RELOAD, ACT_VM_RELOAD_EMPTY, ACT_VM_TURNON, ACT_VM_TURNOFF, ACT_VM_PUMP, // pumping gun ACT_VM_PUMP_EMPTY, ACT_VM_START_CHARGE, ACT_VM_CHARGE, ACT_VM_OVERLOAD, ACT_VM_IDLE_EMPTY, }; static activity_map_t activity_map[] = { {ACT_IDLE, "ACT_IDLE" }, {ACT_GUARD, "ACT_GUARD" }, {ACT_WALK, "ACT_WALK" }, {ACT_RUN, "ACT_RUN" }, {ACT_FLY, "ACT_FLY" }, {ACT_SWIM, "ACT_SWIM", }, {ACT_HOP, "ACT_HOP", }, {ACT_LEAP, "ACT_LEAP" }, {ACT_FALL, "ACT_FALL" }, {ACT_LAND, "ACT_LAND" }, {ACT_STRAFE_LEFT, "ACT_STRAFE_LEFT" }, {ACT_STRAFE_RIGHT, "ACT_STRAFE_RIGHT" }, {ACT_ROLL_LEFT, "ACT_ROLL_LEFT" }, {ACT_ROLL_RIGHT, "ACT_ROLL_RIGHT" }, {ACT_TURN_LEFT, "ACT_TURN_LEFT" }, {ACT_TURN_RIGHT, "ACT_TURN_RIGHT" }, {ACT_CROUCH, "ACT_CROUCH" }, {ACT_CROUCHIDLE, "ACT_CROUCHIDLE" }, {ACT_STAND, "ACT_STAND" }, {ACT_USE, "ACT_USE" }, {ACT_SIGNAL1, "ACT_SIGNAL1" }, {ACT_SIGNAL2, "ACT_SIGNAL2" }, {ACT_SIGNAL3, "ACT_SIGNAL3" }, {ACT_TWITCH, "ACT_TWITCH" }, {ACT_COWER, "ACT_COWER" }, {ACT_SMALL_FLINCH, "ACT_SMALL_FLINCH" }, {ACT_BIG_FLINCH, "ACT_BIG_FLINCH" }, {ACT_RANGE_ATTACK1, "ACT_RANGE_ATTACK1" }, {ACT_RANGE_ATTACK2, "ACT_RANGE_ATTACK2" }, {ACT_MELEE_ATTACK1, "ACT_MELEE_ATTACK1" }, {ACT_MELEE_ATTACK2, "ACT_MELEE_ATTACK2" }, {ACT_RELOAD, "ACT_RELOAD" }, {ACT_ARM, "ACT_ARM" }, {ACT_DISARM, "ACT_DISARM" }, {ACT_EAT, "ACT_EAT" }, {ACT_DIESIMPLE, "ACT_DIESIMPLE" }, {ACT_DIEBACKWARD, "ACT_DIEBACKWARD" }, {ACT_DIEFORWARD, "ACT_DIEFORWARD" }, {ACT_DIEVIOLENT, "ACT_DIEVIOLENT" }, {ACT_BARNACLE_HIT, "ACT_BARNACLE_HIT" }, {ACT_BARNACLE_PULL, "ACT_BARNACLE_PULL" }, {ACT_BARNACLE_CHOMP, "ACT_BARNACLE_CHOMP" }, {ACT_BARNACLE_CHEW, "ACT_BARNACLE_CHEW" }, {ACT_SLEEP, "ACT_SLEEP" }, {ACT_INSPECT_FLOOR, "ACT_INSPECT_FLOOR" }, {ACT_INSPECT_WALL, "ACT_INSPECT_WALL" }, {ACT_IDLE_ANGRY, "ACT_IDLE_ANGRY" }, {ACT_WALK_HURT, "ACT_WALK_HURT" }, {ACT_RUN_HURT, "ACT_RUN_HURT" }, {ACT_HOVER, "ACT_HOVER" }, {ACT_GLIDE, "ACT_GLIDE" }, {ACT_FLY_LEFT, "ACT_FLY_LEFT" }, {ACT_FLY_RIGHT, "ACT_FLY_RIGHT" }, {ACT_DETECT_SCENT, "ACT_DETECT_SCENT" }, {ACT_SNIFF, "ACT_SNIFF" }, {ACT_BITE, "ACT_BITE" }, {ACT_THREAT_DISPLAY, "ACT_THREAT_DISPLAY" }, {ACT_FEAR_DISPLAY, "ACT_FEAR_DISPLAY" }, {ACT_EXCITED, "ACT_EXCITED" }, {ACT_SPECIAL_ATTACK1, "ACT_SPECIAL_ATTACK1" }, {ACT_SPECIAL_ATTACK2, "ACT_SPECIAL_ATTACK2" }, {ACT_COMBAT_IDLE, "ACT_COMBAT_IDLE" }, {ACT_WALK_SCARED, "ACT_WALK_SCARED" }, {ACT_RUN_SCARED, "ACT_RUN_SCARED" }, {ACT_VICTORY_DANCE, "ACT_VICTORY_DANCE" }, {ACT_DIE_HEADSHOT, "ACT_DIE_HEADSHOT" }, {ACT_DIE_CHESTSHOT, "ACT_DIE_CHESTSHOT" }, {ACT_DIE_GUTSHOT, "ACT_DIE_GUTSHOT" }, {ACT_DIE_BACKSHOT, "ACT_DIE_BACKSHOT" }, {ACT_FLINCH_HEAD, "ACT_FLINCH_HEAD" }, {ACT_FLINCH_CHEST, "ACT_FLINCH_CHEST" }, {ACT_FLINCH_STOMACH, "ACT_FLINCH_STOMACH" }, {ACT_FLINCH_LEFTARM, "ACT_FLINCH_LEFTARM" }, {ACT_FLINCH_RIGHTARM, "ACT_FLINCH_RIGHTARM" }, {ACT_FLINCH_LEFTLEG, "ACT_FLINCH_LEFTLEG" }, {ACT_FLINCH_RIGHTLEG, "ACT_FLINCH_RIGHTLEG" }, {ACT_VM_NONE, "ACT_VM_NONE" }, // invalid animation {ACT_VM_DEPLOY, "ACT_VM_DEPLOY" }, // deploy {ACT_VM_DEPLOY_EMPTY, "ACT_VM_DEPLOY_EMPTY" }, // deploy empty weapon {ACT_VM_HOLSTER, "ACT_VM_HOLSTER" }, // holster empty weapon {ACT_VM_HOLSTER_EMPTY, "ACT_VM_HOLSTER_EMPTY" }, {ACT_VM_IDLE1, "ACT_VM_IDLE1" }, {ACT_VM_IDLE2, "ACT_VM_IDLE2" }, {ACT_VM_IDLE3, "ACT_VM_IDLE3" }, {ACT_VM_RANGE_ATTACK1, "ACT_VM_RANGE_ATTACK1" }, {ACT_VM_RANGE_ATTACK2, "ACT_VM_RANGE_ATTACK2" }, {ACT_VM_RANGE_ATTACK3, "ACT_VM_RANGE_ATTACK3" }, {ACT_VM_MELEE_ATTACK1, "ACT_VM_MELEE_ATTACK1" }, {ACT_VM_MELEE_ATTACK2, "ACT_VM_MELEE_ATTACK2" }, {ACT_VM_MELEE_ATTACK3, "ACT_VM_MELEE_ATTACK3" }, {ACT_VM_SHOOT_EMPTY, "ACT_VM_SHOOT_EMPTY" }, {ACT_VM_START_RELOAD, "ACT_VM_START_RELOAD" }, {ACT_VM_RELOAD, "ACT_VM_RELOAD" }, {ACT_VM_RELOAD_EMPTY, "ACT_VM_RELOAD_EMPTY" }, {ACT_VM_TURNON, "ACT_VM_TURNON" }, {ACT_VM_TURNOFF, "ACT_VM_TURNOFF" }, {ACT_VM_PUMP, "ACT_VM_PUMP" }, // user animations {ACT_VM_PUMP_EMPTY, "ACT_VM_PUMP_EMPTY" }, {ACT_VM_START_CHARGE, "ACT_VM_START_CHARGE" }, {ACT_VM_CHARGE, "ACT_VM_CHARGE" }, {ACT_VM_OVERLOAD, "ACT_VM_OVERLOAD" }, {ACT_VM_IDLE_EMPTY, "ACT_VM_IDLE_EMPTY" }, {0, NULL }, }; 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; 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 ) { Msg( "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 ) { Msg( "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; } } void FindNeighbor( int starttri, int startv ) { s_trianglevert_t m1, m2; s_trianglevert_t *last, *check; int j, k; // used[starttri] |= (1 << startv); last = &triangles[starttri][0]; m1 = last[(startv+1)%3]; m2 = last[(startv+0)%3]; for (j = starttri + 1, check = &triangles[starttri + 1][0]; j < pmesh->numtris; j++, check += 3) { if (used[j] == 7) continue; for (k = 0; k < 3; k++) { if (memcmp(&check[k],&m1,sizeof(m1))) continue; if (memcmp(&check[ (k+1)%3 ],&m2,sizeof(m2))) continue; neighbortri[starttri][startv] = j; neighboredge[starttri][startv] = k; neighbortri[j][k] = starttri; neighboredge[j][k] = startv; used[starttri] |= (1 << startv); used[j] |= (1 << k); return; } } } int StripLength( int starttri, int startv ) { int j, k; used[starttri] = 2; stripverts[0] = (startv)%3; stripverts[1] = (startv+1)%3; stripverts[2] = (startv+2)%3; striptris[0] = starttri; striptris[1] = starttri; striptris[2] = starttri; stripcount = 3; while( 1 ) { if (stripcount & 1) { j = neighbortri[starttri][(startv+1)%3]; k = neighboredge[starttri][(startv+1)%3]; } else { j = neighbortri[starttri][(startv+2)%3]; k = neighboredge[starttri][(startv+2)%3]; } if (j == -1 || used[j]) goto done; stripverts[stripcount] = (k+2)%3; striptris[stripcount] = j; stripcount++; used[j] = 2; starttri = j; startv = k; } done: // clear the temp used flags for (j = 0; j < pmesh->numtris; j++) if (used[j] == 2) used[j] = 0; return stripcount; } int FanLength (int starttri, int startv) { int j, k; used[starttri] = 2; stripverts[0] = (startv)%3; stripverts[1] = (startv+1)%3; stripverts[2] = (startv+2)%3; striptris[0] = starttri; striptris[1] = starttri; striptris[2] = starttri; stripcount = 3; while( 1 ) { j = neighbortri[starttri][(startv+2)%3]; k = neighboredge[starttri][(startv+2)%3]; if (j == -1 || used[j]) goto done; stripverts[stripcount] = (k+2)%3; striptris[stripcount] = j; stripcount++; used[j] = 2; starttri = j; startv = k; } done: // clear the temp used flags for (j=0 ; jnumtris ; j++) if (used[j] == 2) used[j] = 0; return stripcount; } int BuildTris (s_trianglevert_t (*x)[3], s_mesh_t *y, byte **ppdata ) { int i, j, k, m; int startv; int len, bestlen, besttype; int bestverts[MAXSTUDIOTRIANGLES]; int besttris[MAXSTUDIOTRIANGLES]; int peak[MAXSTUDIOTRIANGLES]; int type; int total = 0; long t; int maxlen; triangles = x; pmesh = y; t = time( NULL ); for (i = 0; i < pmesh->numtris; i++) { neighbortri[i][0] = neighbortri[i][1] = neighbortri[i][2] = -1; used[i] = 0; peak[i] = pmesh->numtris; } for (i = 0; i < pmesh->numtris; i++) { for (k = 0; k < 3; k++) { if (used[i] & (1 << k)) continue; FindNeighbor( i, k ); } } // build tristrips numcommandnodes = 0; numcommands = 0; Mem_Set (used, 0, sizeof(used)); for (i=0 ; inumtris ;) { // pick an unused triangle and start the trifan if (used[i]) { i++; continue; } maxlen = 9999; bestlen = 0; m = 0; for (k = i; k < pmesh->numtris && bestlen < 127; k++) { int localpeak = 0; if (used[k]) continue; if (peak[k] <= bestlen) continue; m++; for (type = 0 ; type < 2 ; type++) { for (startv =0 ; startv < 3 ; startv++) { if (type == 1) len = FanLength (k, startv); else len = StripLength (k, startv); if (len > 127) { // skip these, they are too long to encode } else if (len > bestlen) { besttype = type; bestlen = len; for (j = 0; j < bestlen; j++) { besttris[j] = striptris[j]; bestverts[j] = stripverts[j]; } } if (len > localpeak) localpeak = len; } } peak[k] = localpeak; if (localpeak == maxlen) break; } total += (bestlen - 2); maxlen = bestlen; // mark the tris on the best strip as used for (j = 0; j < bestlen; j++) used[besttris[j]] = 1; if (besttype == 1) commands[numcommands++] = -bestlen; else commands[numcommands++] = bestlen; for (j = 0; j < bestlen; j++) { s_trianglevert_t *tri; tri = &triangles[besttris[j]][bestverts[j]]; commands[numcommands++] = tri->vertindex; commands[numcommands++] = tri->normindex; commands[numcommands++] = tri->s; commands[numcommands++] = tri->t; } numcommandnodes++; if (t != time(NULL)) { Msg("%2d%%\r", (total * 100) / pmesh->numtris ); t = time(NULL); } } commands[numcommands++] = 0;// end of list marker *ppdata = (byte *)commands; return numcommands * sizeof( short ); } void ExtractMotion( void ) { int i, j, k, 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]; 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 ); 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 = 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; 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) { 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] ); } } } } } } int findNode( char *name ) { int k; for (k = 0; k < numbones; k++) { if(!com.strcmp( bonetable[k].name, name )) return k; } return -1; } void MakeTransitions( void ) { 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); } int lookup_texture( char *texturename ) { int i; for( i = 0; i < numtextures; i++ ) { if(!com.stricmp( texture[i]->name, texturename )) return i; } if( i >= MAXSTUDIOSKINS ) Sys_Break( "too many textures in model\n" ); // allocate a new one texture[i] = Mem_Alloc( studiopool, sizeof(s_texture_t)); com.strncpy( texture[i]->name, texturename, sizeof(texture[i]->name)); if(com.stristr( texturename, "chrome" ) != NULL) texture[i]->flags = STUDIO_NF_FLATSHADE | STUDIO_NF_CHROME; else if(com.stristr( texturename, "bright" ) != NULL) texture[i]->flags = STUDIO_NF_FLATSHADE | STUDIO_NF_FULLBRIGHT; 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) Sys_Break( "too many meshes in model: \"%s\"\n", pmodel->name ); pmodel->nummesh = i + 1; pmodel->pmesh[i] = Kalloc( 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; pmesh->triangle = Realloc( pmesh->triangle, 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 (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) Sys_Break( "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) Sys_Break( "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; } 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; } 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; } void clip_rotations( vec3_t rot ) { int j;// clip everything to : -M_PI <= x < M_PI for (j = 0; j < 3; j++) { while (rot[j] >= M_PI) rot[j] -= M_PI*2; while (rot[j] < -M_PI) rot[j] += M_PI*2; } } int lookupActivity( char *szActivity ) { int i; for (i = 0; activity_map[i].name; i++) { if (!com.stricmp( szActivity, activity_map[i].name )) return activity_map[i].type; } // match ACT_# if (!com.strnicmp( szActivity, "ACT_", 4 )) return com.atoi( &szActivity[4] ); return 0; } 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; i < pmesh->numtris; 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; } for (i = 0; i < pmesh->numtris; 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; i < pmesh->numtris; 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) { for (j = 0; j < 3; j++) pmesh->triangle[k][j].u += 1.0; } else if (n_min_u - 1.0 > 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; i < pmesh->numtris ; 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) { for (j = 0; j < 3; j++) pmesh->triangle[k][j].v += 1.0; } else if (n_min_v - 1.0 > min_v) { for (j = 0; j < 3; j++) pmesh->triangle[n][j].v -= 1.0; } else break; } while (1); } else { for (i = 0; i < pmesh->numtris; 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; i < pmesh->numtris ; 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; i < pmesh->numtris; 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; } } 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; pmesh->triangle[i][j].t = (ptexture->max_t - ptexture->min_t) - (pmesh->triangle[i][j].t - ptexture->min_t); } } } void ResizeTexture( s_texture_t *ptexture ) { int i, j, s, t; byte *pdest; int srcadjwidth; float percent; // 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; percent = ((ptexture->skinwidth * ptexture->skinheight) / (float)(ptexture->srcwidth * ptexture->srcheight)) * 100.0f; Msg("BMP %s [%d %d] (%.0f%%) %6s\n", ptexture->name, ptexture->skinwidth, ptexture->skinheight, percent, memprint( ptexture->size )); if( ptexture->size > 1536 * 1536) { Msg("%g %g %g %g\n", ptexture->min_s, ptexture->max_s, ptexture->min_t, ptexture->max_t ); Sys_Break("texture too large\n"); } pdest = Kalloc( 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 Mem_Copy( pdest, ptexture->ppal, 256 * sizeof( rgb_t )); Mem_Free( ptexture->ppicture ); Mem_Free( ptexture->ppal ); }