/*** * * 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. * ****/ // // write.c: writes a studio .mdl file // #pragma warning( disable : 4244 ) #pragma warning( disable : 4237 ) #pragma warning( disable : 4305 ) #include #include #include #include "cmdlib.h" #include "lbmlib.h" #include "scriplib.h" #include "mathlib.h" #include "..\..\engine\studio.h" #include "studiomdl.h" int totalframes = 0; float totalseconds = 0; extern int numcommandnodes; /* ============ WriteModel ============ */ byte *pData; byte *pStart; studiohdr_t *phdr; studioseqhdr_t *pseqhdr; #define ALIGN( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3) void WriteBoneInfo( ) { int i, j; mstudiobone_t *pbone; mstudiobonecontroller_t *pbonecontroller; mstudioattachment_t *pattachment; mstudiobbox_t *pbbox; // save bone info pbone = (mstudiobone_t *)pData; phdr->numbones = numbones; phdr->boneindex = (pData - pStart); for (i = 0; i < numbones; i++) { strcpy( pbone[i].name, bonetable[i].name ); pbone[i].parent = bonetable[i].parent; pbone[i].flags = bonetable[i].flags; pbone[i].value[0] = bonetable[i].pos[0]; pbone[i].value[1] = bonetable[i].pos[1]; pbone[i].value[2] = bonetable[i].pos[2]; pbone[i].value[3] = bonetable[i].rot[0]; pbone[i].value[4] = bonetable[i].rot[1]; pbone[i].value[5] = bonetable[i].rot[2]; pbone[i].scale[0] = bonetable[i].posscale[0]; pbone[i].scale[1] = bonetable[i].posscale[1]; pbone[i].scale[2] = bonetable[i].posscale[2]; pbone[i].scale[3] = bonetable[i].rotscale[0]; pbone[i].scale[4] = bonetable[i].rotscale[1]; pbone[i].scale[5] = bonetable[i].rotscale[2]; } pData += numbones * sizeof( mstudiobone_t ); ALIGN( pData ); // map bonecontroller to bones for (i = 0; i < numbones; i++) { for (j = 0; j < 6; j++) { pbone[i].bonecontroller[j] = -1; } } for (i = 0; i < numbonecontrollers; i++) { j = bonecontroller[i].bone; switch( bonecontroller[i].type & STUDIO_TYPES ) { case STUDIO_X: pbone[j].bonecontroller[0] = i; break; case STUDIO_Y: pbone[j].bonecontroller[1] = i; break; case STUDIO_Z: pbone[j].bonecontroller[2] = i; break; case STUDIO_XR: pbone[j].bonecontroller[3] = i; break; case STUDIO_YR: pbone[j].bonecontroller[4] = i; break; case STUDIO_ZR: pbone[j].bonecontroller[5] = i; break; default: printf("unknown bonecontroller type\n"); exit(1); } } // save bonecontroller info pbonecontroller = (mstudiobonecontroller_t *)pData; phdr->numbonecontrollers = numbonecontrollers; phdr->bonecontrollerindex = (pData - pStart); for (i = 0; i < numbonecontrollers; i++) { pbonecontroller[i].bone = bonecontroller[i].bone; pbonecontroller[i].index = bonecontroller[i].index; pbonecontroller[i].type = bonecontroller[i].type; pbonecontroller[i].start = bonecontroller[i].start; pbonecontroller[i].end = bonecontroller[i].end; } pData += numbonecontrollers * sizeof( mstudiobonecontroller_t ); ALIGN( pData ); // save attachment info pattachment = (mstudioattachment_t *)pData; phdr->numattachments = numattachments; phdr->attachmentindex = (pData - pStart); for (i = 0; i < numattachments; i++) { pattachment[i].bone = attachment[i].bone; VectorCopy( attachment[i].org, pattachment[i].org ); } pData += numattachments * sizeof( mstudioattachment_t ); ALIGN( pData ); // save bbox info pbbox = (mstudiobbox_t *)pData; phdr->numhitboxes = numhitboxes; phdr->hitboxindex = (pData - pStart); for (i = 0; i < numhitboxes; i++) { pbbox[i].bone = hitbox[i].bone; pbbox[i].group = hitbox[i].group; VectorCopy( hitbox[i].bmin, pbbox[i].bbmin ); VectorCopy( hitbox[i].bmax, pbbox[i].bbmax ); } pData += numhitboxes * sizeof( mstudiobbox_t ); ALIGN( pData ); } void WriteSequenceInfo( ) { int i, j; mstudioseqgroup_t *pseqgroup; mstudioseqdesc_t *pseqdesc; mstudioseqdesc_t *pbaseseqdesc; mstudioevent_t *pevent; mstudiopivot_t *ppivot; byte *ptransition; // save sequence info pseqdesc = (mstudioseqdesc_t *)pData; pbaseseqdesc = pseqdesc; phdr->numseq = numseq; phdr->seqindex = (pData - pStart); pData += numseq * sizeof( mstudioseqdesc_t ); for (i = 0; i < numseq; i++, pseqdesc++) { strcpy( pseqdesc->label, sequence[i].name ); pseqdesc->numframes = sequence[i].numframes; pseqdesc->fps = sequence[i].fps; pseqdesc->flags = sequence[i].flags; pseqdesc->numblends = sequence[i].numblends; pseqdesc->blendtype[0] = sequence[i].blendtype[0]; pseqdesc->blendtype[1] = sequence[i].blendtype[1]; pseqdesc->blendstart[0] = sequence[i].blendstart[0]; pseqdesc->blendend[0] = sequence[i].blendend[0]; pseqdesc->blendstart[1] = sequence[i].blendstart[1]; pseqdesc->blendend[1] = sequence[i].blendend[1]; pseqdesc->motiontype = sequence[i].motiontype; pseqdesc->motionbone = 0; // sequence[i].motionbone; VectorCopy( sequence[i].linearmovement, pseqdesc->linearmovement ); pseqdesc->seqgroup = sequence[i].seqgroup; pseqdesc->animindex = sequence[i].animindex; pseqdesc->activity = sequence[i].activity; pseqdesc->actweight = sequence[i].actweight; VectorCopy( sequence[i].bmin, pseqdesc->bbmin ); VectorCopy( sequence[i].bmax, pseqdesc->bbmax ); pseqdesc->entrynode = sequence[i].entrynode; pseqdesc->exitnode = sequence[i].exitnode; pseqdesc->nodeflags = sequence[i].nodeflags; totalframes += sequence[i].numframes; totalseconds += sequence[i].numframes / sequence[i].fps; // save events pevent = (mstudioevent_t *)pData; pseqdesc->numevents = sequence[i].numevents; pseqdesc->eventindex = (pData - pStart); pData += pseqdesc->numevents * sizeof( mstudioevent_t ); for (j = 0; j < sequence[i].numevents; j++) { pevent[j].frame = sequence[i].event[j].frame - sequence[i].frameoffset; pevent[j].event = sequence[i].event[j].event; memcpy( pevent[j].options, sequence[i].event[j].options, sizeof( pevent[j].options ) ); } ALIGN( pData ); // save pivots ppivot = (mstudiopivot_t *)pData; pseqdesc->numpivots = sequence[i].numpivots; pseqdesc->pivotindex = (pData - pStart); pData += pseqdesc->numpivots * sizeof( mstudiopivot_t ); for (j = 0; j < sequence[i].numpivots; j++) { VectorCopy( sequence[i].pivot[j].org, ppivot[j].org ); ppivot[j].start = sequence[i].pivot[j].start - sequence[i].frameoffset; ppivot[j].end = sequence[i].pivot[j].end - sequence[i].frameoffset; } ALIGN( pData ); } // save sequence group info pseqgroup = (mstudioseqgroup_t *)pData; phdr->numseqgroups = numseqgroups; phdr->seqgroupindex = (pData - pStart); pData += phdr->numseqgroups * sizeof( mstudioseqgroup_t ); ALIGN( pData ); for (i = 0; i < numseqgroups; i++) { strcpy( pseqgroup[i].label, sequencegroup[i].label ); strcpy( pseqgroup[i].name, sequencegroup[i].name ); } // save transition graph ptransition = (byte *)pData; phdr->numtransitions = numxnodes; phdr->transitionindex = (pData - pStart); pData += numxnodes * numxnodes * sizeof( byte ); ALIGN( pData ); for (i = 0; i < numxnodes; i++) { for (j = 0; j < numxnodes; j++) { *ptransition++ = xnode[i][j]; } } } byte *WriteAnimations( byte *pData, byte *pStart, int group ) { int i, j, k; int q, n; mstudioanim_t *panim; mstudioanimvalue_t *panimvalue; // hack for seqgroup 0 // pseqgroup->data = (pData - pStart); for (i = 0; i < numseq; i++) { if (sequence[i].seqgroup == group) { // save animations panim = (mstudioanim_t *)pData; sequence[i].animindex = (pData - pStart); pData += sequence[i].numblends * numbones * sizeof( mstudioanim_t ); ALIGN( pData ); panimvalue = (mstudioanimvalue_t *)pData; for (q = 0; q < sequence[i].numblends; q++) { // save animation value info for (j = 0; j < numbones; j++) { for (k = 0; k < 6; k++) { if (sequence[i].panim[q]->numanim[j][k] == 0) { panim->offset[k] = 0; } else { panim->offset[k] = ((byte *)panimvalue - (byte *)panim); for (n = 0; n < sequence[i].panim[q]->numanim[j][k]; n++) { panimvalue->value = sequence[i].panim[q]->anim[j][k][n].value; panimvalue++; } } } if (((byte *)panimvalue - (byte *)panim) > 65535) Error("sequence \"%s\" is greate than 64K\n", sequence[i].name ); panim++; } } // printf("raw bone data %d : %s\n", (byte *)panimvalue - pData, sequence[i].name); pData = (byte *)panimvalue; ALIGN( pData ); } } return pData; } void WriteTextures( ) { int i, j; mstudiotexture_t *ptexture; short *pref; // save bone info ptexture = (mstudiotexture_t *)pData; phdr->numtextures = numtextures; phdr->textureindex = (pData - pStart); pData += numtextures * sizeof( mstudiotexture_t ); ALIGN( pData ); phdr->skinindex = (pData - pStart); phdr->numskinref = numskinref; phdr->numskinfamilies = numskinfamilies; pref = (short *)pData; for (i = 0; i < phdr->numskinfamilies; i++) { for (j = 0; j < phdr->numskinref; j++) { *pref = skinref[i][j]; pref++; } } pData = (byte *)pref; ALIGN( pData ); phdr->texturedataindex = (pData - pStart); // must be the end of the file! for (i = 0; i < numtextures; i++) { strcpy( ptexture[i].name, texture[i].name ); ptexture[i].flags = texture[i].flags; ptexture[i].width = texture[i].skinwidth; ptexture[i].height = texture[i].skinheight; ptexture[i].index = (pData - pStart); memcpy( pData, texture[i].pdata, texture[i].size ); pData += texture[i].size; } ALIGN( pData ); } void WriteModel( ) { int i, j, k; mstudiobodyparts_t *pbodypart; mstudiomodel_t *pmodel; // vec3_t *bbox; byte *pbone; vec3_t *pvert; vec3_t *pnorm; mstudiomesh_t *pmesh; s_trianglevert_t *psrctri; int cur; int total_tris = 0; int total_strips = 0; pbodypart = (mstudiobodyparts_t *)pData; phdr->numbodyparts = numbodyparts; phdr->bodypartindex = (pData - pStart); pData += numbodyparts * sizeof( mstudiobodyparts_t ); pmodel = (mstudiomodel_t *)pData; pData += nummodels * sizeof( mstudiomodel_t ); for (i = 0, j = 0; i < numbodyparts; i++) { strcpy( pbodypart[i].name, bodypart[i].name ); pbodypart[i].nummodels = bodypart[i].nummodels; pbodypart[i].base = bodypart[i].base; pbodypart[i].modelindex = ((byte *)&pmodel[j]) - pStart; j += bodypart[i].nummodels; } ALIGN( pData ); cur = (int)pData; for (i = 0; i < nummodels; i++) { int normmap[MAXSTUDIOVERTS]; int normimap[MAXSTUDIOVERTS]; int n = 0; strcpy( pmodel[i].name, model[i]->name ); // save bbox info // remap normals to be sorted by skin reference for (j = 0; j < model[i]->nummesh; j++) { for (k = 0; k < model[i]->numnorms; k++) { if (model[i]->normal[k].skinref == model[i]->pmesh[j]->skinref) { normmap[k] = n; normimap[n] = k; n++; model[i]->pmesh[j]->numnorms++; } } } // save vertice bones pbone = pData; pmodel[i].numverts = model[i]->numverts; pmodel[i].vertinfoindex = (pData - pStart); for (j = 0; j < pmodel[i].numverts; j++) { *pbone++ = model[i]->vert[j].bone; } ALIGN( pbone ); // save normal bones pmodel[i].numnorms = model[i]->numnorms; pmodel[i].norminfoindex = ((byte *)pbone - pStart); for (j = 0; j < pmodel[i].numnorms; j++) { *pbone++ = model[i]->normal[normimap[j]].bone; } ALIGN( pbone ); pData = pbone; // save group info pvert = (vec3_t *)pData; pData += model[i]->numverts * sizeof( vec3_t ); pmodel[i].vertindex = ((byte *)pvert - pStart); ALIGN( pData ); pnorm = (vec3_t *)pData; pData += model[i]->numnorms * sizeof( vec3_t ); pmodel[i].normindex = ((byte *)pnorm - pStart); ALIGN( pData ); for (j = 0; j < model[i]->numverts; j++) { VectorCopy( model[i]->vert[j].org, pvert[j] ); } for (j = 0; j < model[i]->numnorms; j++) { VectorCopy( model[i]->normal[normimap[j]].org, pnorm[j] ); } printf("vertices %6d bytes (%d vertices, %d normals)\n", pData - cur, model[i]->numverts, model[i]->numnorms); cur = (int)pData; // save mesh info pmesh = (mstudiomesh_t *)pData; pmodel[i].nummesh = model[i]->nummesh; pmodel[i].meshindex = (pData - pStart); pData += pmodel[i].nummesh * sizeof( mstudiomesh_t ); ALIGN( pData ); total_tris = 0; total_strips = 0; for (j = 0; j < model[i]->nummesh; j++) { int numCmdBytes; byte *pCmdSrc; pmesh[j].numtris = model[i]->pmesh[j]->numtris; pmesh[j].skinref = model[i]->pmesh[j]->skinref; pmesh[j].numnorms = model[i]->pmesh[j]->numnorms; psrctri = (s_trianglevert_t *)(model[i]->pmesh[j]->triangle); for (k = 0; k < pmesh[j].numtris * 3; k++) { psrctri->normindex = normmap[psrctri->normindex]; psrctri++; } numCmdBytes = BuildTris( model[i]->pmesh[j]->triangle, model[i]->pmesh[j], &pCmdSrc ); pmesh[j].triindex = (pData - pStart); memcpy( pData, pCmdSrc, numCmdBytes ); pData += numCmdBytes; ALIGN( pData ); total_tris += pmesh[j].numtris; total_strips += numcommandnodes; } printf("mesh %6d bytes (%d tris, %d strips)\n", pData - cur, total_tris, total_strips); cur = (int)pData; } } #define FILEBUFFER (2 * 1024 * 1024) void WriteFile (void) { FILE *modelouthandle; int total = 0; int i; pStart = kalloc( 1, FILEBUFFER ); StripExtension (outname); for (i = 1; i < numseqgroups; i++) { // write the non-default sequence group data to separate files char groupname[128], localname[128]; sprintf( groupname, "%s%02d.mdl", outname, i ); printf ("writing %s:\n", groupname); modelouthandle = SafeOpenWrite (groupname); pseqhdr = (studioseqhdr_t *)pStart; pseqhdr->id = IDSTUDIOSEQHEADER; pseqhdr->version = STUDIO_VERSION; pData = pStart + sizeof( studioseqhdr_t ); pData = WriteAnimations( pData, pStart, i ); ExtractFileBase( groupname, localname ); sprintf( sequencegroup[i].name, "models\\%s.mdl", localname ); strcpy( pseqhdr->name, sequencegroup[i].name ); pseqhdr->length = pData - pStart; printf("total %6d\n", pseqhdr->length ); SafeWrite( modelouthandle, pStart, pseqhdr->length ); fclose (modelouthandle); memset( pStart, 0, pseqhdr->length ); } if (split_textures) { // write textures out to a separate file char texname[128]; sprintf( texname, "%sT.mdl", outname ); printf ("writing %s:\n", texname); modelouthandle = SafeOpenWrite (texname); phdr = (studiohdr_t *)pStart; phdr->id = IDSTUDIOHEADER; phdr->version = STUDIO_VERSION; pData = (byte *)phdr + sizeof( studiohdr_t ); WriteTextures( ); phdr->length = pData - pStart; printf("textures %6d bytes\n", phdr->length ); SafeWrite( modelouthandle, pStart, phdr->length ); fclose (modelouthandle); memset( pStart, 0, phdr->length ); pData = pStart; } // // write the model output file // strcat (outname, ".mdl"); printf ("---------------------\n"); printf ("writing %s:\n", outname); modelouthandle = SafeOpenWrite (outname); phdr = (studiohdr_t *)pStart; phdr->id = IDSTUDIOHEADER; phdr->version = STUDIO_VERSION; strcpy( phdr->name, outname ); VectorCopy( eyeposition, phdr->eyeposition ); VectorCopy( bbox[0], phdr->min ); VectorCopy( bbox[1], phdr->max ); VectorCopy( cbox[0], phdr->bbmin ); VectorCopy( cbox[1], phdr->bbmax ); phdr->flags = gflags; pData = (byte *)phdr + sizeof( studiohdr_t ); WriteBoneInfo( ); printf("bones %6d bytes (%d)\n", pData - pStart - total, numbones ); total = pData - pStart; pData = WriteAnimations( pData, pStart, 0 ); WriteSequenceInfo( ); printf("sequences %6d bytes (%d frames) [%d:%02d]\n", pData - pStart - total, totalframes, (int)totalseconds / 60, (int)totalseconds % 60 ); total = pData - pStart; WriteModel( ); printf("models %6d bytes\n", pData - pStart - total ); total = pData - pStart; if (!split_textures) { WriteTextures( ); printf("textures %6d bytes\n", pData - pStart - total ); } phdr->length = pData - pStart; printf("total %6d\n", phdr->length ); SafeWrite( modelouthandle, pStart, phdr->length ); fclose (modelouthandle); }