Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

432 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. //
  9. // studiomdl.c: generates a studio .mdl file from a .qc script
  10. // models/<scriptname>.mdl.
  11. //
  12. #pragma warning( disable : 4244 )
  13. #pragma warning( disable : 4237 )
  14. #pragma warning( disable : 4305 )
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <sys/stat.h>
  18. #include <math.h>
  19. #include "tier1/utlbuffer.h"
  20. #include "cmdlib.h"
  21. #include "scriplib.h"
  22. #include "mathlib/mathlib.h"
  23. #include "studio.h"
  24. #include "tier1/characterset.h"
  25. #include "studiomdl.h"
  26. //#include "..\..\dlls\activity.h"
  27. bool IsEnd( char const* pLine );
  28. int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] );
  29. int AddToVlist( int v, int m, int n, int t, int firstref );
  30. void DecrementReferenceVlist( int uv, int numverts );
  31. int faceCompare( const void *elem1, const void *elem2 );
  32. void UnifyIndices( s_source_t *psource );
  33. struct MtlInfo_t
  34. {
  35. CUtlString m_MtlName;
  36. CUtlString m_TgaName;
  37. };
  38. static CUtlVector<MtlInfo_t> g_MtlLib;
  39. void ParseMtlLib( CUtlBuffer &buf )
  40. {
  41. int nCurrentMtl = -1;
  42. while ( buf.IsValid() )
  43. {
  44. buf.GetLine( g_szLine, sizeof(g_szLine) );
  45. if ( !Q_strnicmp( g_szLine, "newmtl ", 7 ) )
  46. {
  47. char mtlName[1024];
  48. if ( sscanf( g_szLine, "newmtl %s", mtlName ) == 1 )
  49. {
  50. nCurrentMtl = g_MtlLib.AddToTail( );
  51. g_MtlLib[nCurrentMtl].m_MtlName = mtlName;
  52. g_MtlLib[nCurrentMtl].m_TgaName = "debugempty";
  53. }
  54. continue;
  55. }
  56. if ( !Q_strnicmp( g_szLine, "map_Kd ", 7 ) )
  57. {
  58. if ( nCurrentMtl < 0 )
  59. continue;
  60. char tgaPath[MAX_PATH];
  61. char tgaName[1024];
  62. if ( sscanf( g_szLine, "map_Kd %s", tgaPath ) == 1 )
  63. {
  64. Q_FileBase( tgaPath, tgaName, sizeof(tgaName) );
  65. g_MtlLib[nCurrentMtl].m_TgaName = tgaName;
  66. }
  67. continue;
  68. }
  69. }
  70. }
  71. const char *FindMtlEntry( const char *pTgaName )
  72. {
  73. int nCount = g_MtlLib.Count();
  74. for ( int i = 0; i < nCount; ++i )
  75. {
  76. if ( !Q_stricmp( g_MtlLib[i].m_MtlName, pTgaName ) )
  77. return g_MtlLib[i].m_TgaName;
  78. }
  79. return pTgaName;
  80. }
  81. static bool ParseVertex( CUtlBuffer& bufParse, characterset_t &breakSet, int &v, int &t, int &n )
  82. {
  83. char cmd[1024];
  84. int nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  85. if ( nLen <= 0 )
  86. return false;
  87. v = atoi( cmd );
  88. n = 0;
  89. t = 0;
  90. char c = *(char*)bufParse.PeekGet();
  91. bool bHasTexCoord = IN_CHARACTERSET( breakSet, c ) != 0;
  92. bool bHasNormal = false;
  93. if ( bHasTexCoord )
  94. {
  95. // Snag the '/'
  96. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  97. Assert( nLen == 1 );
  98. c = *(char*)bufParse.PeekGet();
  99. if ( !IN_CHARACTERSET( breakSet, c ) )
  100. {
  101. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  102. Assert( nLen > 0 );
  103. t = atoi( cmd );
  104. c = *(char*)bufParse.PeekGet();
  105. bHasNormal = IN_CHARACTERSET( breakSet, c ) != 0;
  106. }
  107. else
  108. {
  109. bHasNormal = true;
  110. bHasTexCoord = false;
  111. }
  112. if ( bHasNormal )
  113. {
  114. // Snag the '/'
  115. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  116. Assert( nLen == 1 );
  117. nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
  118. Assert( nLen > 0 );
  119. n = atoi( cmd );
  120. }
  121. }
  122. return true;
  123. }
  124. int Load_OBJ( s_source_t *psource )
  125. {
  126. char cmd[1024];
  127. int i;
  128. int material = -1;
  129. g_MtlLib.RemoveAll();
  130. if ( !OpenGlobalFile( psource->filename ) )
  131. return 0;
  132. char pFullPath[MAX_PATH];
  133. if ( !GetGlobalFilePath( psource->filename, pFullPath, sizeof(pFullPath) ) )
  134. return 0;
  135. char pFullDir[MAX_PATH];
  136. Q_ExtractFilePath( pFullPath, pFullDir, sizeof(pFullDir) );
  137. if( !g_quiet )
  138. {
  139. printf( "grabbing %s\n", psource->filename );
  140. }
  141. g_iLinecount = 0;
  142. psource->numbones = 1;
  143. strcpy( psource->localBone[0].name, "default" );
  144. psource->localBone[0].parent = -1;
  145. Assert( psource->m_Animations.Count() == 0 );
  146. s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( psource, "BindPose" );
  147. pSourceAnim->numframes = 1;
  148. pSourceAnim->startframe = 0;
  149. pSourceAnim->endframe = 0;
  150. pSourceAnim->rawanim[0] = (s_bone_t *)kalloc( 1, sizeof( s_bone_t ) );
  151. pSourceAnim->rawanim[0][0].pos.Init();
  152. pSourceAnim->rawanim[0][0].rot.Init();
  153. Build_Reference( psource, "BindPose" );
  154. characterset_t breakSet;
  155. CharacterSetBuild( &breakSet, "/\\" );
  156. while ( GetLineInput() )
  157. {
  158. Vector tmp;
  159. if ( strncmp( g_szLine, "v ", 2 ) == 0 )
  160. {
  161. i = g_numverts++;
  162. sscanf( g_szLine, "v %f %f %f", &g_vertex[i].x, &g_vertex[i].y, &g_vertex[i].z );
  163. g_bone[i].numbones = 1;
  164. g_bone[i].bone[0] = 0;
  165. g_bone[i].weight[0] = 1.0;
  166. continue;
  167. }
  168. if (strncmp( g_szLine, "vn ", 3 ) == 0)
  169. {
  170. i = g_numnormals++;
  171. sscanf( g_szLine, "vn %f %f %f", &g_normal[i].x, &g_normal[i].y, &g_normal[i].z );
  172. continue;
  173. }
  174. if (strncmp( g_szLine, "vt ", 3 ) == 0)
  175. {
  176. i = g_numtexcoords++;
  177. sscanf( g_szLine, "vt %f %f", &g_texcoord[i].x, &g_texcoord[i].y );
  178. g_texcoord[i].y = 1.0 - g_texcoord[i].y;
  179. continue;
  180. }
  181. if ( !Q_strncmp( g_szLine, "mtllib ", 7 ) )
  182. {
  183. sscanf( g_szLine, "mtllib %s", &cmd[0] );
  184. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  185. char pFullMtlLibPath[MAX_PATH];
  186. Q_ComposeFileName( pFullDir, cmd, pFullMtlLibPath, sizeof(pFullMtlLibPath) );
  187. if ( g_pFullFileSystem->ReadFile( pFullMtlLibPath, NULL, buf ) )
  188. {
  189. ParseMtlLib( buf );
  190. }
  191. continue;
  192. }
  193. if (strncmp( g_szLine, "usemtl ", 7 ) == 0)
  194. {
  195. sscanf( g_szLine, "usemtl %s", &cmd[0] );
  196. const char *pTexture = FindMtlEntry( cmd );
  197. int texture = LookupTexture( pTexture );
  198. psource->texmap[texture] = texture; // hack, make it 1:1
  199. material = UseTextureAsMaterial( texture );
  200. continue;
  201. }
  202. if (strncmp( g_szLine, "f ", 2 ) == 0)
  203. {
  204. if ( material < 0 )
  205. {
  206. int texture = LookupTexture( "debugempty.tga" );
  207. psource->texmap[texture] = texture;
  208. material = UseTextureAsMaterial( texture );
  209. }
  210. int v0, n0, t0;
  211. int v1, n1, t1;
  212. int v2, n2, t2;
  213. s_tmpface_t f;
  214. // Are we specifying p only, p and t only, p and n only, or p and n and t?
  215. char *pData = g_szLine + 2;
  216. int nLen = Q_strlen( pData );
  217. CUtlBuffer bufParse( pData, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  218. ParseVertex( bufParse, breakSet, v0, t0, n0 );
  219. ParseVertex( bufParse, breakSet, v1, t1, n1 );
  220. Assert( v0 <= g_numverts && t0 <= g_numtexcoords && n0 <= g_numnormals );
  221. Assert( v1 <= g_numverts && t1 <= g_numtexcoords && n1 <= g_numnormals );
  222. while ( bufParse.IsValid() )
  223. {
  224. if ( !ParseVertex( bufParse, breakSet, v2, t2, n2 ) )
  225. break;
  226. Assert( v2 <= g_numverts && t2 <= g_numtexcoords && n2 <= g_numnormals );
  227. i = g_numfaces++;
  228. f.material = material;
  229. f.a = v0 - 1; f.na = (n0 > 0) ? n0 - 1 : 0, f.ta = (t0 > 0) ? t0 - 1 : 0;
  230. f.b = v2 - 1; f.nb = (n2 > 0) ? n2 - 1 : 0, f.tb = (t2 > 0) ? t2 - 1 : 0;
  231. f.c = v1 - 1; f.nc = (n1 > 0) ? n1 - 1 : 0, f.tc = (t1 > 0) ? t1 - 1 : 0;
  232. g_face[i] = f;
  233. v1 = v2; t1 = t2; n1 = n2;
  234. }
  235. continue;
  236. }
  237. }
  238. UnifyIndices( psource );
  239. BuildIndividualMeshes( psource );
  240. fclose( g_fpInput );
  241. return 1;
  242. }
  243. int AppendVTAtoOBJ( s_source_t *psource, char *filename, int frame )
  244. {
  245. char cmd[1024];
  246. int i, j;
  247. int material = 0;
  248. Vector tmp;
  249. matrix3x4_t m;
  250. AngleMatrix( RadianEuler( 1.570796, 0, 0 ), m );
  251. if ( !OpenGlobalFile( filename ) )
  252. return 0;
  253. if( !g_quiet )
  254. {
  255. printf ("grabbing %s\n", filename );
  256. }
  257. g_iLinecount = 0;
  258. g_numverts = g_numnormals = g_numtexcoords = g_numfaces = 0;
  259. while ( GetLineInput() )
  260. {
  261. Vector tmp;
  262. if (strncmp( g_szLine, "v ", 2 ) == 0)
  263. {
  264. i = g_numverts++;
  265. sscanf( g_szLine, "v %f %f %f", &tmp.x, &tmp.y, &tmp.z );
  266. VectorTransform( tmp, m, g_vertex[i] );
  267. // printf("%f %f %f\n", g_vertex[i].x, g_vertex[i].y, g_vertex[i].z );
  268. g_bone[i].numbones = 1;
  269. g_bone[i].bone[0] = 0;
  270. g_bone[i].weight[0] = 1.0;
  271. }
  272. else if (strncmp( g_szLine, "vn ", 3 ) == 0)
  273. {
  274. i = g_numnormals++;
  275. sscanf( g_szLine, "vn %f %f %f", &tmp.x, &tmp.y, &tmp.z );
  276. VectorRotate( tmp, m, g_normal[i] );
  277. }
  278. else if (strncmp( g_szLine, "vt ", 3 ) == 0)
  279. {
  280. i = g_numtexcoords++;
  281. sscanf( g_szLine, "vt %f %f", &g_texcoord[i].x, &g_texcoord[i].y );
  282. }
  283. else if (strncmp( g_szLine, "usemtl ", 7 ) == 0)
  284. {
  285. sscanf( g_szLine, "usemtl %s", &cmd[0] );
  286. int texture = LookupTexture( cmd );
  287. psource->texmap[texture] = texture; // hack, make it 1:1
  288. material = UseTextureAsMaterial( texture );
  289. }
  290. else if (strncmp( g_szLine, "f ", 2 ) == 0)
  291. {
  292. int v0, n0, t0;
  293. int v1, n1, t1;
  294. int v2, n2, t2;
  295. int v3, n3, t3;
  296. s_tmpface_t f;
  297. i = g_numfaces++;
  298. j = sscanf( g_szLine, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d", &v0, &t0, &n0, &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3 );
  299. f.material = material;
  300. f.a = v0 - 1; f.na = n0 - 1, f.ta = 0;
  301. f.b = v2 - 1; f.nb = n2 - 1, f.tb = 0;
  302. f.c = v1 - 1; f.nc = n1 - 1, f.tc = 0;
  303. Assert( v0 <= g_numverts && v1 <= g_numverts && v2 <= g_numverts );
  304. Assert( n0 <= g_numnormals && n1 <= g_numnormals && n2 <= g_numnormals );
  305. g_face[i] = f;
  306. if (j == 12)
  307. {
  308. i = g_numfaces++;
  309. f.a = v0 - 1; f.na = n0 - 1, f.ta = 0;
  310. f.b = v3 - 1; f.nb = n3 - 1, f.tb = 0;
  311. f.c = v2 - 1; f.nc = n2 - 1, f.tc = 0;
  312. g_face[i] = f;
  313. }
  314. }
  315. }
  316. UnifyIndices( psource );
  317. s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( psource, "BindPose" );
  318. if ( frame == 0 )
  319. {
  320. psource->numbones = 1;
  321. strcpy( psource->localBone[0].name, "default" );
  322. psource->localBone[0].parent = -1;
  323. pSourceAnim->numframes = 1;
  324. pSourceAnim->startframe = 0;
  325. pSourceAnim->endframe = 0;
  326. pSourceAnim->rawanim[0] = (s_bone_t *)kalloc( 1, sizeof( s_bone_t ) );
  327. pSourceAnim->rawanim[0][0].pos.Init();
  328. pSourceAnim->rawanim[0][0].rot = RadianEuler( 1.570796, 0.0, 0.0 );
  329. Build_Reference( psource, "BindPose" );
  330. BuildIndividualMeshes( psource );
  331. }
  332. // printf("%d %d : %d\n", g_numverts, g_numnormals, numvlist );
  333. int t = frame;
  334. int count = numvlist;
  335. pSourceAnim->numvanims[t] = count;
  336. pSourceAnim->vanim[t] = (s_vertanim_t *)kalloc( count, sizeof( s_vertanim_t ) );
  337. for (i = 0; i < count; i++)
  338. {
  339. pSourceAnim->vanim[t][i].vertex = i;
  340. pSourceAnim->vanim[t][i].pos = g_vertex[v_listdata[i].v];
  341. pSourceAnim->vanim[t][i].normal = g_normal[v_listdata[i].n];
  342. }
  343. fclose( g_fpInput );
  344. return 1;
  345. }