diff options
Diffstat (limited to 'utils/studiomdl/mrmsupport.cpp')
| -rw-r--r-- | utils/studiomdl/mrmsupport.cpp | 882 |
1 files changed, 882 insertions, 0 deletions
diff --git a/utils/studiomdl/mrmsupport.cpp b/utils/studiomdl/mrmsupport.cpp new file mode 100644 index 0000000..60cfcb1 --- /dev/null +++ b/utils/studiomdl/mrmsupport.cpp @@ -0,0 +1,882 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + + +// +// studiomdl.c: generates a studio .mdl file from a .qc script +// models/<scriptname>.mdl. +// + + +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4237 ) +#pragma warning( disable : 4305 ) + + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <math.h> + +#include "cmdlib.h" +#include "scriplib.h" +#include "mathlib/mathlib.h" +#include "studio.h" +#include "studiomdl.h" +//#include "..\..\dlls\activity.h" + +bool IsEnd( char const* pLine ) +{ + if (strncmp( "end", pLine, 3 ) != 0) + return false; + return (pLine[3] == '\0') || (pLine[3] == '\n'); +} + + +int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] ) +{ + int i; + + // collapse duplicate bone weights + for (i = 0; i < iCount-1; i++) + { + int j; + for (j = i + 1; j < iCount; j++) + { + if (bones[i] == bones[j]) + { + weights[i] += weights[j]; + weights[j] = 0.0; + } + } + } + + // do sleazy bubble sort + int bShouldSort; + do { + bShouldSort = false; + for (i = 0; i < iCount-1; i++) + { + if (weights[i+1] > weights[i]) + { + int j = bones[i+1]; bones[i+1] = bones[i]; bones[i] = j; + float w = weights[i+1]; weights[i+1] = weights[i]; weights[i] = w; + bShouldSort = true; + } + } + } while (bShouldSort); + + // throw away all weights less than 1/20th + while (iCount > 1 && weights[iCount-1] < 0.05) + { + iCount--; + } + + // clip to the top iMaxCount bones + if (iCount > iMaxCount) + { + iCount = iMaxCount; + } + + float t = 0; + for (i = 0; i < iCount; i++) + { + t += weights[i]; + } + + if (t <= 0.0) + { + // missing weights?, go ahead and evenly share? + // FIXME: shouldn't this error out? + t = 1.0 / iCount; + + for (i = 0; i < iCount; i++) + { + weights[i] = t; + } + } + else + { + // scale to sum to 1.0 + t = 1.0 / t; + + for (i = 0; i < iCount; i++) + { + weights[i] = weights[i] * t; + } + } + + return iCount; +} + + + +void Grab_Vertexlist( s_source_t *psource ) +{ + while (1) + { + if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + int j; + int bone; + Vector p; + int iCount, bones[4]; + float weights[4]; + + g_iLinecount++; + + // check for end + if (IsEnd(g_szLine)) + return; + + + int i = sscanf( g_szLine, "%d %d %f %f %f %d %d %f %d %f %d %f %d %f", + &j, + &bone, + &p[0], &p[1], &p[2], + &iCount, + &bones[0], &weights[0], &bones[1], &weights[1], &bones[2], &weights[2], &bones[3], &weights[3] ); + + if (i == 5) + { + if (bone < 0 || bone >= psource->numbones) + { + MdlWarning( "bogus bone index\n" ); + MdlWarning( "%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine ); + MdlError( "Exiting due to errors\n" ); + } + + VectorCopy( p, g_vertex[j] ); + g_bone[j].numbones = 1; + g_bone[j].bone[0] = bone; + g_bone[j].weight[0] = 1.0; + } + else if (i > 5) + { + iCount = SortAndBalanceBones( iCount, MAXSTUDIOBONEWEIGHTS, bones, weights ); + + VectorCopy( p, g_vertex[j] ); + g_bone[j].numbones = iCount; + for (i = 0; i < iCount; i++) + { + g_bone[j].bone[i] = bones[i]; + g_bone[j].weight[i] = weights[i]; + } + } + else + { + MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine ); + } + } + } +} + + + +void Grab_Facelist( s_source_t *psource ) +{ + while (1) + { + if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + int j; + s_tmpface_t f; + + g_iLinecount++; + + // check for end + if (IsEnd(g_szLine)) + return; + + if (sscanf( g_szLine, "%d %d %d %d", + &j, + &f.a, &f.b, &f.c) == 4) + { + g_face[j] = f; + } + else + { + MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine ); + } + } + } +} + + + +void Grab_Materiallist( s_source_t *psource ) +{ + while (1) + { + if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + // char name[256]; + char path[MAX_PATH]; + rgb2_t a, d, s; + float g; + int j; + + g_iLinecount++; + + // check for end + if (IsEnd(g_szLine)) + return; + + if (sscanf( g_szLine, "%d %f %f %f %f %f %f %f %f %f %f %f %f %f \"%[^\"]s", + &j, + &a.r, &a.g, &a.b, &a.a, + &d.r, &d.g, &d.b, &d.a, + &s.r, &s.g, &s.b, &s.a, + &g, + path ) == 15) + { + if (path[0] == '\0') + { + psource->texmap[j] = -1; + } + else if (j < ARRAYSIZE(psource->texmap)) + { + psource->texmap[j] = LookupTexture( path ); + } + else + { + MdlError( "Too many materials, max %d\n", ARRAYSIZE(psource->texmap) ); + } + } + } + } +} + + +void Grab_Texcoordlist( s_source_t *psource ) +{ + while (1) + { + if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + int j; + Vector2D t; + + g_iLinecount++; + + // check for end + if (IsEnd(g_szLine)) + return; + + if (sscanf( g_szLine, "%d %f %f", + &j, + &t[0], &t[1]) == 3) + { + t[1] = 1.0 - t[1]; + g_texcoord[j][0] = t[0]; + g_texcoord[j][1] = t[1]; + } + else + { + MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine ); + } + } + } +} + + +void Grab_Normallist( s_source_t *psource ) +{ + while (1) + { + if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + int j; + int bone; + Vector n; + + g_iLinecount++; + + // check for end + if (IsEnd(g_szLine)) + return; + + + if (sscanf( g_szLine, "%d %d %f %f %f", + &j, + &bone, + &n[0], &n[1], &n[2]) == 5) + { + if (bone < 0 || bone >= psource->numbones) + { + MdlWarning( "bogus bone index\n" ); + MdlWarning( "%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine ); + MdlError( "Exiting due to errors\n" ); + } + + VectorCopy( n, g_normal[j] ); + } + else + { + MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine ); + } + } + } +} + + + +void Grab_Faceattriblist( s_source_t *psource ) +{ + while (1) + { + if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + int j; + int smooth; + int material; + s_tmpface_t f; + unsigned short s; + + g_iLinecount++; + + // check for end + if (IsEnd(g_szLine)) + return; + + if (sscanf( g_szLine, "%d %d %d %d %d %d %d %d %d", + &j, + &material, + &smooth, + &f.ta, &f.tb, &f.tc, + &f.na, &f.nb, &f.nc) == 9) + { + f.a = g_face[j].a; + f.b = g_face[j].b; + f.c = g_face[j].c; + + f.material = UseTextureAsMaterial( psource->texmap[material] ); + if (f.material < 0) + { + MdlError( "face %d references NULL texture %d\n", j, material ); + } + + if (1) + { + s = f.b; f.b = f.c; f.c = s; + s = f.tb; f.tb = f.tc; f.tc = s; + s = f.nb; f.nb = f.nc; f.nc = s; + } + + g_face[j] = f; + } + else + { + MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine ); + } + } + } +} + + +int closestNormal( int v, int n ) +{ + float maxdot = -1.0; + float dot; + int r = n; + + v_unify_t *cur = v_list[v]; + + while (cur) + { + dot = DotProduct( g_normal[cur->n], g_normal[n] ); + if (dot > maxdot) + { + r = cur->n; + maxdot = dot; + } + cur = cur->next; + } + + return r; +} + + +int AddToVlist( int v, int m, int n, int t, int firstref ) +{ + v_unify_t *prev = NULL; + v_unify_t *cur = v_list[v]; + + while (cur) + { + if (cur->m == m && cur->n == n && cur->t == t) + { + cur->refcount++; + return cur - v_listdata; + } + prev = cur; + cur = cur->next; + } + + if (numvlist >= MAXSTUDIOVERTS) + { + MdlError( "Too many unified vertices\n"); + } + + cur = &v_listdata[numvlist++]; + cur->lastref = -1; + cur->refcount = 1; + cur->firstref = firstref; + cur->v = v; + cur->m = m; + cur->n = n; + cur->t = t; + + if (prev) + { + prev->next = cur; + } + else + { + v_list[v] = cur; + } + + return numvlist - 1; +} + +void DecrementReferenceVlist( int uv, int numverts ) +{ + if (uv < 0 || uv >= MAXSTUDIOVERTS) + MdlError( "decrement outside of range\n"); + + v_listdata[uv].refcount--; + + if (v_listdata[uv].refcount == 0) + { + v_listdata[uv].lastref = numverts; + } + else if (v_listdata[uv].refcount < 0) + { + MdlError("<0 ref\n"); + } +} + + +void UnifyIndices( s_source_t *psource ) +{ + int i; + + static s_tmpface_t tmpface[MAXSTUDIOTRIANGLES]; // mrm processed g_face + static s_face_t uface[MAXSTUDIOTRIANGLES]; // mrm processed unified face + + // clear v_list + numvlist = 0; + memset( v_list, 0, sizeof( v_list ) ); + memset( v_listdata, 0, sizeof( v_listdata ) ); + + // create an list of all the + for (i = 0; i < g_numfaces; i++) + { + tmpface[i] = g_face[i]; + + uface[i].a = AddToVlist( g_face[i].a, g_face[i].material, g_face[i].na, g_face[i].ta, g_numverts ); + uface[i].b = AddToVlist( g_face[i].b, g_face[i].material, g_face[i].nb, g_face[i].tb, g_numverts ); + uface[i].c = AddToVlist( g_face[i].c, g_face[i].material, g_face[i].nc, g_face[i].tc, g_numverts ); + + // keep an original copy + g_src_uface[i] = uface[i]; + } + + // printf("%d : %d %d %d\n", numvlist, g_numverts, g_numnormals, g_numtexcoords ); +} + +void CalcModelTangentSpaces( s_source_t *pSrc ); + + +//----------------------------------------------------------------------------- +// Builds a list of unique vertices in a source +//----------------------------------------------------------------------------- +static void BuildUniqueVertexList( s_source_t *pSource, const int *pDesiredToVList ) +{ + // allocate memory + pSource->vertex = (s_vertexinfo_t *)kalloc( pSource->numvertices, sizeof( s_vertexinfo_t ) ); + + // create arrays of unique vertexes, normals, texcoords. + for (int i = 0; i < pSource->numvertices; i++) + { + int j = pDesiredToVList[i]; + + s_vertexinfo_t &vertex = pSource->vertex[i]; + VectorCopy( g_vertex[ v_listdata[j].v ], vertex.position ); + VectorCopy( g_normal[ v_listdata[j].n ], vertex.normal ); + Vector2Copy( g_texcoord[ v_listdata[j].t ], vertex.texcoord ); + + vertex.boneweight.numbones = g_bone[ v_listdata[j].v ].numbones; + int k; + for( k = 0; k < MAXSTUDIOBONEWEIGHTS; k++ ) + { + vertex.boneweight.bone[k] = g_bone[ v_listdata[j].v ].bone[k]; + vertex.boneweight.weight[k] = g_bone[ v_listdata[j].v ].weight[k]; + } + + // store a bunch of other info + vertex.material = v_listdata[j].m; + +#if 0 + pSource->vertexInfo[i].firstref = v_listdata[j].firstref; + pSource->vertexInfo[i].lastref = v_listdata[j].lastref; +#endif + // printf("%4d : %2d : %6.2f %6.2f %6.2f\n", i, psource->boneweight[i].bone[0], psource->vertex[i][0], psource->vertex[i][1], psource->vertex[i][2] ); + } + +} + + +//----------------------------------------------------------------------------- +// sort new vertices by materials, last used +//----------------------------------------------------------------------------- +static int vlistCompare( const void *elem1, const void *elem2 ) +{ + v_unify_t *u1 = &v_listdata[*(int *)elem1]; + v_unify_t *u2 = &v_listdata[*(int *)elem2]; + + // sort by material + if (u1->m < u2->m) + return -1; + if (u1->m > u2->m) + return 1; + + // sort by last used + if (u1->lastref < u2->lastref) + return -1; + if (u1->lastref > u2->lastref) + return 1; + + return 0; +} + +static void SortVerticesByMaterial( int *pDesiredToVList, int *pVListToDesired ) +{ + for ( int i = 0; i < numvlist; i++ ) + { + pDesiredToVList[i] = i; + } + qsort( pDesiredToVList, numvlist, sizeof( int ), vlistCompare ); + for ( int i = 0; i < numvlist; i++ ) + { + pVListToDesired[ pDesiredToVList[i] ] = i; + } +} + + +//----------------------------------------------------------------------------- +// sort new faces by materials, last used +//----------------------------------------------------------------------------- +static int faceCompare( const void *elem1, const void *elem2 ) +{ + int i1 = *(int *)elem1; + int i2 = *(int *)elem2; + + // sort by material + if (g_face[i1].material < g_face[i2].material) + return -1; + if (g_face[i1].material > g_face[i2].material) + return 1; + + // sort by original usage + if (i1 < i2) + return -1; + if (i1 > i2) + return 1; + + return 0; +} + +static void SortFacesByMaterial( int *pDesiredToSrcFace ) +{ + // NOTE: Unlike SortVerticesByMaterial, srcFaceToDesired isn't needed, so we're not computing it + for ( int i = 0; i < g_numfaces; i++ ) + { + pDesiredToSrcFace[i] = i; + } + qsort( pDesiredToSrcFace, g_numfaces, sizeof( int ), faceCompare ); +} + + +//----------------------------------------------------------------------------- +// Builds mesh structures in the source +//----------------------------------------------------------------------------- +static void PointMeshesToVertexAndFaceData( s_source_t *pSource, int *pDesiredToSrcFace ) +{ + // First, assign all meshes to be empty + // A mesh is a set of faces + vertices that all use 1 material + for ( int m = 0; m < MAXSTUDIOSKINS; m++ ) + { + pSource->mesh[m].numvertices = 0; + pSource->mesh[m].vertexoffset = pSource->numvertices; + + pSource->mesh[m].numfaces = 0; + pSource->mesh[m].faceoffset = pSource->numfaces; + } + + // find first and count of vertices per material + for ( int i = 0; i < pSource->numvertices; i++ ) + { + int m = pSource->vertex[i].material; + pSource->mesh[m].numvertices++; + if (pSource->mesh[m].vertexoffset > i) + { + pSource->mesh[m].vertexoffset = i; + } + } + + // find first and count of faces per material + for ( int i = 0; i < pSource->numfaces; i++ ) + { + int m = g_face[ pDesiredToSrcFace[i] ].material; + + pSource->mesh[m].numfaces++; + if (pSource->mesh[m].faceoffset > i) + { + pSource->mesh[m].faceoffset = i; + } + } + + /* + for (k = 0; k < MAXSTUDIOSKINS; k++) + { + printf("%d : %d:%d %d:%d\n", k, psource->mesh[k].numvertices, psource->mesh[k].vertexoffset, psource->mesh[k].numfaces, psource->mesh[k].faceoffset ); + } + */ +} + + +//----------------------------------------------------------------------------- +// Builds the face list in the mesh +//----------------------------------------------------------------------------- +static void BuildFaceList( s_source_t *pSource, int *pVListToDesired, int *pDesiredToSrcFace ) +{ + pSource->face = (s_face_t *)kalloc( pSource->numfaces, sizeof( s_face_t )); + for ( int m = 0; m < MAXSTUDIOSKINS; m++) + { + if ( !pSource->mesh[m].numfaces ) + continue; + + pSource->meshindex[ pSource->nummeshes++ ] = m; + + for ( int i = pSource->mesh[m].faceoffset; i < pSource->mesh[m].numfaces + pSource->mesh[m].faceoffset; i++) + { + int j = pDesiredToSrcFace[i]; + + // NOTE: per-face vertex indices a,b,c are mesh relative (hence the subtraction), + // while g_src_uface are model relative + pSource->face[i].a = pVListToDesired[ g_src_uface[j].a ] - pSource->mesh[m].vertexoffset; + pSource->face[i].b = pVListToDesired[ g_src_uface[j].b ] - pSource->mesh[m].vertexoffset; + pSource->face[i].c = pVListToDesired[ g_src_uface[j].c ] - pSource->mesh[m].vertexoffset; + Assert( ((pSource->face[i].a & 0xF0000000) == 0) && ((pSource->face[i].b & 0xF0000000) == 0) && + ((pSource->face[i].c & 0xF0000000) == 0) ); + // printf("%3d : %4d %4d %4d\n", i, pSource->face[i].a, pSource->face[i].b, pSource->face[i].c ); + } + } +} + + +//----------------------------------------------------------------------------- +// Remaps the vertex animations based on the new vertex ordering +//----------------------------------------------------------------------------- +static void RemapVertexAnimations( s_source_t *pSource, int *pVListToDesired ) +{ + int nAnimationCount = pSource->m_Animations.Count(); + for ( int i = 0; i < nAnimationCount; ++i ) + { + s_sourceanim_t &anim = pSource->m_Animations[i]; + if ( !anim.newStyleVertexAnimations ) + continue; + + for ( int j = 0; j < MAXSTUDIOANIMFRAMES; ++j ) + { + int nVAnimCount = anim.numvanims[j]; + if ( nVAnimCount == 0 ) + continue; + + // Copy off the initial vertex data + // Have to do it in 2 loops because it'll overwrite itself if we do it in 1 + int *pTemp = (int*)_alloca( nVAnimCount * sizeof(int) ); + for ( int k = 0; k < nVAnimCount; ++k ) + { + pTemp[k] = anim.vanim[j][k].vertex; + } + + for ( int k = 0; k < nVAnimCount; ++k ) + { + // NOTE: vertex animations are model relative, not mesh relative + anim.vanim[j][k].vertex = pVListToDesired[ pTemp[k] ]; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Sorts vertices by material type, re-maps data structures that refer to those vertices +// to use the new indices +//----------------------------------------------------------------------------- +void BuildIndividualMeshes( s_source_t *pSource ) +{ + static int v_listsort[MAXSTUDIOVERTS]; // map desired order to vlist entry + static int v_ilistsort[MAXSTUDIOVERTS]; // map vlist entry to desired order + static int facesort[MAXSTUDIOTRIANGLES]; // map desired order to src_face entry + + SortVerticesByMaterial( v_listsort, v_ilistsort ); + SortFacesByMaterial( facesort ); + + pSource->numvertices = numvlist; + pSource->numfaces = g_numfaces; + + BuildUniqueVertexList( pSource, v_listsort ); + PointMeshesToVertexAndFaceData( pSource, facesort ); + BuildFaceList( pSource, v_ilistsort, facesort ); + RemapVertexAnimations( pSource, v_ilistsort ); + CalcModelTangentSpaces( pSource ); +} + + +void Grab_MRMFaceupdates( s_source_t *psource ) +{ + while (1) + { + if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + g_iLinecount++; + + // check for end + if (IsEnd(g_szLine)) + return; + } + } +} + +int Load_VRM ( s_source_t *psource ) +{ + char cmd[1024]; + int option; + + if (!OpenGlobalFile( psource->filename )) + { + return 0; + } + + if( !g_quiet ) + { + printf ("grabbing %s\n", psource->filename); + } + + g_iLinecount = 0; + + while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) + { + g_iLinecount++; + sscanf( g_szLine, "%1023s %d", cmd, &option ); + if (stricmp( cmd, "version" ) == 0) + { + if (option != 2) + { + MdlError("bad version\n"); + } + } + else if (stricmp( cmd, "name" ) == 0) + { + } + else if (stricmp( cmd, "vertices" ) == 0) + { + g_numverts = option; + } + else if (stricmp( cmd, "faces" ) == 0) + { + g_numfaces = option; + } + else if (stricmp( cmd, "materials" ) == 0) + { + // doesn't matter; + } + else if (stricmp( cmd, "texcoords" ) == 0) + { + g_numtexcoords = option; + if (option == 0) + MdlError( "model has no texture coordinates\n"); + } + else if (stricmp( cmd, "normals" ) == 0) + { + g_numnormals = option; + } + else if (stricmp( cmd, "tristrips" ) == 0) + { + // should be 0; + } + + else if (stricmp( cmd, "vertexlist" ) == 0) + { + Grab_Vertexlist( psource ); + } + else if (stricmp( cmd, "facelist" ) == 0) + { + Grab_Facelist( psource ); + } + else if (stricmp( cmd, "materiallist" ) == 0) + { + Grab_Materiallist( psource ); + } + else if (stricmp( cmd, "texcoordlist" ) == 0) + { + Grab_Texcoordlist( psource ); + } + else if (stricmp( cmd, "normallist" ) == 0) + { + Grab_Normallist( psource ); + } + else if (stricmp( cmd, "faceattriblist" ) == 0) + { + Grab_Faceattriblist( psource ); + } + + else if (stricmp( cmd, "MRM" ) == 0) + { + } + else if (stricmp( cmd, "MRMvertices" ) == 0) + { + } + else if (stricmp( cmd, "MRMfaces" ) == 0) + { + } + else if (stricmp( cmd, "MRMfaceupdates" ) == 0) + { + Grab_MRMFaceupdates( psource ); + } + + else if (stricmp( cmd, "nodes" ) == 0) + { + psource->numbones = Grab_Nodes( psource->localBone ); + } + else if (stricmp( cmd, "skeleton" ) == 0) + { + Grab_Animation( psource, "BindPose" ); + } +/* + else if (stricmp( cmd, "triangles" ) == 0) { + Grab_Triangles( psource ); + } +*/ + else + { + MdlError("unknown VRM command : %s \n", cmd ); + } + } + + UnifyIndices( psource ); + BuildIndividualMeshes( psource ); + + fclose( g_fpInput ); + + return 1; +} + |