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.

4585 lines
132 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. //
  9. // write.c: writes a studio .mdl file
  10. //
  11. #pragma warning( disable : 4244 )
  12. #pragma warning( disable : 4237 )
  13. #pragma warning( disable : 4305 )
  14. #include <io.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <sys/stat.h>
  18. #include <limits.h>
  19. #include "cmdlib.h"
  20. #include "scriplib.h"
  21. #include "mathlib/mathlib.h"
  22. #include "studio.h"
  23. #include "studiomdl.h"
  24. #include "collisionmodel.h"
  25. #include "optimize.h"
  26. #include "studiobyteswap.h"
  27. #include "byteswap.h"
  28. #include "materialsystem/imaterial.h"
  29. #include "materialsystem/imaterialvar.h"
  30. #include "mdlobjects/dmeboneflexdriver.h"
  31. #include "perfstats.h"
  32. #include "tier1/smartptr.h"
  33. #include "tier2/p4helpers.h"
  34. int totalframes = 0;
  35. float totalseconds = 0;
  36. extern int numcommandnodes;
  37. // WriteFile is the only externally visible function in this file.
  38. // pData points to the current location in an output buffer and pStart is
  39. // the beginning of the buffer.
  40. bool FixupToSortedLODVertexes( studiohdr_t *pStudioHdr );
  41. bool Clamp_RootLOD( studiohdr_t *phdr );
  42. static void WriteAllSwappedFiles( const char *filename );
  43. /*
  44. ============
  45. WriteModel
  46. ============
  47. */
  48. static byte *pData;
  49. static byte *pStart;
  50. static byte *pBlockData;
  51. static byte *pBlockStart;
  52. #undef ALIGN4
  53. #undef ALIGN16
  54. #undef ALIGN32
  55. #define ALIGN4( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3)
  56. #define ALIGN16( a ) a = (byte *)((int)((byte *)a + 15) & ~ 15)
  57. #define ALIGN32( a ) a = (byte *)((int)((byte *)a + 31) & ~ 31)
  58. #define ALIGN64( a ) a = (byte *)((int)((byte *)a + 63) & ~ 63)
  59. #define ALIGN512( a ) a = (byte *)((int)((byte *)a + 511) & ~ 511)
  60. // make sure kalloc aligns to maximum alignment size
  61. #define FILEBUFFER (8 * 1024 * 1024)
  62. void WriteSeqKeyValues( mstudioseqdesc_t *pseqdesc, CUtlVector< char > *pKeyValue );
  63. //-----------------------------------------------------------------------------
  64. // Purpose: stringtable is a session global string table.
  65. //-----------------------------------------------------------------------------
  66. struct stringtable_t
  67. {
  68. byte *base;
  69. int *ptr;
  70. const char *string;
  71. int dupindex;
  72. byte *addr;
  73. };
  74. static int numStrings;
  75. static stringtable_t strings[32768];
  76. static void BeginStringTable( )
  77. {
  78. strings[0].base = NULL;
  79. strings[0].ptr = NULL;
  80. strings[0].string = "";
  81. strings[0].dupindex = -1;
  82. numStrings = 1;
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose: add a string to the file-global string table.
  86. // Keep track of fixup locations
  87. //-----------------------------------------------------------------------------
  88. static void AddToStringTable( void *base, int *ptr, const char *string )
  89. {
  90. for (int i = 0; i < numStrings; i++)
  91. {
  92. if (!string || !strcmp( string, strings[i].string ))
  93. {
  94. strings[numStrings].base = (byte *)base;
  95. strings[numStrings].ptr = ptr;
  96. strings[numStrings].string = string;
  97. strings[numStrings].dupindex = i;
  98. numStrings++;
  99. return;
  100. }
  101. }
  102. strings[numStrings].base = (byte *)base;
  103. strings[numStrings].ptr = ptr;
  104. strings[numStrings].string = string;
  105. strings[numStrings].dupindex = -1;
  106. numStrings++;
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose: Write out stringtable
  110. // fixup local pointers
  111. //-----------------------------------------------------------------------------
  112. static byte *WriteStringTable( byte *pData )
  113. {
  114. // force null at first address
  115. strings[0].addr = pData;
  116. *pData = '\0';
  117. pData++;
  118. // save all the rest
  119. for (int i = 1; i < numStrings; i++)
  120. {
  121. if (strings[i].dupindex == -1)
  122. {
  123. // not in table yet
  124. // calc offset relative to local base
  125. *strings[i].ptr = pData - strings[i].base;
  126. // keep track of address in case of duplication
  127. strings[i].addr = pData;
  128. // copy string data, add a terminating \0
  129. strcpy( (char *)pData, strings[i].string );
  130. pData += strlen( strings[i].string );
  131. *pData = '\0';
  132. pData++;
  133. }
  134. else
  135. {
  136. // already in table, calc offset of existing string relative to local base
  137. *strings[i].ptr = strings[strings[i].dupindex].addr - strings[i].base;
  138. }
  139. }
  140. ALIGN4( pData );
  141. return pData;
  142. }
  143. // compare function for qsort below
  144. static int BoneNameCompare( const void *elem1, const void *elem2 )
  145. {
  146. int index1 = *(byte *)elem1;
  147. int index2 = *(byte *)elem2;
  148. // compare bones by name
  149. return strcmpi( g_bonetable[index1].name, g_bonetable[index2].name );
  150. }
  151. static void WriteBoneInfo( studiohdr_t *phdr )
  152. {
  153. int i, j, k;
  154. mstudiobone_t *pbone;
  155. mstudiobonecontroller_t *pbonecontroller;
  156. mstudioattachment_t *pattachment;
  157. mstudiobbox_t *pbbox;
  158. // save bone info
  159. pbone = (mstudiobone_t *)pData;
  160. phdr->numbones = g_numbones;
  161. phdr->boneindex = pData - pStart;
  162. char* pSurfacePropName = GetDefaultSurfaceProp( );
  163. AddToStringTable( phdr, &phdr->surfacepropindex, pSurfacePropName );
  164. phdr->contents = GetDefaultContents();
  165. for (i = 0; i < g_numbones; i++)
  166. {
  167. AddToStringTable( &pbone[i], &pbone[i].sznameindex, g_bonetable[i].name );
  168. pbone[i].parent = g_bonetable[i].parent;
  169. pbone[i].flags = g_bonetable[i].flags;
  170. pbone[i].procindex = 0;
  171. pbone[i].physicsbone = g_bonetable[i].physicsBoneIndex;
  172. pbone[i].pos = g_bonetable[i].pos;
  173. pbone[i].rot = g_bonetable[i].rot;
  174. pbone[i].posscale = g_bonetable[i].posscale;
  175. pbone[i].rotscale = g_bonetable[i].rotscale;
  176. MatrixInvert( g_bonetable[i].boneToPose, pbone[i].poseToBone );
  177. pbone[i].qAlignment = g_bonetable[i].qAlignment;
  178. AngleQuaternion( RadianEuler( g_bonetable[i].rot[0], g_bonetable[i].rot[1], g_bonetable[i].rot[2] ), pbone[i].quat );
  179. QuaternionAlign( pbone[i].qAlignment, pbone[i].quat, pbone[i].quat );
  180. pSurfacePropName = GetSurfaceProp( g_bonetable[i].name );
  181. AddToStringTable( &pbone[i], &pbone[i].surfacepropidx, pSurfacePropName );
  182. pbone[i].contents = GetContents( g_bonetable[i].name );
  183. }
  184. pData += g_numbones * sizeof( mstudiobone_t );
  185. ALIGN4( pData );
  186. // save procedural bone info
  187. if (g_numaxisinterpbones)
  188. {
  189. mstudioaxisinterpbone_t *pProc = (mstudioaxisinterpbone_t *)pData;
  190. for (i = 0; i < g_numaxisinterpbones; i++)
  191. {
  192. j = g_axisinterpbonemap[i];
  193. k = g_axisinterpbones[j].bone;
  194. pbone[k].procindex = (byte *)&pProc[i] - (byte *)&pbone[k];
  195. pbone[k].proctype = STUDIO_PROC_AXISINTERP;
  196. // printf("bone %d %d\n", j, pbone[k].procindex );
  197. pProc[i].control = g_axisinterpbones[j].control;
  198. pProc[i].axis = g_axisinterpbones[j].axis;
  199. for (k = 0; k < 6; k++)
  200. {
  201. VectorCopy( g_axisinterpbones[j].pos[k], pProc[i].pos[k] );
  202. pProc[i].quat[k] = g_axisinterpbones[j].quat[k];
  203. }
  204. }
  205. pData += g_numaxisinterpbones * sizeof( mstudioaxisinterpbone_t );
  206. ALIGN4( pData );
  207. }
  208. if (g_numquatinterpbones)
  209. {
  210. mstudioquatinterpbone_t *pProc = (mstudioquatinterpbone_t *)pData;
  211. pData += g_numquatinterpbones * sizeof( mstudioquatinterpbone_t );
  212. ALIGN4( pData );
  213. for (i = 0; i < g_numquatinterpbones; i++)
  214. {
  215. j = g_quatinterpbonemap[i];
  216. k = g_quatinterpbones[j].bone;
  217. pbone[k].procindex = (byte *)&pProc[i] - (byte *)&pbone[k];
  218. pbone[k].proctype = STUDIO_PROC_QUATINTERP;
  219. // printf("bone %d %d\n", j, pbone[k].procindex );
  220. pProc[i].control = g_quatinterpbones[j].control;
  221. mstudioquatinterpinfo_t *pTrigger = (mstudioquatinterpinfo_t *)pData;
  222. pProc[i].numtriggers = g_quatinterpbones[j].numtriggers;
  223. pProc[i].triggerindex = (byte *)pTrigger - (byte *)&pProc[i];
  224. pData += pProc[i].numtriggers * sizeof( mstudioquatinterpinfo_t );
  225. for (k = 0; k < pProc[i].numtriggers; k++)
  226. {
  227. pTrigger[k].inv_tolerance = 1.0 / g_quatinterpbones[j].tolerance[k];
  228. pTrigger[k].trigger = g_quatinterpbones[j].trigger[k];
  229. pTrigger[k].pos = g_quatinterpbones[j].pos[k];
  230. pTrigger[k].quat = g_quatinterpbones[j].quat[k];
  231. }
  232. }
  233. }
  234. if (g_numjigglebones)
  235. {
  236. mstudiojigglebone_t *jiggleInfo = (mstudiojigglebone_t *)pData;
  237. for (i = 0; i < g_numjigglebones; i++)
  238. {
  239. j = g_jigglebonemap[i];
  240. k = g_jigglebones[j].bone;
  241. pbone[k].procindex = (byte *)&jiggleInfo[i] - (byte *)&pbone[k];
  242. pbone[k].proctype = STUDIO_PROC_JIGGLE;
  243. jiggleInfo[i] = g_jigglebones[j].data;
  244. }
  245. pData += g_numjigglebones * sizeof( mstudiojigglebone_t );
  246. ALIGN4( pData );
  247. }
  248. // write aim at bones
  249. if (g_numaimatbones)
  250. {
  251. mstudioaimatbone_t *pProc = (mstudioaimatbone_t *)pData;
  252. for (i = 0; i < g_numaimatbones; i++)
  253. {
  254. j = g_aimatbonemap[i];
  255. k = g_aimatbones[j].bone;
  256. pbone[k].procindex = (byte *)&pProc[i] - (byte *)&pbone[k];
  257. pbone[k].proctype = g_aimatbones[j].aimAttach == -1 ? STUDIO_PROC_AIMATBONE : STUDIO_PROC_AIMATATTACH;
  258. pProc[i].parent = g_aimatbones[j].parent;
  259. pProc[i].aim = g_aimatbones[j].aimAttach == -1 ? g_aimatbones[j].aimBone : g_aimatbones[j].aimAttach;
  260. pProc[i].aimvector = g_aimatbones[j].aimvector;
  261. pProc[i].upvector = g_aimatbones[j].upvector;
  262. pProc[i].basepos = g_aimatbones[j].basepos;
  263. }
  264. pData += g_numaimatbones * sizeof( mstudioaimatbone_t );
  265. ALIGN4( pData );
  266. }
  267. // map g_bonecontroller to bones
  268. for (i = 0; i < g_numbones; i++)
  269. {
  270. for (j = 0; j < 6; j++)
  271. {
  272. pbone[i].bonecontroller[j] = -1;
  273. }
  274. }
  275. for (i = 0; i < g_numbonecontrollers; i++)
  276. {
  277. j = g_bonecontroller[i].bone;
  278. switch( g_bonecontroller[i].type & STUDIO_TYPES )
  279. {
  280. case STUDIO_X:
  281. pbone[j].bonecontroller[0] = i;
  282. break;
  283. case STUDIO_Y:
  284. pbone[j].bonecontroller[1] = i;
  285. break;
  286. case STUDIO_Z:
  287. pbone[j].bonecontroller[2] = i;
  288. break;
  289. case STUDIO_XR:
  290. pbone[j].bonecontroller[3] = i;
  291. break;
  292. case STUDIO_YR:
  293. pbone[j].bonecontroller[4] = i;
  294. break;
  295. case STUDIO_ZR:
  296. pbone[j].bonecontroller[5] = i;
  297. break;
  298. default:
  299. MdlError("unknown g_bonecontroller type\n");
  300. }
  301. }
  302. // save g_bonecontroller info
  303. pbonecontroller = (mstudiobonecontroller_t *)pData;
  304. phdr->numbonecontrollers = g_numbonecontrollers;
  305. phdr->bonecontrollerindex = pData - pStart;
  306. for (i = 0; i < g_numbonecontrollers; i++)
  307. {
  308. pbonecontroller[i].bone = g_bonecontroller[i].bone;
  309. pbonecontroller[i].inputfield = g_bonecontroller[i].inputfield;
  310. pbonecontroller[i].type = g_bonecontroller[i].type;
  311. pbonecontroller[i].start = g_bonecontroller[i].start;
  312. pbonecontroller[i].end = g_bonecontroller[i].end;
  313. }
  314. pData += g_numbonecontrollers * sizeof( mstudiobonecontroller_t );
  315. ALIGN4( pData );
  316. // save attachment info
  317. pattachment = (mstudioattachment_t *)pData;
  318. phdr->numlocalattachments = g_numattachments;
  319. phdr->localattachmentindex = pData - pStart;
  320. for (i = 0; i < g_numattachments; i++)
  321. {
  322. pattachment[i].localbone = g_attachment[i].bone;
  323. AddToStringTable( &pattachment[i], &pattachment[i].sznameindex, g_attachment[i].name );
  324. MatrixCopy( g_attachment[i].local, pattachment[i].local );
  325. pattachment[i].flags = g_attachment[i].flags;
  326. }
  327. pData += g_numattachments * sizeof( mstudioattachment_t );
  328. ALIGN4( pData );
  329. // save hitbox sets
  330. phdr->numhitboxsets = g_hitboxsets.Size();
  331. // Remember start spot
  332. mstudiohitboxset_t *hitboxset = (mstudiohitboxset_t *)pData;
  333. phdr->hitboxsetindex = pData - pStart;
  334. pData += phdr->numhitboxsets * sizeof( mstudiohitboxset_t );
  335. ALIGN4( pData );
  336. for ( int s = 0; s < g_hitboxsets.Size(); s++, hitboxset++ )
  337. {
  338. s_hitboxset *set = &g_hitboxsets[ s ];
  339. AddToStringTable( hitboxset, &hitboxset->sznameindex, set->hitboxsetname );
  340. hitboxset->numhitboxes = set->numhitboxes;
  341. hitboxset->hitboxindex = ( pData - (byte *)hitboxset );
  342. // save bbox info
  343. pbbox = (mstudiobbox_t *)pData;
  344. for (i = 0; i < hitboxset->numhitboxes; i++)
  345. {
  346. pbbox[i].bone = set->hitbox[i].bone;
  347. pbbox[i].group = set->hitbox[i].group;
  348. VectorCopy( set->hitbox[i].bmin, pbbox[i].bbmin );
  349. VectorCopy( set->hitbox[i].bmax, pbbox[i].bbmax );
  350. pbbox[i].szhitboxnameindex = 0;
  351. AddToStringTable( &(pbbox[i]), &(pbbox[i].szhitboxnameindex), set->hitbox[i].hitboxname );
  352. }
  353. pData += hitboxset->numhitboxes * sizeof( mstudiobbox_t );
  354. ALIGN4( pData );
  355. }
  356. byte *pBoneTable = pData;
  357. phdr->bonetablebynameindex = (pData - pStart);
  358. // make a table in bone order and sort it with qsort
  359. for ( i = 0; i < phdr->numbones; i++ )
  360. {
  361. pBoneTable[i] = i;
  362. }
  363. qsort( pBoneTable, phdr->numbones, sizeof(byte), BoneNameCompare );
  364. pData += phdr->numbones * sizeof( byte );
  365. ALIGN4( pData );
  366. }
  367. // load a preexisting model to remember its sequence names and indices
  368. CUtlVector< CUtlString > g_vecPreexistingSequences;
  369. void LoadPreexistingSequenceOrder( const char *pFilename )
  370. {
  371. g_vecPreexistingSequences.RemoveAll();
  372. if ( !FileExists( pFilename ) )
  373. return;
  374. Msg( "Loading preexisting model: %s\n", pFilename );
  375. studiohdr_t *pStudioHdr;
  376. int len = LoadFile((char*)pFilename, (void **)&pStudioHdr);
  377. if ( len && pStudioHdr && pStudioHdr->SequencesAvailable() )
  378. {
  379. Msg( " Found %i preexisting sequences.\n", pStudioHdr->GetNumSeq() );
  380. for ( int i=0; i<pStudioHdr->GetNumSeq(); i++ )
  381. {
  382. //Msg( " Sequence %i : \"%s\"\n", i, pStudioHdr->pSeqdesc(i).pszLabel() );
  383. g_vecPreexistingSequences.AddToTail( pStudioHdr->pSeqdesc(i).pszLabel() );
  384. }
  385. }
  386. else
  387. {
  388. MdlWarning( "Zero-size file or no sequences.\n" );
  389. }
  390. }
  391. static void WriteSequenceInfo( studiohdr_t *phdr )
  392. {
  393. int i, j, k;
  394. mstudioseqdesc_t *pseqdesc;
  395. mstudioseqdesc_t *pbaseseqdesc;
  396. mstudioevent_t *pevent;
  397. byte *ptransition;
  398. // write models to disk with this flag set false. This will force
  399. // the sequences to be indexed by activity whenever the g_model is loaded
  400. // from disk.
  401. phdr->activitylistversion = 0;
  402. phdr->eventsindexed = 0;
  403. // save g_sequence info
  404. pseqdesc = (mstudioseqdesc_t *)pData;
  405. pbaseseqdesc = pseqdesc;
  406. phdr->numlocalseq = g_sequence.Count();
  407. phdr->localseqindex = (pData - pStart);
  408. pData += g_sequence.Count() * sizeof( mstudioseqdesc_t );
  409. bool bErrors = false;
  410. // build a table to remap new sequence indices to match the preexisting model
  411. bool bUseSeqOrderRemapping = false;
  412. int nSeqOrderRemappingTable[MAXSTUDIOSEQUENCES];
  413. for (i=0; i<MAXSTUDIOSEQUENCES; i++)
  414. nSeqOrderRemappingTable[i] = -1;
  415. bool bAllowSequenceRemoval = false;
  416. if ( g_vecPreexistingSequences.Count() )
  417. {
  418. if ( g_sequence.Count() < g_vecPreexistingSequences.Count() && !bAllowSequenceRemoval )
  419. {
  420. Msg( "\n" );
  421. MdlWarning( "This model has fewer sequences than its predecessor.\nPlease confirm sequence deletion: [y/n] " );
  422. int nInput = 0;
  423. do { nInput = getchar(); } while ( nInput != 121 /* y */ && nInput != 110 /* n */ );
  424. if ( nInput == 110 )
  425. {
  426. MdlError( "Model contains fewer sequences than its predecessor!\n" );
  427. }
  428. else if ( nInput == 121 )
  429. {
  430. bAllowSequenceRemoval = true;
  431. }
  432. }
  433. {
  434. Msg( "Building sequence index remapping table...\n" );
  435. CUtlVector<int> vecNewIndices;
  436. vecNewIndices.RemoveAll();
  437. // map current sequences to their old indices
  438. for (i = 0; i < g_sequence.Count(); i++ )
  439. {
  440. int nIdx = g_vecPreexistingSequences.Find( g_sequence[i].name );
  441. if ( nIdx >= 0 )
  442. {
  443. nSeqOrderRemappingTable[nIdx] = i;
  444. }
  445. else
  446. {
  447. if ( i < g_vecPreexistingSequences.Count() )
  448. {
  449. Msg( " Found new sequence \"%s\" using index of old sequence \"%s\".\n", g_sequence[i].name, g_vecPreexistingSequences[i].String() );
  450. }
  451. else
  452. {
  453. Msg( " Found new sequence \"%s\".\n", g_sequence[i].name );
  454. }
  455. vecNewIndices.AddToTail(i);
  456. }
  457. }
  458. // slot new sequences into unused indices
  459. while ( vecNewIndices.Count() )
  460. {
  461. for (i = 0; i < MAXSTUDIOSEQUENCES; i++ )
  462. {
  463. if ( nSeqOrderRemappingTable[i] == -1 )
  464. {
  465. nSeqOrderRemappingTable[i] = vecNewIndices[0];
  466. vecNewIndices.Remove(0);
  467. break;
  468. }
  469. }
  470. }
  471. // verify no indices are undefined
  472. for (i = 0; i < g_sequence.Count(); i++ )
  473. {
  474. if ( nSeqOrderRemappingTable[i] == -1 )
  475. {
  476. if ( bAllowSequenceRemoval )
  477. {
  478. do
  479. {
  480. for ( int nB=i; nB<g_vecPreexistingSequences.Count(); nB++ )
  481. {
  482. nSeqOrderRemappingTable[nB] = nSeqOrderRemappingTable[nB+1];
  483. }
  484. }
  485. while (nSeqOrderRemappingTable[i] == -1);
  486. }
  487. else
  488. {
  489. MdlError( "Failed to reorder sequence indices.\n" );
  490. }
  491. }
  492. else if ( nSeqOrderRemappingTable[i] != i )
  493. {
  494. bUseSeqOrderRemapping = true;
  495. }
  496. }
  497. if ( bUseSeqOrderRemapping )
  498. {
  499. Msg( "Sequence indices need re-ordering.\n" );
  500. }
  501. else
  502. {
  503. Msg( "No re-ordering required.\n" );
  504. }
  505. }
  506. }
  507. // build an inverted remapping table so autolayer sequence indices can find their sources later
  508. int nSeqOrderRemappingTableInv[MAXSTUDIOSEQUENCES];
  509. if ( bUseSeqOrderRemapping )
  510. {
  511. for (i=0; i<MAXSTUDIOSEQUENCES; i++)
  512. nSeqOrderRemappingTableInv[nSeqOrderRemappingTable[i]] = i;
  513. }
  514. int m;
  515. for (m = 0; m < g_sequence.Count(); m++, pseqdesc++)
  516. {
  517. if ( bUseSeqOrderRemapping )
  518. {
  519. i = nSeqOrderRemappingTable[m];
  520. if ( i != m )
  521. {
  522. Msg( " Remapping sequence %i to index %i (%s) to retain existing order.\n", i, m, g_sequence[i].name );
  523. }
  524. }
  525. else
  526. {
  527. i = m;
  528. }
  529. byte *pSequenceStart = (byte *)pseqdesc;
  530. AddToStringTable( pseqdesc, &pseqdesc->szlabelindex, g_sequence[i].name );
  531. AddToStringTable( pseqdesc, &pseqdesc->szactivitynameindex, g_sequence[i].activityname );
  532. pseqdesc->baseptr = pStart - (byte *)pseqdesc;
  533. pseqdesc->flags = g_sequence[i].flags;
  534. pseqdesc->numblends = g_sequence[i].numblends;
  535. pseqdesc->groupsize[0] = g_sequence[i].groupsize[0];
  536. pseqdesc->groupsize[1] = g_sequence[i].groupsize[1];
  537. pseqdesc->paramindex[0] = g_sequence[i].paramindex[0];
  538. pseqdesc->paramstart[0] = g_sequence[i].paramstart[0];
  539. pseqdesc->paramend[0] = g_sequence[i].paramend[0];
  540. pseqdesc->paramindex[1] = g_sequence[i].paramindex[1];
  541. pseqdesc->paramstart[1] = g_sequence[i].paramstart[1];
  542. pseqdesc->paramend[1] = g_sequence[i].paramend[1];
  543. if (g_sequence[i].groupsize[0] > 1 || g_sequence[i].groupsize[1] > 1)
  544. {
  545. // save posekey values
  546. float *pposekey = (float *)pData;
  547. pseqdesc->posekeyindex = (pData - pSequenceStart);
  548. pData += (pseqdesc->groupsize[0] + pseqdesc->groupsize[1]) * sizeof( float );
  549. for (j = 0; j < pseqdesc->groupsize[0]; j++)
  550. {
  551. *(pposekey++) = g_sequence[i].param0[j];
  552. // printf("%.2f ", g_sequence[i].param0[j] );
  553. }
  554. for (j = 0; j < pseqdesc->groupsize[1]; j++)
  555. {
  556. *(pposekey++) = g_sequence[i].param1[j];
  557. // printf("%.2f ", g_sequence[i].param1[j] );
  558. }
  559. // printf("\n" );
  560. }
  561. // pseqdesc->motiontype = g_sequence[i].motiontype;
  562. // pseqdesc->motionbone = 0; // g_sequence[i].motionbone;
  563. // VectorCopy( g_sequence[i].linearmovement, pseqdesc->linearmovement );
  564. pseqdesc->activity = g_sequence[i].activity;
  565. pseqdesc->actweight = g_sequence[i].actweight;
  566. pseqdesc->bbmin = g_sequence[i].bmin;
  567. pseqdesc->bbmax = g_sequence[i].bmax;
  568. pseqdesc->fadeintime = g_sequence[i].fadeintime;
  569. pseqdesc->fadeouttime = g_sequence[i].fadeouttime;
  570. pseqdesc->localentrynode = g_sequence[i].entrynode;
  571. pseqdesc->localexitnode = g_sequence[i].exitnode;
  572. //pseqdesc->entryphase = g_sequence[i].entryphase;
  573. //pseqdesc->exitphase = g_sequence[i].exitphase;
  574. pseqdesc->nodeflags = g_sequence[i].nodeflags;
  575. // save events
  576. pevent = (mstudioevent_t *)pData;
  577. pseqdesc->numevents = g_sequence[i].numevents;
  578. pseqdesc->eventindex = (pData - pSequenceStart);
  579. pData += pseqdesc->numevents * sizeof( mstudioevent_t );
  580. for (j = 0; j < g_sequence[i].numevents; j++)
  581. {
  582. k = g_sequence[i].panim[0][0]->numframes - 1;
  583. if (g_sequence[i].event[j].frame <= k)
  584. pevent[j].cycle = g_sequence[i].event[j].frame / ((float)k);
  585. else if (k == 0 && g_sequence[i].event[j].frame == 0)
  586. pevent[j].cycle = 0;
  587. else
  588. {
  589. MdlWarning("Event %d (frame %d) out of range in %s\n", g_sequence[i].event[j].event, g_sequence[i].event[j].frame, g_sequence[i].name );
  590. bErrors = true;
  591. }
  592. //Adrian - Remove me once we phase out the old event system.
  593. if ( V_isdigit( g_sequence[i].event[j].eventname[0] ) )
  594. {
  595. pevent[j].event = atoi( g_sequence[i].event[j].eventname );
  596. pevent[j].type = 0;
  597. pevent[j].szeventindex = 0;
  598. }
  599. else
  600. {
  601. AddToStringTable( &pevent[j], &pevent[j].szeventindex, g_sequence[i].event[j].eventname );
  602. pevent[j].type = NEW_EVENT_STYLE;
  603. }
  604. // printf("%4d : %d %f\n", pevent[j].event, g_sequence[i].event[j].frame, pevent[j].cycle );
  605. // AddToStringTable( &pevent[j], &pevent[j].szoptionindex, g_sequence[i].event[j].options );
  606. strcpy( pevent[j].options, g_sequence[i].event[j].options );
  607. }
  608. ALIGN4( pData );
  609. // save ikrules
  610. pseqdesc->numikrules = g_sequence[i].numikrules;
  611. // save autolayers
  612. mstudioautolayer_t *pautolayer = (mstudioautolayer_t *)pData;
  613. pseqdesc->numautolayers = g_sequence[i].numautolayers;
  614. pseqdesc->autolayerindex = (pData - pSequenceStart);
  615. pData += pseqdesc->numautolayers * sizeof( mstudioautolayer_t );
  616. for (j = 0; j < g_sequence[i].numautolayers; j++)
  617. {
  618. pautolayer[j].iSequence = g_sequence[i].autolayer[j].sequence;
  619. pautolayer[j].iPose = g_sequence[i].autolayer[j].pose;
  620. pautolayer[j].flags = g_sequence[i].autolayer[j].flags;
  621. // autolayer indices are stored by index, so remap them now using the invertex lookup table
  622. if ( bUseSeqOrderRemapping )
  623. {
  624. int nRemapAutoLayer = nSeqOrderRemappingTableInv[ pautolayer[j].iSequence ];
  625. if ( nRemapAutoLayer != pautolayer[j].iSequence )
  626. {
  627. Msg( " Autolayer remapping index %i to %i.\n", pautolayer[j].iSequence, nRemapAutoLayer );
  628. pautolayer[j].iSequence = nRemapAutoLayer;
  629. }
  630. }
  631. if (!(pautolayer[j].flags & STUDIO_AL_POSE))
  632. {
  633. pautolayer[j].start = g_sequence[i].autolayer[j].start / (g_sequence[i].panim[0][0]->numframes - 1);
  634. pautolayer[j].peak = g_sequence[i].autolayer[j].peak / (g_sequence[i].panim[0][0]->numframes - 1);
  635. pautolayer[j].tail = g_sequence[i].autolayer[j].tail / (g_sequence[i].panim[0][0]->numframes - 1);
  636. pautolayer[j].end = g_sequence[i].autolayer[j].end / (g_sequence[i].panim[0][0]->numframes - 1);
  637. }
  638. else
  639. {
  640. pautolayer[j].start = g_sequence[i].autolayer[j].start;
  641. pautolayer[j].peak = g_sequence[i].autolayer[j].peak;
  642. pautolayer[j].tail = g_sequence[i].autolayer[j].tail;
  643. pautolayer[j].end = g_sequence[i].autolayer[j].end;
  644. }
  645. }
  646. // save boneweights
  647. float *pweight = 0;
  648. j = 0;
  649. // look up previous sequence weights and try to find a match
  650. for (k = 0; k < m; k++)
  651. {
  652. j = 0;
  653. // only check newer boneweights than the last one
  654. if (pseqdesc[k-m].pBoneweight( 0 ) > pweight)
  655. {
  656. pweight = pseqdesc[k-m].pBoneweight( 0 );
  657. for (j = 0; j < g_numbones; j++)
  658. {
  659. // we're not walking the linear sequence list if we're remapping, so we need to remap this check
  660. int nRemap = k;
  661. if ( bUseSeqOrderRemapping )
  662. nRemap = nSeqOrderRemappingTable[k];
  663. if (g_sequence[i].weight[j] != g_sequence[nRemap].weight[j])
  664. break;
  665. }
  666. if (j == g_numbones)
  667. break;
  668. }
  669. }
  670. // check to see if all the bones matched
  671. if (j < g_numbones)
  672. {
  673. // allocate new block
  674. //printf("new %08x\n", pData );
  675. pweight = (float *)pData;
  676. pseqdesc->weightlistindex = (pData - pSequenceStart);
  677. pData += g_numbones * sizeof( float );
  678. for (j = 0; j < g_numbones; j++)
  679. {
  680. pweight[j] = g_sequence[i].weight[j];
  681. }
  682. }
  683. else
  684. {
  685. // use previous boneweight
  686. //printf("prev %08x\n", pweight );
  687. pseqdesc->weightlistindex = ((byte *)pweight - pSequenceStart);
  688. }
  689. // save iklocks
  690. mstudioiklock_t *piklock = (mstudioiklock_t *)pData;
  691. pseqdesc->numiklocks = g_sequence[i].numiklocks;
  692. pseqdesc->iklockindex = (pData - pSequenceStart);
  693. pData += pseqdesc->numiklocks * sizeof( mstudioiklock_t );
  694. ALIGN4( pData );
  695. for (j = 0; j < pseqdesc->numiklocks; j++)
  696. {
  697. piklock->chain = g_sequence[i].iklock[j].chain;
  698. piklock->flPosWeight = g_sequence[i].iklock[j].flPosWeight;
  699. piklock->flLocalQWeight = g_sequence[i].iklock[j].flLocalQWeight;
  700. piklock++;
  701. }
  702. // Write animation blend parameters
  703. short *blends = ( short * )pData;
  704. pseqdesc->animindexindex = ( pData - pSequenceStart );
  705. pData += ( g_sequence[i].groupsize[0] * g_sequence[i].groupsize[1] ) * sizeof( short );
  706. ALIGN4( pData );
  707. for ( j = 0; j < g_sequence[i].groupsize[0] ; j++ )
  708. {
  709. for ( k = 0; k < g_sequence[i].groupsize[1]; k++ )
  710. {
  711. // height value * width of row + width value
  712. int offset = k * g_sequence[i].groupsize[0] + j;
  713. if ( g_sequence[i].panim[j][k] )
  714. {
  715. int animindex = g_sequence[i].panim[j][k]->index;
  716. Assert( animindex >= 0 && animindex < SHRT_MAX );
  717. blends[ offset ] = (short)animindex;
  718. }
  719. else
  720. {
  721. blends[ offset ] = 0;
  722. }
  723. }
  724. }
  725. // Write cycle overrides
  726. pseqdesc->cycleposeindex = g_sequence[i].cycleposeindex;
  727. WriteSeqKeyValues( pseqdesc, &g_sequence[i].KeyValue );
  728. }
  729. if (bErrors)
  730. {
  731. MdlError( "Exiting due to Errors\n");
  732. }
  733. // save transition graph
  734. int *pxnodename = (int *)pData;
  735. phdr->localnodenameindex = (pData - pStart);
  736. pData += g_numxnodes * sizeof( *pxnodename );
  737. ALIGN4( pData );
  738. for (i = 0; i < g_numxnodes; i++)
  739. {
  740. AddToStringTable( phdr, pxnodename, g_xnodename[i+1] );
  741. // printf("%d : %s\n", i, g_xnodename[i+1] );
  742. pxnodename++;
  743. }
  744. ptransition = (byte *)pData;
  745. phdr->numlocalnodes = g_numxnodes;
  746. phdr->localnodeindex = pData - pStart;
  747. pData += g_numxnodes * g_numxnodes * sizeof( byte );
  748. ALIGN4( pData );
  749. for (i = 0; i < g_numxnodes; i++)
  750. {
  751. // printf("%2d (%12s) : ", i + 1, g_xnodename[i+1] );
  752. for (j = 0; j < g_numxnodes; j++)
  753. {
  754. *ptransition++ = g_xnode[i][j];
  755. // printf(" %2d", g_xnode[i][j] );
  756. }
  757. // printf("\n" );
  758. }
  759. }
  760. //-----------------------------------------------------------------------------
  761. // Purpose: Stub implementation
  762. // Input : *group -
  763. //-----------------------------------------------------------------------------
  764. const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *modelname ) const
  765. {
  766. return NULL;
  767. }
  768. virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const
  769. {
  770. return NULL;
  771. }
  772. const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
  773. {
  774. return (studiohdr_t *)cache;
  775. }
  776. byte *studiohdr_t::GetAnimBlock( int i ) const
  777. {
  778. return NULL;
  779. }
  780. int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
  781. {
  782. return 0;
  783. }
  784. int rawanimbytes = 0;
  785. int animboneframes = 0;
  786. int numAxis[4] = { 0, 0, 0, 0 };
  787. int numPos[4] = { 0, 0, 0, 0 };
  788. int useRaw = 0;
  789. void WriteAnimationData( s_animation_t *srcanim, mstudioanimdesc_t *destanimdesc, byte *&pLocalData, byte *&pExtData )
  790. {
  791. int j, k, n;
  792. byte *pData = NULL;
  793. for (int w = 0; w < srcanim->numsections; w++)
  794. {
  795. bool bUseExtData = false;
  796. pData = pLocalData;
  797. if (pExtData != NULL && !srcanim->disableAnimblocks && !(w == 0 && srcanim->isFirstSectionLocal))
  798. {
  799. pData = pExtData;
  800. bUseExtData = true;
  801. }
  802. mstudioanim_t *destanim = (mstudioanim_t *)pData;
  803. byte *pStartSection = pData;
  804. pData += sizeof( *destanim );
  805. destanim->bone = 255;
  806. mstudioanim_t *prevanim = NULL;
  807. // save animation value info
  808. for (j = 0; j < g_numbones; j++)
  809. {
  810. // destanim->weight = srcanim->weight[j];
  811. // printf( "%s %.1f\n", g_bonetable[j].name, destanim->weight );
  812. destanim->flags = 0;
  813. s_compressed_t *psrcdata = &srcanim->anim[w][j];
  814. numPos[ (psrcdata->num[0] != 0) + (psrcdata->num[1] != 0) + (psrcdata->num[2] != 0) ]++;
  815. numAxis[ (psrcdata->num[3] != 0) + (psrcdata->num[4] != 0) + (psrcdata->num[5] != 0) ]++;
  816. if (psrcdata->num[0] + psrcdata->num[1] + psrcdata->num[2] + psrcdata->num[3] + psrcdata->num[4] + psrcdata->num[5] == 0)
  817. {
  818. // no animation, skip
  819. continue;
  820. }
  821. destanim->bone = j;
  822. // copy flags over if delta animation
  823. if (srcanim->flags & STUDIO_DELTA)
  824. {
  825. destanim->flags |= STUDIO_ANIM_DELTA;
  826. }
  827. if ((srcanim->numframes == 1) || (psrcdata->num[0] <= 2 && psrcdata->num[1] <= 2 && psrcdata->num[2] <= 2 && psrcdata->num[3] <= 2 && psrcdata->num[4] <= 2 && psrcdata->num[5] <= 2))
  828. {
  829. // printf("%d : %d %d %d : %d %d %d\n", j, psrcdata->num[0], psrcdata->num[1], psrcdata->num[2], psrcdata->num[3], psrcdata->num[4], psrcdata->num[5] );
  830. // single frame, if animation detected just store as raw
  831. int iFrame = min( w * srcanim->sectionframes, srcanim->numframes - 1 );
  832. if (psrcdata->num[3] != 0 || psrcdata->num[4] != 0 || psrcdata->num[5] != 0)
  833. {
  834. Quaternion q;
  835. AngleQuaternion( srcanim->sanim[iFrame][j].rot, q );
  836. *((Quaternion64 *)pData) = q;
  837. pData += sizeof( Quaternion64 );
  838. rawanimbytes += sizeof( Quaternion64 );
  839. destanim->flags |= STUDIO_ANIM_RAWROT2;
  840. }
  841. if (psrcdata->num[0] != 0 || psrcdata->num[1] != 0 || psrcdata->num[2] != 0)
  842. {
  843. *((Vector48 *)pData) = srcanim->sanim[iFrame][j].pos;
  844. pData += sizeof( Vector48 );
  845. rawanimbytes += sizeof( Vector48 );
  846. destanim->flags |= STUDIO_ANIM_RAWPOS;
  847. }
  848. }
  849. else
  850. {
  851. // look to see if storing raw quat's would have taken less space
  852. if (psrcdata->num[3] >= srcanim->numframes && psrcdata->num[4] >= srcanim->numframes && psrcdata->num[5] >= srcanim->numframes)
  853. {
  854. useRaw++;
  855. }
  856. mstudioanim_valueptr_t *posvptr = NULL;
  857. mstudioanim_valueptr_t *rotvptr = NULL;
  858. // allocate room for rotation ptrs
  859. rotvptr = (mstudioanim_valueptr_t *)pData;
  860. pData += sizeof( *rotvptr );
  861. // skip all position info if there's no animation
  862. if (psrcdata->num[0] != 0 || psrcdata->num[1] != 0 || psrcdata->num[2] != 0)
  863. {
  864. posvptr = (mstudioanim_valueptr_t *)pData;
  865. pData += sizeof( *posvptr );
  866. }
  867. mstudioanimvalue_t *destanimvalue = (mstudioanimvalue_t *)pData;
  868. if (rotvptr)
  869. {
  870. // store rotation animations
  871. for (k = 3; k < 6; k++)
  872. {
  873. if (psrcdata->num[k] == 0)
  874. {
  875. rotvptr->offset[k-3] = 0;
  876. }
  877. else
  878. {
  879. rotvptr->offset[k-3] = ((byte *)destanimvalue - (byte *)rotvptr);
  880. for (n = 0; n < psrcdata->num[k]; n++)
  881. {
  882. destanimvalue->value = psrcdata->data[k][n].value;
  883. destanimvalue++;
  884. }
  885. }
  886. }
  887. destanim->flags |= STUDIO_ANIM_ANIMROT;
  888. }
  889. if (posvptr)
  890. {
  891. // store position animations
  892. for (k = 0; k < 3; k++)
  893. {
  894. if (psrcdata->num[k] == 0)
  895. {
  896. posvptr->offset[k] = 0;
  897. }
  898. else
  899. {
  900. posvptr->offset[k] = ((byte *)destanimvalue - (byte *)posvptr);
  901. for (n = 0; n < psrcdata->num[k]; n++)
  902. {
  903. destanimvalue->value = psrcdata->data[k][n].value;
  904. destanimvalue++;
  905. }
  906. }
  907. }
  908. destanim->flags |= STUDIO_ANIM_ANIMPOS;
  909. }
  910. rawanimbytes += ((byte *)destanimvalue - pData);
  911. pData = (byte *)destanimvalue;
  912. }
  913. prevanim = destanim;
  914. destanim->nextoffset = pData - (byte *)destanim;
  915. destanim = (mstudioanim_t *)pData;
  916. pData += sizeof( *destanim );
  917. }
  918. if (prevanim)
  919. {
  920. prevanim->nextoffset = 0;
  921. }
  922. ALIGN4( pData );
  923. // write into anim blocks if needed
  924. if (destanimdesc->sectionindex)
  925. {
  926. if (bUseExtData)
  927. {
  928. if (g_numanimblocks && pData - g_animblock[g_numanimblocks-1].start > g_animblocksize)
  929. {
  930. // advance to next animblock
  931. g_animblock[g_numanimblocks-1].end = pStartSection;
  932. g_animblock[g_numanimblocks].start = pStartSection;
  933. g_numanimblocks++;
  934. }
  935. destanimdesc->pSection(w)->animblock = g_numanimblocks - 1;
  936. destanimdesc->pSection(w)->animindex = pStartSection - g_animblock[g_numanimblocks-1].start;
  937. }
  938. else
  939. {
  940. destanimdesc->pSection(w)->animblock = 0;
  941. destanimdesc->pSection(w)->animindex = pStartSection - (byte *)destanimdesc;
  942. }
  943. // printf("%s (%d) : %d:%d\n", srcanim->name, w, destanimdesc->pSection(w)->animblock, destanimdesc->pSection(w)->animindex );
  944. }
  945. if (!bUseExtData)
  946. {
  947. pLocalData = pData;
  948. }
  949. else
  950. {
  951. pExtData = pData;
  952. }
  953. }
  954. }
  955. byte *WriteIkErrors( s_animation_t *srcanim, byte *pData )
  956. {
  957. int j, k;
  958. // write IK error keys
  959. mstudioikrule_t *pikruledata = (mstudioikrule_t *)pData;
  960. pData += srcanim->numikrules * sizeof( *pikruledata );
  961. ALIGN4( pData );
  962. for (j = 0; j < srcanim->numikrules; j++)
  963. {
  964. mstudioikrule_t *pikrule = pikruledata + j;
  965. pikrule->index = srcanim->ikrule[j].index;
  966. pikrule->chain = srcanim->ikrule[j].chain;
  967. pikrule->bone = srcanim->ikrule[j].bone;
  968. pikrule->type = srcanim->ikrule[j].type;
  969. pikrule->slot = srcanim->ikrule[j].slot;
  970. pikrule->pos = srcanim->ikrule[j].pos;
  971. pikrule->q = srcanim->ikrule[j].q;
  972. pikrule->height = srcanim->ikrule[j].height;
  973. pikrule->floor = srcanim->ikrule[j].floor;
  974. pikrule->radius = srcanim->ikrule[j].radius;
  975. if (srcanim->numframes > 1.0)
  976. {
  977. pikrule->start = srcanim->ikrule[j].start / (srcanim->numframes - 1.0f);
  978. pikrule->peak = srcanim->ikrule[j].peak / (srcanim->numframes - 1.0f);
  979. pikrule->tail = srcanim->ikrule[j].tail / (srcanim->numframes - 1.0f);
  980. pikrule->end = srcanim->ikrule[j].end / (srcanim->numframes - 1.0f);
  981. pikrule->contact= srcanim->ikrule[j].contact / (srcanim->numframes - 1.0f);
  982. }
  983. else
  984. {
  985. pikrule->start = 0.0f;
  986. pikrule->peak = 0.0f;
  987. pikrule->tail = 1.0f;
  988. pikrule->end = 1.0f;
  989. pikrule->contact= 0.0f;
  990. }
  991. /*
  992. printf("%d %d %d %d : %.2f %.2f %.2f %.2f\n",
  993. srcanim->ikrule[j].start, srcanim->ikrule[j].peak, srcanim->ikrule[j].tail, srcanim->ikrule[j].end,
  994. pikrule->start, pikrule->peak, pikrule->tail, pikrule->end );
  995. */
  996. pikrule->iStart = srcanim->ikrule[j].start;
  997. #if 0
  998. // uncompressed
  999. pikrule->ikerrorindex = (pData - (byte*)pikrule);
  1000. mstudioikerror_t *perror = (mstudioikerror_t *)pData;
  1001. pData += srcanim->ikrule[j].numerror * sizeof( *perror );
  1002. for (k = 0; k < srcanim->ikrule[j].numerror; k++)
  1003. {
  1004. perror[k].pos = srcanim->ikrule[j].pError[k].pos;
  1005. perror[k].q = srcanim->ikrule[j].pError[k].q;
  1006. }
  1007. #endif
  1008. #if 1
  1009. // skip writting the header if there's no IK data
  1010. for (k = 0; k < 6; k++)
  1011. {
  1012. if (srcanim->ikrule[j].errorData.numanim[k]) break;
  1013. }
  1014. if (k == 6)
  1015. continue;
  1016. // compressed
  1017. pikrule->compressedikerrorindex = (pData - (byte*)pikrule);
  1018. mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pData;
  1019. pData += sizeof( *pCompressed );
  1020. for (k = 0; k < 6; k++)
  1021. {
  1022. pCompressed->scale[k] = srcanim->ikrule[j].errorData.scale[k];
  1023. pCompressed->offset[k] = (pData - (byte*)pCompressed);
  1024. int size = srcanim->ikrule[j].errorData.numanim[k] * sizeof( mstudioanimvalue_t );
  1025. memmove( pData, srcanim->ikrule[j].errorData.anim[k], size );
  1026. pData += size;
  1027. }
  1028. if (strlen( srcanim->ikrule[j].attachment ) > 0)
  1029. {
  1030. // don't use string table, we're probably not in the same file.
  1031. int size = strlen( srcanim->ikrule[j].attachment ) + 1;
  1032. strcpy( (char *)pData, srcanim->ikrule[j].attachment );
  1033. pikrule->szattachmentindex = pData - (byte *)pikrule;
  1034. pData += size;
  1035. }
  1036. ALIGN4( pData );
  1037. #endif
  1038. // AddToStringTable( pikrule, &pikrule->szattachmentindex, srcanim->ikrule[j].attachment );
  1039. }
  1040. return pData;
  1041. }
  1042. byte *WriteLocalHierarchy( s_animation_t *srcanim, byte *pData )
  1043. {
  1044. int j, k;
  1045. // write hierarchy keys
  1046. mstudiolocalhierarchy_t *pHierarchyData = (mstudiolocalhierarchy_t *)pData;
  1047. pData += srcanim->numlocalhierarchy * sizeof( *pHierarchyData );
  1048. ALIGN4( pData );
  1049. for (j = 0; j < srcanim->numlocalhierarchy; j++)
  1050. {
  1051. mstudiolocalhierarchy_t *pHierarchy = pHierarchyData + j;
  1052. pHierarchy->iBone = srcanim->localhierarchy[j].bone;
  1053. pHierarchy->iNewParent = srcanim->localhierarchy[j].newparent;
  1054. if (srcanim->numframes > 1.0)
  1055. {
  1056. pHierarchy->start = srcanim->localhierarchy[j].start / (srcanim->numframes - 1.0f);
  1057. pHierarchy->peak = srcanim->localhierarchy[j].peak / (srcanim->numframes - 1.0f);
  1058. pHierarchy->tail = srcanim->localhierarchy[j].tail / (srcanim->numframes - 1.0f);
  1059. pHierarchy->end = srcanim->localhierarchy[j].end / (srcanim->numframes - 1.0f);
  1060. }
  1061. else
  1062. {
  1063. pHierarchy->start = 0.0f;
  1064. pHierarchy->peak = 0.0f;
  1065. pHierarchy->tail = 1.0f;
  1066. pHierarchy->end = 1.0f;
  1067. }
  1068. pHierarchy->iStart = srcanim->localhierarchy[j].start;
  1069. #if 0
  1070. // uncompressed
  1071. pHierarchy->ikerrorindex = (pData - (byte*)pHierarchy);
  1072. mstudioikerror_t *perror = (mstudioikerror_t *)pData;
  1073. pData += srcanim->ikrule[j].numerror * sizeof( *perror );
  1074. for (k = 0; k < srcanim->ikrule[j].numerror; k++)
  1075. {
  1076. perror[k].pos = srcanim->ikrule[j].pError[k].pos;
  1077. perror[k].q = srcanim->ikrule[j].pError[k].q;
  1078. }
  1079. #endif
  1080. #if 1
  1081. // skip writting the header if there's no IK data
  1082. for (k = 0; k < 6; k++)
  1083. {
  1084. if (srcanim->localhierarchy[j].localData.numanim[k]) break;
  1085. }
  1086. if (k == 6)
  1087. continue;
  1088. // compressed
  1089. pHierarchy->localanimindex = (pData - (byte*)pHierarchy);
  1090. mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pData;
  1091. pData += sizeof( *pCompressed );
  1092. for (k = 0; k < 6; k++)
  1093. {
  1094. pCompressed->scale[k] = srcanim->localhierarchy[j].localData.scale[k];
  1095. pCompressed->offset[k] = (pData - (byte*)pCompressed);
  1096. int size = srcanim->localhierarchy[j].localData.numanim[k] * sizeof( mstudioanimvalue_t );
  1097. memmove( pData, srcanim->localhierarchy[j].localData.anim[k], size );
  1098. pData += size;
  1099. }
  1100. ALIGN4( pData );
  1101. #endif
  1102. // AddToStringTable( pHierarchy, &pHierarchy->szattachmentindex, srcanim->ikrule[j].attachment );
  1103. }
  1104. return pData;
  1105. }
  1106. static byte *WriteAnimations( byte *pData, byte *pStart, studiohdr_t *phdr )
  1107. {
  1108. int i, j;
  1109. mstudioanimdesc_t *panimdesc;
  1110. // save animations
  1111. panimdesc = (mstudioanimdesc_t *)pData;
  1112. if( phdr )
  1113. {
  1114. phdr->numlocalanim = g_numani;
  1115. phdr->localanimindex = (pData - pStart);
  1116. }
  1117. pData += g_numani * sizeof( *panimdesc );
  1118. ALIGN4( pData );
  1119. // ------------ ------- ------- : ------- (-------)
  1120. if( g_verbose )
  1121. {
  1122. printf(" animation x y ips angle\n");
  1123. }
  1124. for (i = 0; i < g_numani; i++)
  1125. {
  1126. s_animation_t *srcanim = g_panimation[ i ];
  1127. mstudioanimdesc_t *destanim = &panimdesc[i];
  1128. Assert( srcanim );
  1129. AddToStringTable( destanim, &destanim->sznameindex, srcanim->name );
  1130. destanim->baseptr = pStart - (byte *)destanim;
  1131. destanim->fps = srcanim->fps;
  1132. destanim->flags = srcanim->flags;
  1133. destanim->sectionframes = srcanim->sectionframes;
  1134. totalframes += srcanim->numframes;
  1135. totalseconds += srcanim->numframes / srcanim->fps;
  1136. destanim->numframes = srcanim->numframes;
  1137. // destanim->motiontype = srcanim->motiontype;
  1138. // destanim->motionbone = srcanim->motionbone;
  1139. // VectorCopy( srcanim->linearpos, destanim->linearpos );
  1140. j = srcanim->numpiecewisekeys - 1;
  1141. if (srcanim->piecewisemove[j].pos[0] != 0 || srcanim->piecewisemove[j].pos[1] != 0)
  1142. {
  1143. float t = (srcanim->numframes - 1) / srcanim->fps;
  1144. float r = 1 / t;
  1145. float a = atan2( srcanim->piecewisemove[j].pos[1], srcanim->piecewisemove[j].pos[0] ) * (180 / M_PI);
  1146. float d = sqrt( DotProduct( srcanim->piecewisemove[j].pos, srcanim->piecewisemove[j].pos ) );
  1147. if( g_verbose )
  1148. {
  1149. printf("%12s %7.2f %7.2f : %7.2f (%7.2f) %.1f\n", srcanim->name, srcanim->piecewisemove[j].pos[0], srcanim->piecewisemove[j].pos[1], d * r, a, t );
  1150. }
  1151. }
  1152. if (srcanim->numsections > 1)
  1153. {
  1154. destanim->sectionindex = pData - (byte *)destanim;
  1155. pData += srcanim->numsections * sizeof( mstudioanimsections_t );
  1156. }
  1157. // VectorCopy( srcanim->linearrot, destanim->linearrot );
  1158. // destanim->automoveposindex = srcanim->automoveposindex;
  1159. // destanim->automoveangleindex = srcanim->automoveangleindex;
  1160. // align all animation data to cache line boundaries
  1161. ALIGN16( pData );
  1162. ALIGN16( pBlockData );
  1163. if (pBlockStart)
  1164. {
  1165. // allocate the first block if needed
  1166. if (g_numanimblocks == 0)
  1167. {
  1168. g_numanimblocks = 1;
  1169. g_animblock[g_numanimblocks].start = pBlockData;
  1170. g_numanimblocks++;
  1171. }
  1172. }
  1173. if (!pBlockStart || (g_bonesaveframe.Count() == 0 && srcanim->numframes == 1))
  1174. {
  1175. // hack
  1176. srcanim->disableAnimblocks = true;
  1177. }
  1178. else if (g_bNoAnimblockStall)
  1179. {
  1180. srcanim->isFirstSectionLocal = true;
  1181. }
  1182. // block zero is relative to me
  1183. g_animblock[0].start = (byte *)(destanim);
  1184. byte *pAnimData = NULL;
  1185. byte *pIkData = NULL;
  1186. byte *pLocalHierarchy = NULL;
  1187. byte *pBlockEnd = pBlockData;
  1188. if (srcanim->disableAnimblocks || srcanim->isFirstSectionLocal)
  1189. {
  1190. destanim->animblock = 0;
  1191. pAnimData = pData;
  1192. WriteAnimationData( srcanim, destanim, pData, pBlockEnd );
  1193. pIkData = pData;
  1194. pLocalHierarchy = WriteIkErrors( srcanim, pIkData );
  1195. pData = WriteLocalHierarchy( srcanim, pLocalHierarchy );
  1196. }
  1197. else
  1198. {
  1199. pAnimData = pBlockEnd;
  1200. WriteAnimationData( srcanim, destanim, pData, pBlockEnd );
  1201. if ( destanim->sectionindex )
  1202. {
  1203. // if sections were written, don't move the data already written to the last block
  1204. pBlockData = pBlockEnd;
  1205. }
  1206. destanim->animblock = g_numanimblocks-1;
  1207. pIkData = pBlockEnd;
  1208. pLocalHierarchy = WriteIkErrors( srcanim, pIkData );
  1209. pBlockEnd = WriteLocalHierarchy( srcanim, pLocalHierarchy );
  1210. }
  1211. // printf("%d %x %x %x %s : %d\n", g_numanimblocks - 1, g_animblock[g_numanimblocks-1].start, pBlockData, pBlockEnd, srcanim->name, srcanim->numsections );
  1212. if (pBlockData != pBlockEnd && pBlockEnd - g_animblock[g_numanimblocks-1].start > g_animblocksize)
  1213. {
  1214. g_animblock[g_numanimblocks-1].end = pBlockData;
  1215. g_animblock[g_numanimblocks].start = pBlockData;
  1216. g_numanimblocks++;
  1217. destanim->animblock = g_numanimblocks-1;
  1218. }
  1219. destanim->animindex = pAnimData - g_animblock[destanim->animblock].start;
  1220. if ( srcanim->numikrules )
  1221. {
  1222. destanim->numikrules = srcanim->numikrules;
  1223. if (destanim->animblock == 0)
  1224. {
  1225. destanim->ikruleindex = pIkData - g_animblock[destanim->animblock].start;
  1226. }
  1227. else
  1228. {
  1229. destanim->animblockikruleindex = pIkData - g_animblock[destanim->animblock].start;
  1230. }
  1231. }
  1232. if ( srcanim->numlocalhierarchy )
  1233. {
  1234. destanim->numlocalhierarchy = srcanim->numlocalhierarchy;
  1235. destanim->localhierarchyindex = pLocalHierarchy - g_animblock[destanim->animblock].start;
  1236. }
  1237. if (g_numanimblocks)
  1238. {
  1239. g_animblock[g_numanimblocks-1].end = pBlockEnd;
  1240. pBlockData = pBlockEnd;
  1241. }
  1242. // printf("%s : %d:%d\n", srcanim->name, destanim->animblock, destanim->animindex );
  1243. // printf("raw bone data %d : %s\n", (byte *)destanimvalue - pData, srcanim->name);
  1244. }
  1245. if( !g_quiet )
  1246. {
  1247. /*
  1248. for (i = 0; i < g_numanimblocks; i++)
  1249. {
  1250. printf("%2d (%3d:%3d): %d\n", i, g_animblock[i].iStartAnim, g_animblock[i].iEndAnim, g_animblock[i].end - g_animblock[i].start );
  1251. }
  1252. */
  1253. }
  1254. if( !g_quiet )
  1255. {
  1256. /*
  1257. printf("raw anim data %d : %d\n", rawanimbytes, animboneframes );
  1258. printf("pos %d %d %d %d\n", numPos[0], numPos[1], numPos[2], numPos[3] );
  1259. printf("axis %d %d %d %d : %d\n", numAxis[0], numAxis[1], numAxis[2], numAxis[3], useRaw );
  1260. */
  1261. }
  1262. // write movement keys
  1263. for (i = 0; i < g_numani; i++)
  1264. {
  1265. s_animation_t *anim = g_panimation[ i ];
  1266. // panimdesc[i].entrancevelocity = anim->entrancevelocity;
  1267. panimdesc[i].nummovements = anim->numpiecewisekeys;
  1268. if (panimdesc[i].nummovements)
  1269. {
  1270. panimdesc[i].movementindex = pData - (byte*)&panimdesc[i];
  1271. mstudiomovement_t *pmove = (mstudiomovement_t *)pData;
  1272. pData += panimdesc[i].nummovements * sizeof( *pmove );
  1273. ALIGN4( pData );
  1274. for (j = 0; j < panimdesc[i].nummovements; j++)
  1275. {
  1276. pmove[j].endframe = anim->piecewisemove[j].endframe;
  1277. pmove[j].motionflags = anim->piecewisemove[j].flags;
  1278. pmove[j].v0 = anim->piecewisemove[j].v0;
  1279. pmove[j].v1 = anim->piecewisemove[j].v1;
  1280. pmove[j].angle = RAD2DEG( anim->piecewisemove[j].rot[2] );
  1281. VectorCopy( anim->piecewisemove[j].vector, pmove[j].vector );
  1282. VectorCopy( anim->piecewisemove[j].pos, pmove[j].position );
  1283. }
  1284. }
  1285. }
  1286. // only write zero frames if the animation data is demand loaded
  1287. if (!pBlockStart)
  1288. return pData;
  1289. // calculate what bones should be have zero frame saved out
  1290. if (g_bonesaveframe.Count() == 0)
  1291. {
  1292. for (j = 0; j < g_numbones; j++)
  1293. {
  1294. if ((g_bonetable[j].parent == -1) || (g_bonetable[j].posrange.Length() > 2.0))
  1295. {
  1296. g_bonetable[j].flags |= BONE_HAS_SAVEFRAME_POS;
  1297. }
  1298. g_bonetable[j].flags |= BONE_HAS_SAVEFRAME_ROT;
  1299. if ((!g_quiet) && (g_bonetable[j].flags & (BONE_HAS_SAVEFRAME_POS | BONE_HAS_SAVEFRAME_ROT)))
  1300. {
  1301. printf("$BoneSaveFrame \"%s\"", g_bonetable[j].name );
  1302. if (g_bonetable[j].flags & BONE_HAS_SAVEFRAME_POS)
  1303. printf(" position" );
  1304. if (g_bonetable[j].flags & BONE_HAS_SAVEFRAME_ROT)
  1305. printf(" rotation" );
  1306. printf("\n");
  1307. }
  1308. }
  1309. }
  1310. else
  1311. {
  1312. for (i = 0; i < g_bonesaveframe.Count(); i++)
  1313. {
  1314. j = findGlobalBone( g_bonesaveframe[i].name );
  1315. if (j != -1)
  1316. {
  1317. if (g_bonesaveframe[i].bSavePos)
  1318. {
  1319. g_bonetable[j].flags |= BONE_HAS_SAVEFRAME_POS;
  1320. }
  1321. if (g_bonesaveframe[i].bSaveRot)
  1322. {
  1323. g_bonetable[j].flags |= BONE_HAS_SAVEFRAME_ROT;
  1324. }
  1325. }
  1326. }
  1327. }
  1328. for (j = 0; j < g_numbones; j++)
  1329. {
  1330. phdr->pBone(j)->flags |= g_bonetable[j].flags;
  1331. }
  1332. ALIGN4( pData );
  1333. // write zero frames
  1334. for (i = 0; i < g_numani; i++)
  1335. {
  1336. s_animation_t *anim = g_panimation[ i ];
  1337. if (panimdesc[i].animblock != 0)
  1338. {
  1339. panimdesc[i].zeroframeindex = pData - (byte *)&panimdesc[i];
  1340. int k = min( panimdesc[i].numframes - 1, 9 );
  1341. if (panimdesc[i].flags & STUDIO_LOOPING)
  1342. {
  1343. k = min( (panimdesc[i].numframes - 1) / 2, k );
  1344. }
  1345. panimdesc[i].zeroframespan = k;
  1346. if (k > 2)
  1347. {
  1348. panimdesc[i].zeroframecount = min( (panimdesc[i].numframes - 1) / panimdesc[i].zeroframespan, 3 ); // save frames 0..24 frames
  1349. }
  1350. if (panimdesc[i].zeroframecount < 1)
  1351. panimdesc[i].zeroframecount = 1;
  1352. for (j = 0; j < g_numbones; j++)
  1353. {
  1354. if (g_bonetable[j].flags & BONE_HAS_SAVEFRAME_POS)
  1355. {
  1356. for (int n = 0; n < panimdesc[i].zeroframecount; n++)
  1357. {
  1358. *(Vector48 *)pData = anim->sanim[panimdesc[i].zeroframespan*n][j].pos;
  1359. pData += sizeof( Vector48 );
  1360. }
  1361. }
  1362. if (g_bonetable[j].flags & BONE_HAS_SAVEFRAME_ROT)
  1363. {
  1364. for (int n = 0; n < panimdesc[i].zeroframecount; n++)
  1365. {
  1366. Quaternion q;
  1367. AngleQuaternion( anim->sanim[panimdesc[i].zeroframespan*n][j].rot, q );
  1368. *((Quaternion64 *)pData) = q;
  1369. pData += sizeof( Quaternion64 );
  1370. }
  1371. }
  1372. }
  1373. }
  1374. }
  1375. ALIGN4( pData );
  1376. return pData;
  1377. }
  1378. static void WriteTextures( studiohdr_t *phdr )
  1379. {
  1380. int i, j;
  1381. short *pref;
  1382. // save texture info
  1383. mstudiotexture_t *ptexture = (mstudiotexture_t *)pData;
  1384. phdr->numtextures = g_nummaterials;
  1385. phdr->textureindex = pData - pStart;
  1386. pData += g_nummaterials * sizeof( mstudiotexture_t );
  1387. for (i = 0; i < g_nummaterials; i++)
  1388. {
  1389. j = g_material[i];
  1390. AddToStringTable( &ptexture[i], &ptexture[i].sznameindex, g_texture[j].name );
  1391. }
  1392. ALIGN4( pData );
  1393. int *cdtextureoffset = (int *)pData;
  1394. phdr->numcdtextures = numcdtextures;
  1395. phdr->cdtextureindex = pData - pStart;
  1396. pData += numcdtextures * sizeof( int );
  1397. for (i = 0; i < numcdtextures; i++)
  1398. {
  1399. AddToStringTable( phdr, &cdtextureoffset[i], cdtextures[i] );
  1400. }
  1401. ALIGN4( pData );
  1402. // save texture directory info
  1403. phdr->skinindex = (pData - pStart);
  1404. phdr->numskinref = g_numskinref;
  1405. phdr->numskinfamilies = g_numskinfamilies;
  1406. pref = (short *)pData;
  1407. for (i = 0; i < phdr->numskinfamilies; i++)
  1408. {
  1409. for (j = 0; j < phdr->numskinref; j++)
  1410. {
  1411. *pref = g_skinref[i][j];
  1412. pref++;
  1413. }
  1414. }
  1415. pData = (byte *)pref;
  1416. ALIGN4( pData );
  1417. }
  1418. //-----------------------------------------------------------------------------
  1419. // Write source bone transforms
  1420. //-----------------------------------------------------------------------------
  1421. static void WriteBoneTransforms( studiohdr2_t *phdr, mstudiobone_t *pBone )
  1422. {
  1423. matrix3x4_t identity;
  1424. SetIdentityMatrix( identity );
  1425. int nTransformCount = 0;
  1426. for (int i = 0; i < g_numbones; i++)
  1427. {
  1428. if ( g_bonetable[i].flags & BONE_ALWAYS_PROCEDURAL )
  1429. continue;
  1430. int nParent = g_bonetable[i].parent;
  1431. // Transformation is necessary if either you or your parent was realigned
  1432. if ( MatricesAreEqual( identity, g_bonetable[i].srcRealign ) &&
  1433. ( ( nParent < 0 ) || MatricesAreEqual( identity, g_bonetable[nParent].srcRealign ) ) )
  1434. continue;
  1435. ++nTransformCount;
  1436. }
  1437. // save bone transform info
  1438. mstudiosrcbonetransform_t *pSrcBoneTransform = (mstudiosrcbonetransform_t *)pData;
  1439. phdr->numsrcbonetransform = nTransformCount;
  1440. phdr->srcbonetransformindex = pData - pStart;
  1441. pData += nTransformCount * sizeof( mstudiosrcbonetransform_t );
  1442. int bt = 0;
  1443. for ( int i = 0; i < g_numbones; i++ )
  1444. {
  1445. if ( g_bonetable[i].flags & BONE_ALWAYS_PROCEDURAL )
  1446. continue;
  1447. int nParent = g_bonetable[i].parent;
  1448. if ( MatricesAreEqual( identity, g_bonetable[i].srcRealign ) &&
  1449. ( ( nParent < 0 ) || MatricesAreEqual( identity, g_bonetable[nParent].srcRealign ) ) )
  1450. continue;
  1451. // What's going on here?
  1452. // So, when we realign a bone, we want to do it in a way so that the child bones
  1453. // have the same bone->world transform. If we take T as the src realignment transform
  1454. // for the parent, P is the parent to world, and C is the child to parent, we expect
  1455. // the child->world is constant after realignment:
  1456. // CtoW = P * C = ( P * T ) * ( T^-1 * C )
  1457. // therefore Cnew = ( T^-1 * C )
  1458. if ( nParent >= 0 )
  1459. {
  1460. MatrixInvert( g_bonetable[nParent].srcRealign, pSrcBoneTransform[bt].pretransform );
  1461. }
  1462. else
  1463. {
  1464. SetIdentityMatrix( pSrcBoneTransform[bt].pretransform );
  1465. }
  1466. MatrixCopy( g_bonetable[i].srcRealign, pSrcBoneTransform[bt].posttransform );
  1467. AddToStringTable( &pSrcBoneTransform[bt], &pSrcBoneTransform[bt].sznameindex, g_bonetable[i].name );
  1468. ++bt;
  1469. }
  1470. ALIGN4( pData );
  1471. if (g_numbones > 1)
  1472. {
  1473. // write second bone table
  1474. phdr->linearboneindex = pData - (byte *)phdr;
  1475. mstudiolinearbone_t *pLinearBone = (mstudiolinearbone_t *)pData;
  1476. pData += sizeof( *pLinearBone );
  1477. pLinearBone->numbones = g_numbones;
  1478. #define WRITE_BONE_BLOCK( type, srcfield, dest, destindex ) \
  1479. type *##dest = (type *)pData; \
  1480. pLinearBone->##destindex = pData - (byte *)pLinearBone; \
  1481. pData += g_numbones * sizeof( *##dest ); \
  1482. ALIGN4( pData ); \
  1483. for ( int i = 0; i < g_numbones; i++) \
  1484. dest##[i] = pBone[i].##srcfield;
  1485. WRITE_BONE_BLOCK( int, flags, pFlags, flagsindex );
  1486. WRITE_BONE_BLOCK( int, parent, pParent, parentindex );
  1487. WRITE_BONE_BLOCK( Vector, pos, pPos, posindex );
  1488. WRITE_BONE_BLOCK( Quaternion, quat, pQuat, quatindex );
  1489. WRITE_BONE_BLOCK( RadianEuler, rot, pRot, rotindex );
  1490. WRITE_BONE_BLOCK( matrix3x4_t, poseToBone, pPoseToBone, posetoboneindex );
  1491. WRITE_BONE_BLOCK( Vector, posscale, pPoseScale, posscaleindex );
  1492. WRITE_BONE_BLOCK( Vector, rotscale, pRotScale, rotscaleindex );
  1493. WRITE_BONE_BLOCK( Quaternion, qAlignment, pQAlignment, qalignmentindex );
  1494. }
  1495. }
  1496. //-----------------------------------------------------------------------------
  1497. // Write the bone flex drivers
  1498. //-----------------------------------------------------------------------------
  1499. static void WriteBoneFlexDrivers( studiohdr2_t *pStudioHdr2 )
  1500. {
  1501. ALIGN4( pData );
  1502. pStudioHdr2->m_nBoneFlexDriverCount = 0;
  1503. pStudioHdr2->m_nBoneFlexDriverIndex = 0;
  1504. CDmeBoneFlexDriverList *pDmeBoneFlexDriverList = GetElement< CDmeBoneFlexDriverList >( g_hDmeBoneFlexDriverList );
  1505. if ( !pDmeBoneFlexDriverList )
  1506. return;
  1507. const int nBoneFlexDriverCount = pDmeBoneFlexDriverList->m_eBoneFlexDriverList.Count();
  1508. if ( nBoneFlexDriverCount <= 0 )
  1509. return;
  1510. mstudioboneflexdriver_t *pBoneFlexDriver = (mstudioboneflexdriver_t *)pData;
  1511. pStudioHdr2->m_nBoneFlexDriverCount = nBoneFlexDriverCount;
  1512. pStudioHdr2->m_nBoneFlexDriverIndex = pData - (byte *)pStudioHdr2;
  1513. pData += nBoneFlexDriverCount * sizeof( mstudioboneflexdriver_t );
  1514. ALIGN4( pData );
  1515. for ( int i = 0; i < nBoneFlexDriverCount; ++i )
  1516. {
  1517. CDmeBoneFlexDriver *pDmeBoneFlexDriver = pDmeBoneFlexDriverList->m_eBoneFlexDriverList[i];
  1518. Assert( pDmeBoneFlexDriver );
  1519. Assert( pDmeBoneFlexDriver->m_eControlList.Count() > 0 );
  1520. Assert( pDmeBoneFlexDriver->GetValue< int >( "__boneIndex", -1 ) >= 0 );
  1521. pBoneFlexDriver->m_nBoneIndex = pDmeBoneFlexDriver->GetValue< int >( "__boneIndex", 0 );
  1522. pBoneFlexDriver->m_nControlCount = pDmeBoneFlexDriver->m_eControlList.Count();
  1523. pBoneFlexDriver->m_nControlIndex = pData - (byte *)pBoneFlexDriver;
  1524. mstudioboneflexdrivercontrol_t *pControl = reinterpret_cast< mstudioboneflexdrivercontrol_t * >( pData );
  1525. for ( int j = 0; j < pBoneFlexDriver->m_nControlCount; ++j )
  1526. {
  1527. CDmeBoneFlexDriverControl *pDmeControl = pDmeBoneFlexDriver->m_eControlList[j];
  1528. Assert( pDmeControl );
  1529. Assert( pDmeControl->GetValue< int >( "__flexControlIndex", -1 ) >= 0 );
  1530. Assert( pDmeControl->m_nBoneComponent >= STUDIO_BONE_FLEX_TX );
  1531. Assert( pDmeControl->m_nBoneComponent <= STUDIO_BONE_FLEX_TZ );
  1532. pControl[j].m_nFlexControllerIndex = pDmeControl->GetValue< int >( "__flexControlIndex", 0 );
  1533. pControl[j].m_nBoneComponent = pDmeControl->m_nBoneComponent;
  1534. pControl[j].m_flMin = pDmeControl->m_flMin;
  1535. pControl[j].m_flMax = pDmeControl->m_flMax;
  1536. }
  1537. pData += pBoneFlexDriver->m_nControlCount * sizeof( mstudioboneflexdrivercontrol_t );
  1538. ALIGN4( pData );
  1539. ++pBoneFlexDriver;
  1540. }
  1541. }
  1542. //-----------------------------------------------------------------------------
  1543. // Write the processed vertices
  1544. //-----------------------------------------------------------------------------
  1545. static void WriteVertices( studiohdr_t *phdr )
  1546. {
  1547. char fileName[MAX_PATH];
  1548. byte *pStart;
  1549. byte *pData;
  1550. int i;
  1551. int j;
  1552. int k;
  1553. int cur;
  1554. if (!g_nummodelsbeforeLOD)
  1555. return;
  1556. V_strcpy_safe( fileName, gamedir );
  1557. // if( *g_pPlatformName )
  1558. // {
  1559. // strcat( fileName, "platform_" );
  1560. // strcat( fileName, g_pPlatformName );
  1561. // strcat( fileName, "/" );
  1562. // }
  1563. V_strcat_safe( fileName, "models/" );
  1564. V_strcat_safe( fileName, outname );
  1565. Q_StripExtension( fileName, fileName, sizeof( fileName ) );
  1566. V_strcat_safe( fileName, ".vvd" );
  1567. if ( !g_quiet )
  1568. {
  1569. printf ("---------------------\n");
  1570. printf ("writing %s:\n", fileName);
  1571. }
  1572. pStart = (byte *)kalloc( 1, FILEBUFFER );
  1573. pData = pStart;
  1574. vertexFileHeader_t *fileHeader = (vertexFileHeader_t *)pData;
  1575. pData += sizeof(vertexFileHeader_t);
  1576. fileHeader->id = MODEL_VERTEX_FILE_ID;
  1577. fileHeader->version = MODEL_VERTEX_FILE_VERSION;
  1578. fileHeader->checksum = phdr->checksum;
  1579. // data has no fixes and requires no fixes
  1580. fileHeader->numFixups = 0;
  1581. fileHeader->fixupTableStart = 0;
  1582. // unfinalized during first pass, fixed during second pass
  1583. // data can be considered as single lod at lod 0
  1584. fileHeader->numLODs = 1;
  1585. fileHeader->numLODVertexes[0] = 0;
  1586. // store vertexes grouped by mesh order
  1587. ALIGN16( pData );
  1588. fileHeader->vertexDataStart = pData-pStart;
  1589. for (i = 0; i < g_nummodelsbeforeLOD; i++)
  1590. {
  1591. s_loddata_t *pLodData = g_model[i]->m_pLodData;
  1592. // skip blank empty model
  1593. if (!pLodData)
  1594. continue;
  1595. // save vertices
  1596. ALIGN16( pData );
  1597. cur = (int)pData;
  1598. mstudiovertex_t *pVert = (mstudiovertex_t *)pData;
  1599. pData += pLodData->numvertices * sizeof( mstudiovertex_t );
  1600. for (j = 0; j < pLodData->numvertices; j++)
  1601. {
  1602. // printf( "saving bone weight %d for model %d at 0x%p\n",
  1603. // j, i, &pbone[j] );
  1604. const s_vertexinfo_t &lodVertex = pLodData->vertex[j];
  1605. VectorCopy( lodVertex.position, pVert[j].m_vecPosition );
  1606. VectorCopy( lodVertex.normal, pVert[j].m_vecNormal );
  1607. Vector2DCopy( lodVertex.texcoord, pVert[j].m_vecTexCoord );
  1608. mstudioboneweight_t *pBoneWeight = &pVert[j].m_BoneWeights;
  1609. memset( pBoneWeight, 0, sizeof( mstudioboneweight_t ) );
  1610. pBoneWeight->numbones = lodVertex.boneweight.numbones;
  1611. for (k = 0; k < pBoneWeight->numbones; k++)
  1612. {
  1613. pBoneWeight->bone[k] = lodVertex.boneweight.bone[k];
  1614. pBoneWeight->weight[k] = lodVertex.boneweight.weight[k];
  1615. }
  1616. }
  1617. fileHeader->numLODVertexes[0] += pLodData->numvertices;
  1618. if (!g_quiet)
  1619. {
  1620. printf( "vertices %7d bytes (%d vertices)\n", (int)(pData - cur), pLodData->numvertices );
  1621. }
  1622. }
  1623. // store tangents grouped by mesh order
  1624. ALIGN4( pData );
  1625. fileHeader->tangentDataStart = pData-pStart;
  1626. for (i = 0; i < g_nummodelsbeforeLOD; i++)
  1627. {
  1628. s_loddata_t *pLodData = g_model[i]->m_pLodData;
  1629. // skip blank empty model
  1630. if (!pLodData)
  1631. continue;
  1632. // save tangent space S
  1633. ALIGN4( pData );
  1634. cur = (int)pData;
  1635. Vector4D *ptangents = (Vector4D *)pData;
  1636. pData += pLodData->numvertices * sizeof( Vector4D );
  1637. for (j = 0; j < pLodData->numvertices; j++)
  1638. {
  1639. Vector4DCopy( pLodData->vertex[j].tangentS, ptangents[j] );
  1640. #ifdef _DEBUG
  1641. float w = ptangents[j].w;
  1642. Assert( w == 1.0f || w == -1.0f );
  1643. #endif
  1644. }
  1645. if (!g_quiet)
  1646. {
  1647. printf( "tangents %7d bytes (%d vertices)\n", (int)(pData - cur), pLodData->numvertices );
  1648. }
  1649. }
  1650. if (!g_quiet)
  1651. {
  1652. printf( "total %7d bytes\n", pData - pStart );
  1653. }
  1654. // fileHeader->length = pData - pStart;
  1655. {
  1656. CP4AutoEditAddFile autop4( fileName, "binary" );
  1657. SaveFile( fileName, pStart, pData - pStart );
  1658. }
  1659. }
  1660. //-----------------------------------------------------------------------------
  1661. // Computes the maximum absolute value of any component of all vertex animation
  1662. // pos (x,y,z) normal (x,y,z) or wrinkle
  1663. //
  1664. // Returns the fixed point scale and also sets appropriate values & flags in
  1665. // passed studiohdr_t
  1666. //-----------------------------------------------------------------------------
  1667. float ComputeVertAnimFixedPointScale( studiohdr_t *pStudioHdr )
  1668. {
  1669. float flVertAnimRange = 0.0f;
  1670. for ( int j = 0; j < g_numflexkeys; ++j )
  1671. {
  1672. if ( g_flexkey[j].numvanims <= 0 )
  1673. continue;
  1674. const bool bWrinkleVAnim = ( g_flexkey[j].vanimtype == STUDIO_VERT_ANIM_WRINKLE );
  1675. s_vertanim_t *pVertAnim = g_flexkey[j].vanim;
  1676. for ( int k = 0; k < g_flexkey[j].numvanims; ++k )
  1677. {
  1678. if ( fabs( pVertAnim->pos.x ) > flVertAnimRange )
  1679. {
  1680. flVertAnimRange = fabs( pVertAnim->pos.x );
  1681. }
  1682. if ( fabs( pVertAnim->pos.y ) > flVertAnimRange )
  1683. {
  1684. flVertAnimRange = fabs( pVertAnim->pos.y );
  1685. }
  1686. if ( fabs( pVertAnim->pos.z ) > flVertAnimRange )
  1687. {
  1688. flVertAnimRange = fabs( pVertAnim->pos.z );
  1689. }
  1690. if ( fabs( pVertAnim->normal.x ) > flVertAnimRange )
  1691. {
  1692. flVertAnimRange = fabs( pVertAnim->normal.x );
  1693. }
  1694. if ( fabs( pVertAnim->normal.y ) > flVertAnimRange )
  1695. {
  1696. flVertAnimRange = fabs( pVertAnim->normal.y );
  1697. }
  1698. if ( fabs( pVertAnim->normal.z ) > flVertAnimRange )
  1699. {
  1700. flVertAnimRange = fabs( pVertAnim->normal.z );
  1701. }
  1702. if ( bWrinkleVAnim )
  1703. {
  1704. if ( fabs( pVertAnim->wrinkle ) > flVertAnimRange )
  1705. {
  1706. flVertAnimRange = fabs( pVertAnim->wrinkle );
  1707. }
  1708. }
  1709. pVertAnim++;
  1710. }
  1711. }
  1712. // Legacy value
  1713. float flVertAnimFixedPointScale = 1.0 / 4096.0f;
  1714. if ( flVertAnimRange > 0.0f )
  1715. {
  1716. if ( flVertAnimRange > 32767 )
  1717. {
  1718. MdlWarning( "Flex value too large: %.2f, Max: 32767\n", flVertAnimRange );
  1719. flVertAnimFixedPointScale = 1.0f;
  1720. }
  1721. else
  1722. {
  1723. const float flTmpScale = flVertAnimRange / 32767.0f;
  1724. if ( flTmpScale > flVertAnimFixedPointScale )
  1725. {
  1726. flVertAnimFixedPointScale = flTmpScale;
  1727. }
  1728. }
  1729. }
  1730. if ( flVertAnimFixedPointScale != 1.0f / 4096.0f )
  1731. {
  1732. pStudioHdr->flags |= STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE;
  1733. pStudioHdr->flVertAnimFixedPointScale = flVertAnimFixedPointScale;
  1734. }
  1735. return flVertAnimFixedPointScale;
  1736. }
  1737. static void WriteModel( studiohdr_t *phdr )
  1738. {
  1739. int i, j, k, m;
  1740. mstudiobodyparts_t *pbodypart;
  1741. mstudiomodel_t *pmodel;
  1742. s_source_t *psource;
  1743. mstudiovertanim_t *pvertanim;
  1744. s_vertanim_t *pvanim;
  1745. int cur = (int)pData;
  1746. // vertex data is written to external file, offsets kept internal
  1747. // track expected external base to store proper offsets
  1748. byte *externalVertexIndex = 0;
  1749. byte *externalTangentsIndex = 0;
  1750. // write bodypart info
  1751. pbodypart = (mstudiobodyparts_t *)pData;
  1752. phdr->numbodyparts = g_numbodyparts;
  1753. phdr->bodypartindex = pData - pStart;
  1754. pData += g_numbodyparts * sizeof( mstudiobodyparts_t );
  1755. pmodel = (mstudiomodel_t *)pData;
  1756. pData += g_nummodelsbeforeLOD * sizeof( mstudiomodel_t );
  1757. for (i = 0, j = 0; i < g_numbodyparts; i++)
  1758. {
  1759. AddToStringTable( &pbodypart[i], &pbodypart[i].sznameindex, g_bodypart[i].name );
  1760. pbodypart[i].nummodels = g_bodypart[i].nummodels;
  1761. pbodypart[i].base = g_bodypart[i].base;
  1762. pbodypart[i].modelindex = ((byte *)&pmodel[j]) - (byte *)&pbodypart[i];
  1763. j += g_bodypart[i].nummodels;
  1764. }
  1765. ALIGN4( pData );
  1766. // write global flex names
  1767. mstudioflexdesc_t *pflexdesc = (mstudioflexdesc_t *)pData;
  1768. phdr->numflexdesc = g_numflexdesc;
  1769. phdr->flexdescindex = pData - pStart;
  1770. pData += g_numflexdesc * sizeof( mstudioflexdesc_t );
  1771. ALIGN4( pData );
  1772. for (j = 0; j < g_numflexdesc; j++)
  1773. {
  1774. // printf("%d %s\n", j, g_flexdesc[j].FACS );
  1775. AddToStringTable( pflexdesc, &pflexdesc->szFACSindex, g_flexdesc[j].FACS );
  1776. pflexdesc++;
  1777. }
  1778. // write global flex controllers
  1779. mstudioflexcontroller_t *pflexcontroller = (mstudioflexcontroller_t *)pData;
  1780. phdr->numflexcontrollers = g_numflexcontrollers;
  1781. phdr->flexcontrollerindex = pData - pStart;
  1782. pData += g_numflexcontrollers * sizeof( mstudioflexcontroller_t );
  1783. ALIGN4( pData );
  1784. for (j = 0; j < g_numflexcontrollers; j++)
  1785. {
  1786. AddToStringTable( pflexcontroller, &pflexcontroller->sznameindex, g_flexcontroller[j].name );
  1787. AddToStringTable( pflexcontroller, &pflexcontroller->sztypeindex, g_flexcontroller[j].type );
  1788. pflexcontroller->min = g_flexcontroller[j].min;
  1789. pflexcontroller->max = g_flexcontroller[j].max;
  1790. pflexcontroller->localToGlobal = -1;
  1791. pflexcontroller++;
  1792. }
  1793. // write flex rules
  1794. mstudioflexrule_t *pflexrule = (mstudioflexrule_t *)pData;
  1795. phdr->numflexrules = g_numflexrules;
  1796. phdr->flexruleindex = pData - pStart;
  1797. pData += g_numflexrules * sizeof( mstudioflexrule_t );
  1798. ALIGN4( pData );
  1799. for (j = 0; j < g_numflexrules; j++)
  1800. {
  1801. pflexrule->flex = g_flexrule[j].flex;
  1802. pflexrule->numops = g_flexrule[j].numops;
  1803. pflexrule->opindex = (pData - (byte *)pflexrule);
  1804. mstudioflexop_t *pflexop = (mstudioflexop_t *)pData;
  1805. for (i = 0; i < pflexrule->numops; i++)
  1806. {
  1807. pflexop[i].op = g_flexrule[j].op[i].op;
  1808. pflexop[i].d.index = g_flexrule[j].op[i].d.index;
  1809. }
  1810. pData += sizeof( mstudioflexop_t ) * pflexrule->numops;
  1811. ALIGN4( pData );
  1812. pflexrule++;
  1813. }
  1814. // write global flex controller information
  1815. mstudioflexcontrollerui_t *pFlexControllerUI = (mstudioflexcontrollerui_t *)pData;
  1816. phdr->numflexcontrollerui = 0;
  1817. phdr->flexcontrolleruiindex = pData - pStart;
  1818. // Loop through all defined controllers and create a UI structure for them
  1819. // All actual controllers will be defined as a member of some ui structure
  1820. // and all actual controllers can only be a member of one ui structure
  1821. bool *pControllerHandled = ( bool * )_alloca( g_numflexcontrollers * sizeof( bool ) );
  1822. memset( pControllerHandled, 0, g_numflexcontrollers * sizeof( bool ) );
  1823. for ( j = 0; j < g_numflexcontrollers; ++j )
  1824. {
  1825. // Don't handle controls twice
  1826. if ( pControllerHandled[ j ] )
  1827. continue;
  1828. const s_flexcontroller_t &flexcontroller = g_flexcontroller[ j ];
  1829. bool found = false;
  1830. // See if this controller is in the remap table
  1831. for ( k = 0; k < g_FlexControllerRemap.Count(); ++k )
  1832. {
  1833. s_flexcontrollerremap_t &remap = g_FlexControllerRemap[ k ];
  1834. if ( j == remap.m_Index || j == remap.m_LeftIndex || j == remap.m_RightIndex || j == remap.m_MultiIndex )
  1835. {
  1836. AddToStringTable( pFlexControllerUI, &pFlexControllerUI->sznameindex, remap.m_Name );
  1837. pFlexControllerUI->stereo = remap.m_bIsStereo;
  1838. if ( pFlexControllerUI->stereo )
  1839. {
  1840. Assert( !pControllerHandled[ remap.m_LeftIndex ] );
  1841. pFlexControllerUI->szindex0 = (
  1842. phdr->flexcontrollerindex - int( pData - pStart ) +
  1843. remap.m_LeftIndex * sizeof( mstudioflexcontroller_t ) );
  1844. pControllerHandled[ remap.m_LeftIndex ] = true;
  1845. Assert( !pControllerHandled[ remap.m_RightIndex ] );
  1846. pFlexControllerUI->szindex1 = (
  1847. phdr->flexcontrollerindex - int( pData - pStart ) +
  1848. remap.m_RightIndex * sizeof( mstudioflexcontroller_t ) );
  1849. pControllerHandled[ remap.m_RightIndex ] = true;
  1850. }
  1851. else
  1852. {
  1853. Assert( !pControllerHandled[ remap.m_Index ] );
  1854. pFlexControllerUI->szindex0 = (
  1855. phdr->flexcontrollerindex - int( pData - pStart ) +
  1856. remap.m_Index * sizeof( mstudioflexcontroller_t ) );
  1857. pControllerHandled[ remap.m_Index ] = true;
  1858. pFlexControllerUI->szindex1 = ( 0 );
  1859. }
  1860. pFlexControllerUI->remaptype = remap.m_RemapType;
  1861. if ( pFlexControllerUI->remaptype == FLEXCONTROLLER_REMAP_NWAY || pFlexControllerUI->remaptype == FLEXCONTROLLER_REMAP_EYELID )
  1862. {
  1863. Assert( remap.m_MultiIndex != -1 );
  1864. Assert( !pControllerHandled[ remap.m_MultiIndex ] );
  1865. pFlexControllerUI->szindex2 = (
  1866. phdr->flexcontrollerindex - int( pData - pStart ) +
  1867. remap.m_MultiIndex * sizeof( mstudioflexcontroller_t ) );
  1868. pControllerHandled[ remap.m_MultiIndex ] = true;
  1869. }
  1870. else
  1871. {
  1872. pFlexControllerUI->szindex2 = 0;
  1873. }
  1874. found = true;
  1875. break;
  1876. }
  1877. }
  1878. if ( !found )
  1879. {
  1880. pFlexControllerUI->remaptype = FLEXCONTROLLER_REMAP_PASSTHRU;
  1881. pFlexControllerUI->szindex2 = 0; // Unused in this case
  1882. if ( j < g_numflexcontrollers - 1 &&
  1883. StringAfterPrefixCaseSensitive( flexcontroller.name, "right_" ) &&
  1884. StringAfterPrefixCaseSensitive( g_flexcontroller[ j + 1 ].name, "left_" ) &&
  1885. !Q_strcmp( StringAfterPrefixCaseSensitive( flexcontroller.name, "right_" ), StringAfterPrefixCaseSensitive( g_flexcontroller[ j + 1 ].name, "left_" ) ) )
  1886. {
  1887. AddToStringTable( pFlexControllerUI, &pFlexControllerUI->sznameindex, flexcontroller.name + 6 );
  1888. pFlexControllerUI->stereo = true;
  1889. Assert( !pControllerHandled[ j + 1 ] );
  1890. pFlexControllerUI->szindex0 = (
  1891. phdr->flexcontrollerindex - int( pData - pStart ) +
  1892. ( j + 1 ) * sizeof( mstudioflexcontroller_t ) );
  1893. pControllerHandled[ j + 1 ] = true;
  1894. Assert( !pControllerHandled[ j ] );
  1895. pFlexControllerUI->szindex1 = (
  1896. phdr->flexcontrollerindex - int( pData - pStart ) +
  1897. j * sizeof( mstudioflexcontroller_t ) );
  1898. pControllerHandled[ j ] = true;
  1899. }
  1900. else if ( j > 0 &&
  1901. StringAfterPrefixCaseSensitive( flexcontroller.name, "left_" ) &&
  1902. StringAfterPrefixCaseSensitive( g_flexcontroller[ j - 1 ].name, "right_" ) &&
  1903. !Q_strcmp( StringAfterPrefixCaseSensitive( flexcontroller.name, "left_" ), StringAfterPrefixCaseSensitive( g_flexcontroller[ j - 1 ].name, "right_" ) ) )
  1904. {
  1905. AddToStringTable( pFlexControllerUI, &pFlexControllerUI->sznameindex, flexcontroller.name + 5 );
  1906. pFlexControllerUI->stereo = true;
  1907. Assert( !pControllerHandled[ j ] );
  1908. pFlexControllerUI->szindex0 = (
  1909. phdr->flexcontrollerindex - int( pData - pStart ) +
  1910. j * sizeof( mstudioflexcontroller_t ) );
  1911. pControllerHandled[ j ] = true;
  1912. Assert( !pControllerHandled[ j - 1 ] );
  1913. pFlexControllerUI->szindex1 = (
  1914. phdr->flexcontrollerindex - int( pData - pStart ) +
  1915. ( j - 1 ) * sizeof( mstudioflexcontroller_t ) );
  1916. pControllerHandled[ j - 1 ] = true;
  1917. }
  1918. else
  1919. {
  1920. AddToStringTable( pFlexControllerUI, &pFlexControllerUI->sznameindex, flexcontroller.name );
  1921. pFlexControllerUI->stereo = false;
  1922. pFlexControllerUI->szindex0 = (
  1923. phdr->flexcontrollerindex - int( pData - pStart ) +
  1924. j * sizeof( mstudioflexcontroller_t ) );
  1925. pFlexControllerUI->szindex1 = 0; // Unused in this case
  1926. pControllerHandled[ j ] = true;
  1927. }
  1928. }
  1929. phdr->numflexcontrollerui++;
  1930. pData += sizeof( mstudioflexcontrollerui_t );
  1931. ++pFlexControllerUI;
  1932. }
  1933. ALIGN4( pData );
  1934. #ifdef _DEBUG
  1935. for ( j = 0; j < g_numflexcontrollers; ++j )
  1936. {
  1937. Assert( pControllerHandled[ j ] );
  1938. }
  1939. #endif // _DEBUG
  1940. // write ik chains
  1941. mstudioikchain_t *pikchain = (mstudioikchain_t *)pData;
  1942. phdr->numikchains = g_numikchains;
  1943. phdr->ikchainindex = pData - pStart;
  1944. pData += g_numikchains * sizeof( mstudioikchain_t );
  1945. ALIGN4( pData );
  1946. for (j = 0; j < g_numikchains; j++)
  1947. {
  1948. AddToStringTable( pikchain, &pikchain->sznameindex, g_ikchain[j].name );
  1949. pikchain->numlinks = g_ikchain[j].numlinks;
  1950. mstudioiklink_t *piklink = (mstudioiklink_t *)pData;
  1951. pikchain->linkindex = (pData - (byte *)pikchain);
  1952. pData += pikchain->numlinks * sizeof( mstudioiklink_t );
  1953. for (i = 0; i < pikchain->numlinks; i++)
  1954. {
  1955. piklink[i].bone = g_ikchain[j].link[i].bone;
  1956. piklink[i].kneeDir = g_ikchain[j].link[i].kneeDir;
  1957. }
  1958. pikchain++;
  1959. }
  1960. // save autoplay locks
  1961. mstudioiklock_t *piklock = (mstudioiklock_t *)pData;
  1962. phdr->numlocalikautoplaylocks = g_numikautoplaylocks;
  1963. phdr->localikautoplaylockindex = pData - pStart;
  1964. pData += g_numikautoplaylocks * sizeof( mstudioiklock_t );
  1965. ALIGN4( pData );
  1966. for (j = 0; j < g_numikautoplaylocks; j++)
  1967. {
  1968. piklock->chain = g_ikautoplaylock[j].chain;
  1969. piklock->flPosWeight = g_ikautoplaylock[j].flPosWeight;
  1970. piklock->flLocalQWeight = g_ikautoplaylock[j].flLocalQWeight;
  1971. piklock++;
  1972. }
  1973. // save mouth info
  1974. mstudiomouth_t *pmouth = (mstudiomouth_t *)pData;
  1975. phdr->nummouths = g_nummouths;
  1976. phdr->mouthindex = pData - pStart;
  1977. pData += g_nummouths * sizeof( mstudiomouth_t );
  1978. ALIGN4( pData );
  1979. for (i = 0; i < g_nummouths; i++) {
  1980. pmouth[i].bone = g_mouth[i].bone;
  1981. VectorCopy( g_mouth[i].forward, pmouth[i].forward );
  1982. pmouth[i].flexdesc = g_mouth[i].flexdesc;
  1983. }
  1984. // save pose parameters
  1985. mstudioposeparamdesc_t *ppose = (mstudioposeparamdesc_t *)pData;
  1986. phdr->numlocalposeparameters = g_numposeparameters;
  1987. phdr->localposeparamindex = pData - pStart;
  1988. pData += g_numposeparameters * sizeof( mstudioposeparamdesc_t );
  1989. ALIGN4( pData );
  1990. for (i = 0; i < g_numposeparameters; i++)
  1991. {
  1992. AddToStringTable( &ppose[i], &ppose[i].sznameindex, g_pose[i].name );
  1993. ppose[i].start = g_pose[i].min;
  1994. ppose[i].end = g_pose[i].max;
  1995. ppose[i].flags = g_pose[i].flags;
  1996. ppose[i].loop = g_pose[i].loop;
  1997. }
  1998. if( !g_quiet )
  1999. {
  2000. printf("ik/pose %7d bytes\n", (int)(pData - cur) );
  2001. }
  2002. cur = (int)pData;
  2003. const float flVertAnimFixedPointScale = ComputeVertAnimFixedPointScale( phdr );
  2004. // write model
  2005. for (i = 0; i < g_nummodelsbeforeLOD; i++)
  2006. {
  2007. int n = 0;
  2008. byte *pModelStart = (byte *)(&pmodel[i]);
  2009. strcpy( pmodel[i].name, g_model[i]->filename );
  2010. // AddToStringTable( &pmodel[i], &pmodel[i].sznameindex, g_model[i]->filename );
  2011. // pmodel[i].mrmbias = g_model[i]->mrmbias;
  2012. // pmodel[i].minresolution = g_model[i]->minresolution;
  2013. // pmodel[i].maxresolution = g_model[i]->maxresolution;
  2014. // save bbox info
  2015. psource = g_model[i]->source;
  2016. s_loddata_t *pLodData = g_model[i]->m_pLodData;
  2017. // save mesh info
  2018. if (pLodData)
  2019. {
  2020. pmodel[i].numvertices = pLodData->numvertices;
  2021. }
  2022. else
  2023. {
  2024. // empty model
  2025. pmodel[i].numvertices = 0;
  2026. }
  2027. if ( pmodel[i].numvertices >= MAXSTUDIOVERTS )
  2028. {
  2029. // We have to check this here so that we don't screw up decal
  2030. // vert caching in the runtime.
  2031. MdlError( "Too many verts in model. (%d verts, MAXSTUDIOVERTS==%d)\n",
  2032. pmodel[i].numvertices, ( int )MAXSTUDIOVERTS );
  2033. }
  2034. mstudiomesh_t *pmesh = (mstudiomesh_t *)pData;
  2035. pmodel[i].meshindex = (pData - pModelStart);
  2036. pData += psource->nummeshes * sizeof( mstudiomesh_t );
  2037. ALIGN4( pData );
  2038. pmodel[i].nummeshes = psource->nummeshes;
  2039. for (m = 0; m < pmodel[i].nummeshes; m++)
  2040. {
  2041. n = psource->meshindex[m];
  2042. pmesh[m].material = n;
  2043. pmesh[m].modelindex = (byte *)&pmodel[i] - (byte *)&pmesh[m];
  2044. pmesh[m].numvertices = pLodData->mesh[n].numvertices;
  2045. pmesh[m].vertexoffset = pLodData->mesh[n].vertexoffset;
  2046. }
  2047. // set expected base offsets to external data
  2048. ALIGN16( externalVertexIndex );
  2049. pmodel[i].vertexindex = (int)externalVertexIndex;
  2050. externalVertexIndex += pmodel[i].numvertices * sizeof(mstudiovertex_t);
  2051. // set expected base offsets to external data
  2052. ALIGN4( externalTangentsIndex );
  2053. pmodel[i].tangentsindex = (int)externalTangentsIndex;
  2054. externalTangentsIndex += pmodel[i].numvertices * sizeof( Vector4D );
  2055. cur = (int)pData;
  2056. // save eyeballs
  2057. mstudioeyeball_t *peyeball;
  2058. peyeball = (mstudioeyeball_t *)pData;
  2059. pmodel[i].numeyeballs = g_model[i]->numeyeballs;
  2060. pmodel[i].eyeballindex = pData - pModelStart;
  2061. pData += g_model[i]->numeyeballs * sizeof( mstudioeyeball_t );
  2062. ALIGN4( pData );
  2063. for (j = 0; j < g_model[i]->numeyeballs; j++)
  2064. {
  2065. k = g_model[i]->eyeball[j].mesh;
  2066. pmesh[k].materialtype = 1; // FIXME: tag custom material
  2067. pmesh[k].materialparam = j; // FIXME: tag custom material
  2068. peyeball[j].bone = g_model[i]->eyeball[j].bone;
  2069. VectorCopy( g_model[i]->eyeball[j].org, peyeball[j].org );
  2070. peyeball[j].zoffset = g_model[i]->eyeball[j].zoffset;
  2071. peyeball[j].radius = g_model[i]->eyeball[j].radius;
  2072. VectorCopy( g_model[i]->eyeball[j].up, peyeball[j].up );
  2073. VectorCopy( g_model[i]->eyeball[j].forward, peyeball[j].forward );
  2074. peyeball[j].iris_scale = g_model[i]->eyeball[j].iris_scale;
  2075. for (k = 0; k < 3; k++)
  2076. {
  2077. peyeball[j].upperflexdesc[k] = g_model[i]->eyeball[j].upperflexdesc[k];
  2078. peyeball[j].lowerflexdesc[k] = g_model[i]->eyeball[j].lowerflexdesc[k];
  2079. peyeball[j].uppertarget[k] = g_model[i]->eyeball[j].uppertarget[k];
  2080. peyeball[j].lowertarget[k] = g_model[i]->eyeball[j].lowertarget[k];
  2081. }
  2082. peyeball[j].upperlidflexdesc = g_model[i]->eyeball[j].upperlidflexdesc;
  2083. peyeball[j].lowerlidflexdesc = g_model[i]->eyeball[j].lowerlidflexdesc;
  2084. }
  2085. if ( !g_quiet )
  2086. {
  2087. printf("eyeballs %7d bytes (%d eyeballs)\n", (int)(pData - cur), g_model[i]->numeyeballs );
  2088. }
  2089. // move flexes into individual meshes
  2090. cur = (int)pData;
  2091. for (m = 0; m < pmodel[i].nummeshes; m++)
  2092. {
  2093. int numflexkeys[MAXSTUDIOFLEXKEYS];
  2094. pmesh[m].numflexes = 0;
  2095. // initialize array
  2096. for (j = 0; j < g_numflexkeys; j++)
  2097. {
  2098. numflexkeys[j] = 0;
  2099. }
  2100. // count flex instances per mesh
  2101. for (j = 0; j < g_numflexkeys; j++)
  2102. {
  2103. if (g_flexkey[j].imodel == i)
  2104. {
  2105. for (k = 0; k < g_flexkey[j].numvanims; k++)
  2106. {
  2107. n = g_flexkey[j].vanim[k].vertex - pmesh[m].vertexoffset;
  2108. if (n >= 0 && n < pmesh[m].numvertices)
  2109. {
  2110. if (numflexkeys[j]++ == 0)
  2111. {
  2112. pmesh[m].numflexes++;
  2113. }
  2114. }
  2115. }
  2116. }
  2117. }
  2118. if (pmesh[m].numflexes)
  2119. {
  2120. pmesh[m].flexindex = ( pData - (byte *)&pmesh[m] );
  2121. mstudioflex_t *pflex = (mstudioflex_t *)pData;
  2122. pData += pmesh[m].numflexes * sizeof( mstudioflex_t );
  2123. ALIGN4( pData );
  2124. for (j = 0; j < g_numflexkeys; j++)
  2125. {
  2126. if (!numflexkeys[j])
  2127. continue;
  2128. pflex->flexdesc = g_flexkey[j].flexdesc;
  2129. pflex->target0 = g_flexkey[j].target0;
  2130. pflex->target1 = g_flexkey[j].target1;
  2131. pflex->target2 = g_flexkey[j].target2;
  2132. pflex->target3 = g_flexkey[j].target3;
  2133. pflex->numverts = numflexkeys[j];
  2134. pflex->vertindex = (pData - (byte *)pflex);
  2135. pflex->flexpair = g_flexkey[j].flexpair;
  2136. pflex->vertanimtype = g_flexkey[j].vanimtype;
  2137. // printf("%d %d %s : %f %f %f %f\n", j, g_flexkey[j].flexdesc, g_flexdesc[g_flexkey[j].flexdesc].FACS, g_flexkey[j].target0, g_flexkey[j].target1, g_flexkey[j].target2, g_flexkey[j].target3 );
  2138. // if (j < 9) printf("%d %d %s : %d (%d) %f\n", j, g_flexkey[j].flexdesc, g_flexdesc[g_flexkey[j].flexdesc].FACS, g_flexkey[j].numvanims, pflex->numverts, g_flexkey[j].target );
  2139. // printf("%d %d : %d %f\n", j, g_flexkey[j].flexnum, g_flexkey[j].numvanims, g_flexkey[j].target );
  2140. pvanim = g_flexkey[j].vanim;
  2141. bool bWrinkleVAnim = ( pflex->vertanimtype == STUDIO_VERT_ANIM_WRINKLE );
  2142. int nVAnimDeltaSize = bWrinkleVAnim ? sizeof(mstudiovertanim_wrinkle_t) : sizeof(mstudiovertanim_t);
  2143. pvertanim = (mstudiovertanim_t *)pData;
  2144. pData += pflex->numverts * nVAnimDeltaSize;
  2145. ALIGN4( pData );
  2146. for ( k = 0; k < g_flexkey[j].numvanims; k++ )
  2147. {
  2148. n = g_flexkey[j].vanim[k].vertex - pmesh[m].vertexoffset;
  2149. if ( n >= 0 && n < pmesh[m].numvertices )
  2150. {
  2151. pvertanim->index = n;
  2152. pvertanim->speed = 255.0F*pvanim->speed;
  2153. pvertanim->side = 255.0F*pvanim->side;
  2154. pvertanim->SetDeltaFloat( pvanim->pos );
  2155. pvertanim->SetNDeltaFloat( pvanim->normal );
  2156. if ( bWrinkleVAnim )
  2157. {
  2158. ( (mstudiovertanim_wrinkle_t*)pvertanim )->SetWrinkleFixed( pvanim->wrinkle, flVertAnimFixedPointScale );
  2159. }
  2160. pvertanim = (mstudiovertanim_t*)( (byte*)pvertanim + nVAnimDeltaSize );
  2161. /*
  2162. if ((tmp - pvanim->pos).Length() > 0.1)
  2163. {
  2164. pvertanim->delta.x = pvanim->pos.x;
  2165. printf("%f %f %f : %f %f %f\n",
  2166. pvanim->pos[0], pvanim->pos[1], pvanim->pos[2],
  2167. tmp.x, tmp.y, tmp.z );
  2168. }
  2169. */
  2170. // if (j < 9) printf("%d %.2f %.2f %.2f\n", n, pvanim->pos[0], pvanim->pos[1], pvanim->pos[2] );
  2171. }
  2172. // printf("%d %.2f %.2f %.2f\n", pvanim->vertex, pvanim->pos[0], pvanim->pos[1], pvanim->pos[2] );
  2173. pvanim++;
  2174. }
  2175. pflex++;
  2176. }
  2177. }
  2178. }
  2179. if( !g_quiet )
  2180. {
  2181. printf("flexes %7d bytes (%d flexes)\n", (int)(pData - cur), g_numflexkeys );
  2182. }
  2183. cur = (int)pData;
  2184. }
  2185. ALIGN4( pData );
  2186. mstudiomodelgroup_t *pincludemodel = (mstudiomodelgroup_t *)pData;
  2187. phdr->numincludemodels = g_numincludemodels;
  2188. phdr->includemodelindex = pData - pStart;
  2189. pData += g_numincludemodels * sizeof( mstudiomodelgroup_t );
  2190. for (i = 0; i < g_numincludemodels; i++)
  2191. {
  2192. AddToStringTable( pincludemodel, &pincludemodel->sznameindex, g_includemodel[i].name );
  2193. pincludemodel++;
  2194. }
  2195. // save animblock group info
  2196. mstudioanimblock_t *panimblock = (mstudioanimblock_t *)pData;
  2197. phdr->numanimblocks = g_numanimblocks;
  2198. phdr->animblockindex = pData - pStart;
  2199. pData += phdr->numanimblocks * sizeof( mstudioanimblock_t );
  2200. ALIGN4( pData );
  2201. for (i = 1; i < g_numanimblocks; i++)
  2202. {
  2203. panimblock[i].datastart = g_animblock[i].start - pBlockStart;
  2204. panimblock[i].dataend = g_animblock[i].end - pBlockStart;
  2205. // printf("block %d : %x %x (%d)\n", i, panimblock[i].datastart, panimblock[i].dataend, panimblock[i].dataend - panimblock[i].datastart );
  2206. }
  2207. AddToStringTable( phdr, &phdr->szanimblocknameindex, g_animblockname );
  2208. }
  2209. static void AssignMeshIDs( studiohdr_t *pStudioHdr )
  2210. {
  2211. int i;
  2212. int j;
  2213. int m;
  2214. int numMeshes;
  2215. mstudiobodyparts_t *pStudioBodyPart;
  2216. mstudiomodel_t *pStudioModel;
  2217. mstudiomesh_t *pStudioMesh;
  2218. numMeshes = 0;
  2219. for (i=0; i<pStudioHdr->numbodyparts; i++)
  2220. {
  2221. pStudioBodyPart = pStudioHdr->pBodypart(i);
  2222. for (j=0; j<pStudioBodyPart->nummodels; j++)
  2223. {
  2224. pStudioModel = pStudioBodyPart->pModel(j);
  2225. for (m=0; m<pStudioModel->nummeshes; m++)
  2226. {
  2227. // get each mesh
  2228. pStudioMesh = pStudioModel->pMesh(m);
  2229. pStudioMesh->meshid = numMeshes + m;
  2230. }
  2231. numMeshes += pStudioModel->nummeshes;
  2232. }
  2233. }
  2234. }
  2235. void LoadMaterials( studiohdr_t *phdr )
  2236. {
  2237. int i, j;
  2238. // get index of each material
  2239. if( phdr->textureindex != 0 )
  2240. {
  2241. for( i = 0; i < phdr->numtextures; i++ )
  2242. {
  2243. char szPath[256];
  2244. IMaterial *pMaterial = NULL;
  2245. // search through all specified directories until a valid material is found
  2246. for( j = 0; j < phdr->numcdtextures && IsErrorMaterial( pMaterial ); j++ )
  2247. {
  2248. strcpy( szPath, phdr->pCdtexture( j ) );
  2249. strcat( szPath, phdr->pTexture( i )->pszName( ) );
  2250. pMaterial = g_pMaterialSystem->FindMaterial( szPath, TEXTURE_GROUP_OTHER, false );
  2251. }
  2252. if( IsErrorMaterial( pMaterial ) && !g_quiet )
  2253. {
  2254. // hack - if it isn't found, go through the motions of looking for it again
  2255. // so that the materialsystem will give an error.
  2256. for( j = 0; j < phdr->numcdtextures; j++ )
  2257. {
  2258. strcpy( szPath, phdr->pCdtexture( j ) );
  2259. strcat( szPath, phdr->pTexture( i )->pszName( ) );
  2260. g_pMaterialSystem->FindMaterial( szPath, TEXTURE_GROUP_OTHER, true );
  2261. }
  2262. }
  2263. phdr->pTexture( i )->material = pMaterial;
  2264. // FIXME: hack, needs proper client side material system interface
  2265. bool found = false;
  2266. IMaterialVar *clientShaderVar = pMaterial->FindVar( "$clientShader", &found, false );
  2267. if( found )
  2268. {
  2269. if (stricmp( clientShaderVar->GetStringValue(), "MouthShader") == 0)
  2270. {
  2271. phdr->pTexture( i )->flags = 1;
  2272. }
  2273. phdr->pTexture( i )->used = 1;
  2274. }
  2275. }
  2276. }
  2277. }
  2278. void WriteKeyValues( studiohdr_t *phdr, CUtlVector< char > *pKeyValue )
  2279. {
  2280. phdr->keyvalueindex = (pData - pStart);
  2281. phdr->keyvaluesize = KeyValueTextSize( pKeyValue );
  2282. if (phdr->keyvaluesize)
  2283. {
  2284. memcpy( pData, KeyValueText( pKeyValue ), phdr->keyvaluesize );
  2285. // Add space for a null terminator
  2286. pData[phdr->keyvaluesize] = 0;
  2287. ++phdr->keyvaluesize;
  2288. pData += phdr->keyvaluesize * sizeof( char );
  2289. }
  2290. ALIGN4( pData );
  2291. }
  2292. void WriteSeqKeyValues( mstudioseqdesc_t *pseqdesc, CUtlVector< char > *pKeyValue )
  2293. {
  2294. pseqdesc->keyvalueindex = (pData - (byte *)pseqdesc);
  2295. pseqdesc->keyvaluesize = KeyValueTextSize( pKeyValue );
  2296. if (pseqdesc->keyvaluesize)
  2297. {
  2298. memcpy( pData, KeyValueText( pKeyValue ), pseqdesc->keyvaluesize );
  2299. // Add space for a null terminator
  2300. pData[pseqdesc->keyvaluesize] = 0;
  2301. ++pseqdesc->keyvaluesize;
  2302. pData += pseqdesc->keyvaluesize * sizeof( char );
  2303. }
  2304. ALIGN4( pData );
  2305. }
  2306. void EnsureFileDirectoryExists( const char *pFilename )
  2307. {
  2308. char dirName[MAX_PATH];
  2309. Q_strncpy( dirName, pFilename, sizeof( dirName ) );
  2310. Q_FixSlashes( dirName );
  2311. char *pLastSlash = strrchr( dirName, CORRECT_PATH_SEPARATOR );
  2312. if ( pLastSlash )
  2313. {
  2314. *pLastSlash = 0;
  2315. if ( _access( dirName, 0 ) != 0 )
  2316. {
  2317. char cmdLine[512];
  2318. Q_snprintf( cmdLine, sizeof( cmdLine ), "md \"%s\"", dirName );
  2319. system( cmdLine );
  2320. }
  2321. }
  2322. }
  2323. void WriteModelFiles(void)
  2324. {
  2325. FileHandle_t modelouthandle = 0;
  2326. FileHandle_t blockouthandle = 0;
  2327. CPlainAutoPtr< CP4File > spFileBlockOut, spFileModelOut;
  2328. int total = 0;
  2329. int i;
  2330. char filename[MAX_PATH];
  2331. studiohdr_t *phdr;
  2332. studiohdr_t *pblockhdr = 0;
  2333. pStart = (byte *)kalloc( 1, FILEBUFFER );
  2334. pBlockData = NULL;
  2335. pBlockStart = NULL;
  2336. Q_StripExtension( outname, outname, sizeof( outname ) );
  2337. if (g_animblocksize != 0)
  2338. {
  2339. // write the non-default g_sequence group data to separate files
  2340. sprintf( g_animblockname, "models/%s.ani", outname );
  2341. V_strcpy_safe( filename, gamedir );
  2342. V_strcat_safe( filename, g_animblockname );
  2343. EnsureFileDirectoryExists( filename );
  2344. if (!g_bVerifyOnly)
  2345. {
  2346. spFileBlockOut.Attach( g_p4factory->AccessFile( filename ) );
  2347. spFileBlockOut->Edit();
  2348. // Create the directory hierarchy for the ANI
  2349. char parentdir[MAX_PATH];
  2350. V_strcpy_safe( parentdir, filename );
  2351. V_StripFilename( parentdir );
  2352. g_pFullFileSystem->CreateDirHierarchy( parentdir );
  2353. blockouthandle = SafeOpenWrite( filename );
  2354. }
  2355. pBlockStart = (byte *)kalloc( 1, FILEBUFFER );
  2356. pBlockData = pBlockStart;
  2357. pblockhdr = (studiohdr_t *)pBlockData;
  2358. pblockhdr->id = IDSTUDIOANIMGROUPHEADER;
  2359. pblockhdr->version = STUDIO_VERSION;
  2360. pBlockData += sizeof( *pblockhdr );
  2361. }
  2362. //
  2363. // write the g_model output file
  2364. //
  2365. phdr = (studiohdr_t *)pStart;
  2366. phdr->id = IDSTUDIOHEADER;
  2367. phdr->version = STUDIO_VERSION;
  2368. V_strcat_safe (outname, ".mdl");
  2369. // strcpy( outname, ExpandPath( outname ) );
  2370. V_strcpy_safe( filename, gamedir );
  2371. // if( *g_pPlatformName )
  2372. // {
  2373. // strcat( filename, "platform_" );
  2374. // strcat( filename, g_pPlatformName );
  2375. // strcat( filename, "/" );
  2376. // }
  2377. V_strcat_safe( filename, "models/" );
  2378. V_strcat_safe( filename, outname );
  2379. // Create the directory.
  2380. EnsureFileDirectoryExists( filename );
  2381. if( !g_quiet )
  2382. {
  2383. printf ("---------------------\n");
  2384. printf ("writing %s:\n", filename);
  2385. }
  2386. LoadPreexistingSequenceOrder( filename );
  2387. if (!g_bVerifyOnly)
  2388. {
  2389. spFileModelOut.Attach( g_p4factory->AccessFile( filename ) );
  2390. spFileModelOut->Edit();
  2391. // Create the directory hierarchy for the MDL
  2392. char parentdir[MAX_PATH];
  2393. V_strcpy_safe( parentdir, filename );
  2394. V_StripFilename( parentdir );
  2395. g_pFullFileSystem->CreateDirHierarchy( parentdir );
  2396. modelouthandle = SafeOpenWrite (filename);
  2397. }
  2398. phdr->eyeposition = eyeposition;
  2399. phdr->illumposition = illumposition;
  2400. if ( !g_wrotebbox && g_sequence.Count() > 0)
  2401. {
  2402. VectorCopy( g_sequence[0].bmin, bbox[0] );
  2403. VectorCopy( g_sequence[0].bmax, bbox[1] );
  2404. CollisionModel_ExpandBBox( bbox[0], bbox[1] );
  2405. VectorCopy( bbox[0], g_sequence[0].bmin );
  2406. VectorCopy( bbox[1], g_sequence[0].bmax );
  2407. }
  2408. if ( !g_wrotecbox )
  2409. {
  2410. // no default clipping box, just use per-sequence box
  2411. VectorCopy( vec3_origin, cbox[0] );
  2412. VectorCopy( vec3_origin, cbox[1] );
  2413. }
  2414. phdr->hull_min = bbox[0];
  2415. phdr->hull_max = bbox[1];
  2416. phdr->view_bbmin = cbox[0];
  2417. phdr->view_bbmax = cbox[1];
  2418. phdr->flags = gflags;
  2419. phdr->mass = GetCollisionModelMass();
  2420. phdr->constdirectionallightdot = g_constdirectionalightdot;
  2421. if ( g_numAllowedRootLODs > 0 )
  2422. {
  2423. phdr->numAllowedRootLODs = g_numAllowedRootLODs;
  2424. }
  2425. pData = (byte *)phdr + sizeof( studiohdr_t );
  2426. // FIXME: Remove when we up the model version
  2427. phdr->studiohdr2index = ( pData - pStart );
  2428. studiohdr2_t* phdr2 = (studiohdr2_t*)pData;
  2429. memset( phdr2, 0, sizeof(studiohdr2_t) );
  2430. pData = (byte*)phdr2 + sizeof(studiohdr2_t);
  2431. phdr2->illumpositionattachmentindex = g_illumpositionattachment;
  2432. phdr2->flMaxEyeDeflection = g_flMaxEyeDeflection;
  2433. BeginStringTable( );
  2434. // Copy the full path for compatibility with older programs
  2435. //V_strcpy_safe( phdr->name, V_UnqualifiedFileName( outname ) );
  2436. V_strcpy_safe( phdr->name, outname );
  2437. AddToStringTable( phdr2, &phdr2->sznameindex, outname );
  2438. WriteBoneInfo( phdr );
  2439. if( !g_quiet )
  2440. {
  2441. printf("bones %7d bytes (%d)\n", pData - pStart - total, g_numbones );
  2442. }
  2443. total = pData - pStart;
  2444. pData = WriteAnimations( pData, pStart, phdr );
  2445. if( !g_quiet )
  2446. {
  2447. printf("animations %7d bytes (%d anims) (%d frames) [%d:%02d]\n", pData - pStart - total, g_numani, totalframes, (int)totalseconds / 60, (int)totalseconds % 60 );
  2448. }
  2449. total = pData - pStart;
  2450. WriteSequenceInfo( phdr );
  2451. if( !g_quiet )
  2452. {
  2453. printf("sequences %7d bytes (%d seq) \n", pData - pStart - total, g_sequence.Count() );
  2454. }
  2455. total = pData - pStart;
  2456. WriteModel( phdr );
  2457. /*
  2458. if( !g_quiet )
  2459. {
  2460. printf("models %7d bytes\n", pData - pStart - total );
  2461. }
  2462. */
  2463. total = pData - pStart;
  2464. WriteTextures( phdr );
  2465. if( !g_quiet )
  2466. {
  2467. printf("textures %7d bytes\n", pData - pStart - total );
  2468. }
  2469. total = pData - pStart;
  2470. WriteKeyValues( phdr, &g_KeyValueText );
  2471. if( !g_quiet )
  2472. {
  2473. printf("keyvalues %7d bytes\n", pData - pStart - total );
  2474. }
  2475. total = pData - pStart;
  2476. WriteBoneTransforms( phdr2, phdr->pBone( 0 ) );
  2477. if( !g_quiet )
  2478. {
  2479. printf("bone transforms %7d bytes\n", pData - pStart - total );
  2480. }
  2481. total = pData - pStart;
  2482. if ( total > FILEBUFFER )
  2483. {
  2484. MdlError( "file exceeds %d bytes (%d)", FILEBUFFER, total );
  2485. }
  2486. WriteBoneFlexDrivers( phdr2 );
  2487. if ( !g_quiet )
  2488. {
  2489. printf("bone flex driver %7d bytes\n", pData - pStart - total );
  2490. }
  2491. total = pData - pStart;
  2492. if ( total > FILEBUFFER )
  2493. {
  2494. MdlError( "file exceeds %d bytes (%d)", FILEBUFFER, total );
  2495. }
  2496. pData = WriteStringTable( pData );
  2497. total = pData - pStart;
  2498. if ( total > FILEBUFFER )
  2499. {
  2500. MdlError( "file exceeds %d bytes (%d)", FILEBUFFER, total );
  2501. }
  2502. phdr->checksum = 0;
  2503. for (i = 0; i < total; i += 4)
  2504. {
  2505. // TODO: does this need something more than a simple shift left and add checksum?
  2506. phdr->checksum = (phdr->checksum << 1) + ((phdr->checksum & 0x8000000) ? 1 : 0) + *((long *)(pStart + i));
  2507. }
  2508. if (g_bVerifyOnly)
  2509. return;
  2510. CollisionModel_Write( phdr->checksum );
  2511. if( !g_quiet )
  2512. {
  2513. printf("collision %7d bytes\n", pData - pStart - total );
  2514. }
  2515. AssignMeshIDs( phdr );
  2516. phdr->length = pData - pStart;
  2517. if( !g_quiet )
  2518. {
  2519. printf("total %7d\n", phdr->length );
  2520. }
  2521. if ( phdr->length > FILEBUFFER )
  2522. {
  2523. MdlError( "file exceeds %d bytes (%d)", FILEBUFFER, total );
  2524. }
  2525. // Load materials for this model via the material system so that the
  2526. // optimizer can ask questions about the materials.
  2527. LoadMaterials( phdr );
  2528. SafeWrite( modelouthandle, pStart, phdr->length );
  2529. g_pFileSystem->Close(modelouthandle);
  2530. if ( spFileModelOut.IsValid() ) spFileModelOut->Add();
  2531. if (pBlockStart)
  2532. {
  2533. pblockhdr->length = pBlockData - pBlockStart;
  2534. if ( g_bX360 )
  2535. {
  2536. // Before writing this .ani, write the byteswapped version
  2537. void *pOutBase = kalloc(1, pblockhdr->length + BYTESWAP_ALIGNMENT_PADDING);
  2538. int finalSize = StudioByteSwap::ByteswapANI( phdr, pOutBase, pBlockStart, pblockhdr->length );
  2539. if ( finalSize == 0 )
  2540. {
  2541. MdlError("Aborted ANI byteswap on '%s':\n", g_animblockname);
  2542. }
  2543. char outname[ MAX_PATH ];
  2544. Q_StripExtension( g_animblockname, outname, sizeof( outname ) );
  2545. Q_strcat( outname, ".360.ani", sizeof( outname ) );
  2546. {
  2547. CP4AutoEditAddFile autop4( outname );
  2548. SaveFile( outname, pOutBase, finalSize );
  2549. }
  2550. }
  2551. SafeWrite( blockouthandle, pBlockStart, pblockhdr->length );
  2552. g_pFileSystem->Close( blockouthandle );
  2553. if ( spFileBlockOut.IsValid() ) spFileBlockOut->Add();
  2554. if ( !g_quiet )
  2555. {
  2556. printf ("---------------------\n");
  2557. printf("writing %s:\n", g_animblockname);
  2558. printf("blocks %7d\n", g_numanimblocks );
  2559. printf("total %7d\n", pblockhdr->length );
  2560. }
  2561. }
  2562. if (phdr->numbodyparts != 0)
  2563. {
  2564. // vertices have become an external peer data store
  2565. // write now prior to impending vertex access from any further code
  2566. // vertex accessors hide shifting vertex data
  2567. WriteVertices( phdr );
  2568. #ifdef _DEBUG
  2569. int bodyPartID;
  2570. for( bodyPartID = 0; bodyPartID < phdr->numbodyparts; bodyPartID++ )
  2571. {
  2572. mstudiobodyparts_t *pBodyPart = phdr->pBodypart( bodyPartID );
  2573. int modelID;
  2574. for( modelID = 0; modelID < pBodyPart->nummodels; modelID++ )
  2575. {
  2576. mstudiomodel_t *pModel = pBodyPart->pModel( modelID );
  2577. const mstudio_modelvertexdata_t *vertData = pModel->GetVertexData();
  2578. Assert( vertData ); // This can only return NULL on X360 for now
  2579. int vertID;
  2580. for( vertID = 0; vertID < pModel->numvertices; vertID++ )
  2581. {
  2582. Vector4D *pTangentS = vertData->TangentS( vertID );
  2583. Assert( pTangentS->w == -1.0f || pTangentS->w == 1.0f );
  2584. }
  2585. }
  2586. }
  2587. #endif
  2588. if ( !g_StudioMdlCheckUVCmd.CheckUVs( g_source, g_numsources ) )
  2589. {
  2590. MdlError( "UV checks failed\n" );
  2591. }
  2592. OptimizedModel::WriteOptimizedFiles( phdr, g_bodypart );
  2593. // now have external finalized vtx (windings) and vvd (vertexes)
  2594. // re-open files, sort vertexes, perform fixups, and rewrite
  2595. // purposely isolated as a post process for stability
  2596. if (!FixupToSortedLODVertexes( phdr ))
  2597. {
  2598. MdlError("Aborted vertex sort fixup on '%s':\n", filename);
  2599. }
  2600. if (!Clamp_RootLOD( phdr ))
  2601. {
  2602. MdlError("Aborted root lod shift '%s':\n", filename);
  2603. }
  2604. }
  2605. if ( g_bX360 )
  2606. {
  2607. // now all files have been finalized and fixed up.
  2608. // re-open the files once more and swap all little-endian
  2609. // data to big-endian format to produce Xbox360 files.
  2610. WriteAllSwappedFiles( filename );
  2611. }
  2612. // NOTE! If you don't want to go through the effort of loading studiorender for perf reasons,
  2613. // make sure spewFlags ends up being zero.
  2614. unsigned int spewFlags = SPEWPERFSTATS_SHOWSTUDIORENDERWARNINGS;
  2615. if ( g_bPerf )
  2616. {
  2617. spewFlags |= SPEWPERFSTATS_SHOWPERF;
  2618. }
  2619. if( spewFlags )
  2620. {
  2621. SpewPerfStats( phdr, filename, spewFlags );
  2622. }
  2623. }
  2624. const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData )
  2625. {
  2626. static vertexFileHeader_t *pVertexHdr;
  2627. char filename[MAX_PATH];
  2628. Assert( pModelData == NULL );
  2629. if (pVertexHdr)
  2630. {
  2631. // studiomdl is a single model process, can simply persist data in static
  2632. goto hasData;
  2633. }
  2634. // load and persist the vertex file
  2635. V_strcpy_safe( filename, gamedir );
  2636. // if( *g_pPlatformName )
  2637. // {
  2638. // strcat( filename, "platform_" );
  2639. // strcat( filename, g_pPlatformName );
  2640. // strcat( filename, "/" );
  2641. // }
  2642. V_strcat_safe( filename, "models/" );
  2643. V_strcat_safe( filename, outname );
  2644. Q_StripExtension( filename, filename, sizeof( filename ) );
  2645. V_strcat_safe( filename, ".vvd" );
  2646. LoadFile(filename, (void**)&pVertexHdr);
  2647. // check id
  2648. if (pVertexHdr->id != MODEL_VERTEX_FILE_ID)
  2649. {
  2650. MdlError("Error Vertex File: '%s' (id %d should be %d)\n", filename, pVertexHdr->id, MODEL_VERTEX_FILE_ID);
  2651. }
  2652. // check version
  2653. if (pVertexHdr->version != MODEL_VERTEX_FILE_VERSION)
  2654. {
  2655. MdlError("Error Vertex File: '%s' (version %d should be %d)\n", filename, pVertexHdr->version, MODEL_VERTEX_FILE_VERSION);
  2656. }
  2657. hasData:
  2658. return pVertexHdr;
  2659. }
  2660. typedef struct
  2661. {
  2662. int meshVertID;
  2663. int finalMeshVertID;
  2664. int vertexOffset;
  2665. int lodFlags;
  2666. } usedVertex_t;
  2667. typedef struct
  2668. {
  2669. int offsets[MAX_NUM_LODS];
  2670. int numVertexes[MAX_NUM_LODS];
  2671. } lodMeshInfo_t;
  2672. typedef struct
  2673. {
  2674. usedVertex_t *pVertexList;
  2675. unsigned short *pVertexMap;
  2676. int numVertexes;
  2677. lodMeshInfo_t lodMeshInfo;
  2678. } vertexPool_t;
  2679. #define ALIGN(b,s) (((unsigned int)(b)+(s)-1)&~((s)-1))
  2680. //-----------------------------------------------------------------------------
  2681. // FindVertexOffsets
  2682. //
  2683. // Iterate sorted vertex list to determine mesh starts and counts.
  2684. //-----------------------------------------------------------------------------
  2685. void FindVertexOffsets(int vertexOffset, int offsets[MAX_NUM_LODS], int counts[MAX_NUM_LODS], int numLods, const usedVertex_t *pVertexList, int numVertexes)
  2686. {
  2687. int lodFlags;
  2688. int i;
  2689. int j;
  2690. int k;
  2691. // vertexOffset uniquely identifies a single mesh's vertexes in lod vertex sorted list
  2692. // lod vertex list is sorted from lod N..lod 0
  2693. for (i=numLods-1; i>=0; i--)
  2694. {
  2695. offsets[i] = 0;
  2696. counts[i] = 0;
  2697. lodFlags = (1<<(i+1))-1;
  2698. for (j=0; j<numVertexes; j++)
  2699. {
  2700. // determine start of mesh at desired lod
  2701. if (pVertexList[j].lodFlags > lodFlags)
  2702. continue;
  2703. if (pVertexList[j].vertexOffset != vertexOffset)
  2704. continue;
  2705. for (k=j; k<numVertexes; k++)
  2706. {
  2707. // determine end of mesh at desired lod
  2708. if (pVertexList[k].vertexOffset != vertexOffset)
  2709. break;
  2710. if (!(pVertexList[k].lodFlags & (1<<i)))
  2711. break;
  2712. }
  2713. offsets[i] = j;
  2714. counts[i] = k-j;
  2715. break;
  2716. }
  2717. }
  2718. }
  2719. //-----------------------------------------------------------------------------
  2720. // _CompareUsedVertexes
  2721. //
  2722. // qsort callback
  2723. //-----------------------------------------------------------------------------
  2724. static int _CompareUsedVertexes(const void *a, const void *b)
  2725. {
  2726. usedVertex_t *pVertexA;
  2727. usedVertex_t *pVertexB;
  2728. int sort;
  2729. int lodA;
  2730. int lodB;
  2731. pVertexA = (usedVertex_t*)a;
  2732. pVertexB = (usedVertex_t*)b;
  2733. // determine highest (lowest detail) lod
  2734. // forces grouping into discrete MAX_NUM_LODS sections
  2735. lodA = Q_log2(pVertexA->lodFlags);
  2736. lodB = Q_log2(pVertexB->lodFlags);
  2737. // descending sort (LodN..Lod0)
  2738. sort = lodB-lodA;
  2739. if (sort)
  2740. return sort;
  2741. // within same lod, sub sort (ascending) by mesh
  2742. sort = pVertexA->vertexOffset - pVertexB->vertexOffset;
  2743. if (sort)
  2744. return sort;
  2745. // within same mesh, sub sort (ascending) by vertex
  2746. sort = pVertexA->meshVertID - pVertexB->meshVertID;
  2747. return sort;
  2748. }
  2749. //-----------------------------------------------------------------------------
  2750. // BuildSortedVertexList
  2751. //
  2752. // Generates the sorted vertex list. Routine is purposely serial to
  2753. // ensure vertex integrity.
  2754. //-----------------------------------------------------------------------------
  2755. bool BuildSortedVertexList(const studiohdr_t *pStudioHdr, const void *pVtxBuff, vertexPool_t **ppVertexPools, int *pNumVertexPools, usedVertex_t **ppVertexList, int *pNumVertexes)
  2756. {
  2757. OptimizedModel::FileHeader_t *pVtxHdr;
  2758. OptimizedModel::BodyPartHeader_t *pBodyPartHdr;
  2759. OptimizedModel::ModelHeader_t *pModelHdr;
  2760. OptimizedModel::ModelLODHeader_t *pModelLODHdr;
  2761. OptimizedModel::MeshHeader_t *pMeshHdr;
  2762. OptimizedModel::StripGroupHeader_t *pStripGroupHdr;
  2763. OptimizedModel::Vertex_t *pStripVertex;
  2764. mstudiobodyparts_t *pStudioBodyPart;
  2765. mstudiomodel_t *pStudioModel;
  2766. mstudiomesh_t *pStudioMesh;
  2767. usedVertex_t *usedVertexes;
  2768. vertexPool_t *pVertexPools;
  2769. vertexPool_t *pPool;
  2770. usedVertex_t *pVertexList;
  2771. int *pVertexes;
  2772. unsigned short *pVertexMap;
  2773. int index;
  2774. int currLod;
  2775. int vertexOffset;
  2776. int i,j,k,m,n,p;
  2777. int poolStart;
  2778. int numVertexPools;
  2779. int numVertexes;
  2780. int numMeshVertexes;
  2781. int offsets[MAX_NUM_LODS];
  2782. int counts[MAX_NUM_LODS];
  2783. int finalMeshVertID;
  2784. int baseMeshVertID;
  2785. *ppVertexPools = NULL;
  2786. *pNumVertexPools = 0;
  2787. *ppVertexList = NULL;
  2788. *pNumVertexes = 0;
  2789. pVtxHdr = (OptimizedModel::FileHeader_t*)pVtxBuff;
  2790. // determine number of vertex pools
  2791. if (pStudioHdr->numbodyparts != pVtxHdr->numBodyParts)
  2792. return false;
  2793. numVertexPools = 0;
  2794. for (i=0; i<pVtxHdr->numBodyParts; i++)
  2795. {
  2796. pBodyPartHdr = pVtxHdr->pBodyPart(i);
  2797. pStudioBodyPart = pStudioHdr->pBodypart(i);
  2798. if (pStudioBodyPart->nummodels != pBodyPartHdr->numModels)
  2799. return false;
  2800. // the model's subordinate lods only reference from a single top level pool
  2801. // no new verts are created for sub lods
  2802. // each model's subordinate mesh dictates its own vertex pool
  2803. for (j=0; j<pBodyPartHdr->numModels; j++)
  2804. {
  2805. pStudioModel = pStudioBodyPart->pModel(j);
  2806. numVertexPools += pStudioModel->nummeshes;
  2807. }
  2808. }
  2809. // allocate pools
  2810. pVertexPools = (vertexPool_t*)malloc(numVertexPools*sizeof(vertexPool_t));
  2811. memset(pVertexPools, 0, numVertexPools*sizeof(vertexPool_t));
  2812. // iterate lods, mark referenced indexes
  2813. numVertexPools = 0;
  2814. for (i=0; i<pVtxHdr->numBodyParts; i++)
  2815. {
  2816. pBodyPartHdr = pVtxHdr->pBodyPart(i);
  2817. pStudioBodyPart = pStudioHdr->pBodypart(i);
  2818. for (j=0; j<pBodyPartHdr->numModels; j++)
  2819. {
  2820. pModelHdr = pBodyPartHdr->pModel(j);
  2821. pStudioModel = pStudioBodyPart->pModel(j);
  2822. // allocate each mesh's vertex list
  2823. poolStart = numVertexPools;
  2824. for (k=0; k<pStudioModel->nummeshes; k++)
  2825. {
  2826. // track the expected relative offset into a flattened vertex list
  2827. vertexOffset = 0;
  2828. for (m=0; m<poolStart+k; m++)
  2829. vertexOffset += pVertexPools[m].numVertexes;
  2830. pStudioMesh = pStudioModel->pMesh(k);
  2831. numMeshVertexes = pStudioMesh->numvertices;
  2832. if (numMeshVertexes)
  2833. {
  2834. usedVertexes = (usedVertex_t*)malloc(numMeshVertexes*sizeof(usedVertex_t));
  2835. pVertexMap = (unsigned short*)malloc(numMeshVertexes*sizeof(unsigned short));
  2836. for (n=0; n<numMeshVertexes; n++)
  2837. {
  2838. // setup mapping
  2839. // due to the hierarchial layout, the vertID's map per mesh's pool
  2840. // a linear layout of the vertexes requires a unique signature to achieve a remap
  2841. // the offset and index form a unique signature
  2842. usedVertexes[n].meshVertID = n;
  2843. usedVertexes[n].finalMeshVertID = -1;
  2844. usedVertexes[n].vertexOffset = vertexOffset;
  2845. usedVertexes[n].lodFlags = 0;
  2846. pVertexMap[n] = n;
  2847. }
  2848. pVertexPools[numVertexPools].pVertexList = usedVertexes;
  2849. pVertexPools[numVertexPools].pVertexMap = pVertexMap;
  2850. }
  2851. pVertexPools[numVertexPools].numVertexes = numMeshVertexes;
  2852. numVertexPools++;
  2853. }
  2854. // iterate all lods
  2855. for (currLod=0; currLod<pVtxHdr->numLODs; currLod++)
  2856. {
  2857. pModelLODHdr = pModelHdr->pLOD(currLod);
  2858. if (pModelLODHdr->numMeshes != pStudioModel->nummeshes)
  2859. return false;
  2860. for (k=0; k<pModelLODHdr->numMeshes; k++)
  2861. {
  2862. pMeshHdr = pModelLODHdr->pMesh(k);
  2863. pStudioMesh = pStudioModel->pMesh(k);
  2864. for (m=0; m<pMeshHdr->numStripGroups; m++)
  2865. {
  2866. pStripGroupHdr = pMeshHdr->pStripGroup(m);
  2867. // sanity check the indexes have 100% coverage of the vertexes
  2868. pVertexes = (int*)malloc(pStripGroupHdr->numVerts*sizeof(int));
  2869. memset(pVertexes, 0xFF, pStripGroupHdr->numVerts*sizeof(int));
  2870. for (n=0; n<pStripGroupHdr->numIndices; n++)
  2871. {
  2872. index = *pStripGroupHdr->pIndex(n);
  2873. if (index < 0 || index >= pStripGroupHdr->numVerts)
  2874. return false;
  2875. pVertexes[index] = index;
  2876. }
  2877. // sanity check for coverage
  2878. for (n=0; n<pStripGroupHdr->numVerts; n++)
  2879. {
  2880. if (pVertexes[n] != n)
  2881. return false;
  2882. }
  2883. free(pVertexes);
  2884. // iterate vertexes
  2885. pPool = &pVertexPools[poolStart + k];
  2886. for (n=0; n<pStripGroupHdr->numVerts; n++)
  2887. {
  2888. pStripVertex = pStripGroupHdr->pVertex(n);
  2889. if (pStripVertex->origMeshVertID < 0 || pStripVertex->origMeshVertID >= pPool->numVertexes)
  2890. return false;
  2891. // arrange binary flags for numerical sorting
  2892. // the lowest detail lod's verts at the top, the root lod's verts at the bottom
  2893. pPool->pVertexList[pStripVertex->origMeshVertID].lodFlags |= 1<<currLod;
  2894. }
  2895. }
  2896. }
  2897. }
  2898. }
  2899. }
  2900. // flatten the vertex pool hierarchy into a linear sequence
  2901. numVertexes = 0;
  2902. for (i=0; i<numVertexPools; i++)
  2903. numVertexes += pVertexPools[i].numVertexes;
  2904. pVertexList = (usedVertex_t*)malloc(numVertexes*sizeof(usedVertex_t));
  2905. numVertexes = 0;
  2906. for (i=0; i<numVertexPools; i++)
  2907. {
  2908. pPool = &pVertexPools[i];
  2909. for (j=0; j<pPool->numVertexes; j++)
  2910. {
  2911. if (!pPool->pVertexList[j].lodFlags)
  2912. {
  2913. // found an orphaned vertex that is unreferenced at any lod strip winding
  2914. // don't know how these occur or who references them
  2915. // cannot cull the orphaned vertexes, otherwise vertex counts are wrong
  2916. // every vertex must be remapped
  2917. // force the vertex to belong to the lowest lod
  2918. // lod flags must be nonzero for proper sorted runs
  2919. pPool->pVertexList[j].lodFlags = 1<<(pVtxHdr->numLODs-1);
  2920. }
  2921. }
  2922. memcpy(&pVertexList[numVertexes], pPool->pVertexList, pPool->numVertexes*sizeof(usedVertex_t));
  2923. numVertexes += pPool->numVertexes;
  2924. }
  2925. // sort the vertexes based on lod flags
  2926. // the sort dictates the linear sequencing of the .vvd data file
  2927. // the vtx file indexes get remapped to the new sort order
  2928. qsort(pVertexList, numVertexes, sizeof(usedVertex_t), _CompareUsedVertexes);
  2929. // build a mapping table from mesh relative indexes to the flat lod sorted array
  2930. vertexOffset = 0;
  2931. for (i=0; i<numVertexPools; i++)
  2932. {
  2933. pPool = &pVertexPools[i];
  2934. for (j=0; j<pPool->numVertexes; j++)
  2935. {
  2936. // scan flattened sorted vertexes
  2937. for (k=0; k<numVertexes; k++)
  2938. {
  2939. if (pVertexList[k].vertexOffset == vertexOffset && pVertexList[k].meshVertID == j)
  2940. break;
  2941. }
  2942. pPool->pVertexMap[j] = k;
  2943. }
  2944. vertexOffset += pPool->numVertexes;
  2945. }
  2946. // build offsets and counts that identifies mesh's distribution across lods
  2947. // calc final fixed vertex location if vertexes were gathered to mesh order from lod sorted list
  2948. finalMeshVertID = 0;
  2949. poolStart = 0;
  2950. for (i=0; i<pStudioHdr->numbodyparts; i++)
  2951. {
  2952. pStudioBodyPart = pStudioHdr->pBodypart(i);
  2953. for (j=0; j<pStudioBodyPart->nummodels; j++)
  2954. {
  2955. pStudioModel = pStudioBodyPart->pModel(j);
  2956. for (m=0; m<pStudioModel->nummeshes; m++)
  2957. {
  2958. // track the expected offset into linear vertexes
  2959. vertexOffset = 0;
  2960. for (n=0; n<poolStart+m; n++)
  2961. vertexOffset += pVertexPools[n].numVertexes;
  2962. // vertexOffset works as unique key to identify vertexes for a specific mesh
  2963. // a mesh's verts are distributed, but guaranteed sequential in the lod sorted vertex list
  2964. // determine base index and offset and run length for target mesh for all lod levels
  2965. FindVertexOffsets(vertexOffset, offsets, counts, pVtxHdr->numLODs, pVertexList, numVertexes);
  2966. for (n=0; n<pVtxHdr->numLODs; n++)
  2967. {
  2968. if (!counts[n])
  2969. offsets[n] = 0;
  2970. pVertexPools[poolStart+m].lodMeshInfo.offsets[n] = offsets[n];
  2971. pVertexPools[poolStart+m].lodMeshInfo.numVertexes[n] = counts[n];
  2972. }
  2973. // iterate using calced offsets to walk each mesh
  2974. // set its expected final vertex id, which is its "gathered" index relative to mesh
  2975. baseMeshVertID = finalMeshVertID;
  2976. for (n=pVtxHdr->numLODs-1; n>=0; n--)
  2977. {
  2978. // iterate each vert in the mesh
  2979. // vertex id is relative to
  2980. for (p=0; p<counts[n]; p++)
  2981. {
  2982. pVertexList[offsets[n] + p].finalMeshVertID = finalMeshVertID - baseMeshVertID;
  2983. finalMeshVertID++;
  2984. }
  2985. }
  2986. }
  2987. poolStart += pStudioModel->nummeshes;
  2988. }
  2989. }
  2990. // safety check
  2991. // every referenced vertex should have been remapped correctly
  2992. // some models do have orphaned vertexes, ignore these
  2993. for (i=0; i<numVertexes; i++)
  2994. {
  2995. if (pVertexList[i].lodFlags && pVertexList[i].finalMeshVertID == -1)
  2996. {
  2997. // should never happen, data occured in unknown manner
  2998. // don't build corrupted data
  2999. return false;
  3000. }
  3001. }
  3002. // provide generated tables
  3003. *ppVertexPools = pVertexPools;
  3004. *pNumVertexPools = numVertexPools;
  3005. *ppVertexList = pVertexList;
  3006. *pNumVertexes = numVertexes;
  3007. // success
  3008. return true;
  3009. }
  3010. //-----------------------------------------------------------------------------
  3011. // FixupVVDFile
  3012. //
  3013. // VVD files get vertexes remapped to a flat lod sorted order.
  3014. //-----------------------------------------------------------------------------
  3015. bool FixupVVDFile(const char *fileName, const studiohdr_t *pStudioHdr, const void *pVtxBuff, const vertexPool_t *pVertexPools, int numVertexPools, const usedVertex_t *pVertexList, int numVertexes)
  3016. {
  3017. OptimizedModel::FileHeader_t *pVtxHdr;
  3018. vertexFileHeader_t *pFileHdr_old;
  3019. vertexFileHeader_t *pFileHdr_new;
  3020. mstudiobodyparts_t *pStudioBodyPart;
  3021. mstudiomodel_t *pStudioModel;
  3022. mstudiomesh_t *pStudioMesh;
  3023. mstudiovertex_t *pVertex_old;
  3024. mstudiovertex_t *pVertex_new;
  3025. Vector4D *pTangent_new;
  3026. Vector4D *pTangent_old;
  3027. mstudiovertex_t **pFlatVertexes;
  3028. Vector4D **pFlatTangents;
  3029. vertexFileFixup_t *pFixupTable;
  3030. const lodMeshInfo_t *pLodMeshInfo;
  3031. byte *pStart_new;
  3032. byte *pData_new;
  3033. byte *pStart_base;
  3034. byte *pVertexBase_old;
  3035. byte *pTangentBase_old;
  3036. void *pVvdBuff;
  3037. int i;
  3038. int j;
  3039. int k;
  3040. int n;
  3041. int p;
  3042. int numFixups;
  3043. int numFlat;
  3044. int oldIndex;
  3045. int mask;
  3046. int maxCount;
  3047. int numMeshes;
  3048. int numOutFixups;
  3049. pVtxHdr = (OptimizedModel::FileHeader_t*)pVtxBuff;
  3050. LoadFile((char*)fileName, &pVvdBuff);
  3051. pFileHdr_old = (vertexFileHeader_t*)pVvdBuff;
  3052. if (pFileHdr_old->numLODs != 1)
  3053. {
  3054. // file has wrong expected state
  3055. return false;
  3056. }
  3057. // meshes need relocation fixup from lod order back to mesh order
  3058. numFixups = 0;
  3059. numMeshes = 0;
  3060. for (i=0; i<pStudioHdr->numbodyparts; i++)
  3061. {
  3062. pStudioBodyPart = pStudioHdr->pBodypart(i);
  3063. for (j=0; j<pStudioBodyPart->nummodels; j++)
  3064. {
  3065. pStudioModel = pStudioBodyPart->pModel(j);
  3066. for (k=0; k<pStudioModel->nummeshes; k++)
  3067. {
  3068. pStudioMesh = pStudioModel->pMesh(k);
  3069. if (!pStudioMesh->numvertices)
  3070. {
  3071. // no vertexes for this mesh, skip it
  3072. continue;
  3073. }
  3074. for (n=pVtxHdr->numLODs-1; n>=0; n--)
  3075. {
  3076. pLodMeshInfo = &pVertexPools[numMeshes+k].lodMeshInfo;
  3077. if (!pLodMeshInfo->numVertexes[n])
  3078. {
  3079. // no vertexes for this portion of the mesh at this lod, skip it
  3080. continue;
  3081. }
  3082. numFixups++;
  3083. }
  3084. }
  3085. numMeshes += k;
  3086. }
  3087. }
  3088. if (numMeshes == 1 || numFixups == 1 || pVtxHdr->numLODs == 1)
  3089. {
  3090. // no fixup required for a single mesh
  3091. // no fixup required for single lod
  3092. // no fixup required when mesh data is contiguous as expected
  3093. numFixups = 0;
  3094. }
  3095. pStart_base = (byte*)malloc(FILEBUFFER);
  3096. memset(pStart_base, 0, FILEBUFFER);
  3097. pStart_new = (byte*)ALIGN(pStart_base,16);
  3098. pData_new = pStart_new;
  3099. // setup headers
  3100. pFileHdr_new = (vertexFileHeader_t*)pData_new;
  3101. pData_new += sizeof(vertexFileHeader_t);
  3102. // clone and fixup new header
  3103. *pFileHdr_new = *pFileHdr_old;
  3104. pFileHdr_new->numLODs = pVtxHdr->numLODs;
  3105. pFileHdr_new->numFixups = numFixups;
  3106. // skip new fixup table
  3107. pData_new = (byte*)ALIGN(pData_new, 4);
  3108. pFixupTable = (vertexFileFixup_t*)pData_new;
  3109. pFileHdr_new->fixupTableStart = pData_new - pStart_new;
  3110. pData_new += numFixups*sizeof(vertexFileFixup_t);
  3111. // skip new vertex data
  3112. pData_new = (byte*)ALIGN(pData_new, 16);
  3113. pVertex_new = (mstudiovertex_t*)pData_new;
  3114. pFileHdr_new->vertexDataStart = pData_new - pStart_new;
  3115. pData_new += numVertexes*sizeof(mstudiovertex_t);
  3116. // skip new tangent data
  3117. pData_new = (byte*)ALIGN(pData_new, 16);
  3118. pTangent_new = (Vector4D*)pData_new;
  3119. pFileHdr_new->tangentDataStart = pData_new - pStart_new;
  3120. pData_new += numVertexes*sizeof(Vector4D);
  3121. pVertexBase_old = (byte*)pFileHdr_old + pFileHdr_old->vertexDataStart;
  3122. pTangentBase_old = (byte*)pFileHdr_old + pFileHdr_old->tangentDataStart;
  3123. // determine number of aggregate verts towards root lod
  3124. // loader can truncate read according to desired root lod
  3125. maxCount = -1;
  3126. for (n=pVtxHdr->numLODs-1; n>=0; n--)
  3127. {
  3128. mask = 1<<n;
  3129. for (p=0; p<numVertexes; p++)
  3130. {
  3131. if (mask & pVertexList[p].lodFlags)
  3132. {
  3133. if (maxCount < p)
  3134. maxCount = p;
  3135. }
  3136. }
  3137. pFileHdr_new->numLODVertexes[n] = maxCount+1;
  3138. }
  3139. for (n=pVtxHdr->numLODs; n<MAX_NUM_LODS; n++)
  3140. {
  3141. // ripple the last valid lod entry all the way down
  3142. pFileHdr_new->numLODVertexes[n] = pFileHdr_new->numLODVertexes[pVtxHdr->numLODs-1];
  3143. }
  3144. // build mesh relocation fixup table
  3145. if (numFixups)
  3146. {
  3147. numMeshes = 0;
  3148. numOutFixups = 0;
  3149. for (i=0; i<pStudioHdr->numbodyparts; i++)
  3150. {
  3151. pStudioBodyPart = pStudioHdr->pBodypart(i);
  3152. for (j=0; j<pStudioBodyPart->nummodels; j++)
  3153. {
  3154. pStudioModel = pStudioBodyPart->pModel(j);
  3155. for (k=0; k<pStudioModel->nummeshes; k++)
  3156. {
  3157. pStudioMesh = pStudioModel->pMesh(k);
  3158. if (!pStudioMesh->numvertices)
  3159. {
  3160. // not vertexes for this mesh, skip it
  3161. continue;
  3162. }
  3163. for (n=pVtxHdr->numLODs-1; n>=0; n--)
  3164. {
  3165. pLodMeshInfo = &pVertexPools[numMeshes+k].lodMeshInfo;
  3166. if (!pLodMeshInfo->numVertexes[n])
  3167. {
  3168. // no vertexes for this portion of the mesh at this lod, skip it
  3169. continue;
  3170. }
  3171. pFixupTable[numOutFixups].lod = n;
  3172. pFixupTable[numOutFixups].numVertexes = pLodMeshInfo->numVertexes[n];
  3173. pFixupTable[numOutFixups].sourceVertexID = pLodMeshInfo->offsets[n];
  3174. numOutFixups++;
  3175. }
  3176. }
  3177. numMeshes += pStudioModel->nummeshes;
  3178. }
  3179. }
  3180. if (numOutFixups != numFixups)
  3181. {
  3182. // logic sync error, final calc should match precalc, otherwise memory corruption
  3183. return false;
  3184. }
  3185. }
  3186. // generate offsets to vertexes
  3187. numFlat = 0;
  3188. pFlatVertexes = (mstudiovertex_t**)malloc(numVertexes*sizeof(mstudiovertex_t*));
  3189. pFlatTangents = (Vector4D**)malloc(numVertexes*sizeof(Vector4D*));
  3190. for (i=0; i<pStudioHdr->numbodyparts; i++)
  3191. {
  3192. pStudioBodyPart = pStudioHdr->pBodypart(i);
  3193. for (j=0; j<pStudioBodyPart->nummodels; j++)
  3194. {
  3195. pStudioModel = pStudioBodyPart->pModel(j);
  3196. pVertex_old = (mstudiovertex_t*)&pVertexBase_old[pStudioModel->vertexindex];
  3197. pTangent_old = (Vector4D*)&pTangentBase_old[pStudioModel->tangentsindex];
  3198. for (k=0; k<pStudioModel->nummeshes; k++)
  3199. {
  3200. // get each mesh's vertexes
  3201. pStudioMesh = pStudioModel->pMesh(k);
  3202. for (n=0; n<pStudioMesh->numvertices; n++)
  3203. {
  3204. // old vertex pools are per model, seperated per mesh by a start offset
  3205. // vertexes are then isolated subpools per mesh
  3206. // build the flat linear array of lookup pointers
  3207. pFlatVertexes[numFlat] = &pVertex_old[pStudioMesh->vertexoffset + n];
  3208. pFlatTangents[numFlat] = &pTangent_old[pStudioMesh->vertexoffset + n];
  3209. numFlat++;
  3210. }
  3211. }
  3212. }
  3213. }
  3214. // write in lod sorted order
  3215. for (i=0; i<numVertexes; i++)
  3216. {
  3217. // iterate sorted order, remap old vert location to new vert location
  3218. oldIndex = pVertexList[i].vertexOffset + pVertexList[i].meshVertID;
  3219. memcpy(&pVertex_new[i], pFlatVertexes[oldIndex], sizeof(mstudiovertex_t));
  3220. memcpy(&pTangent_new[i], pFlatTangents[oldIndex], sizeof(Vector4D));
  3221. }
  3222. // pFileHdr_new->length = pData_new-pStart_new;
  3223. {
  3224. CP4AutoEditAddFile autop4( fileName, "binary" );
  3225. SaveFile((char*)fileName, pStart_new, pData_new-pStart_new);
  3226. }
  3227. free(pStart_base);
  3228. free(pFlatVertexes);
  3229. free(pFlatTangents);
  3230. // success
  3231. return true;
  3232. }
  3233. //-----------------------------------------------------------------------------
  3234. // FixupVTXFile
  3235. //
  3236. // VTX files get their windings remapped.
  3237. //-----------------------------------------------------------------------------
  3238. bool FixupVTXFile(const char *fileName, const studiohdr_t *pStudioHdr, const vertexPool_t *pVertexPools, int numVertexPools, const usedVertex_t *pVertexList, int numVertexes)
  3239. {
  3240. OptimizedModel::FileHeader_t *pVtxHdr;
  3241. OptimizedModel::BodyPartHeader_t *pBodyPartHdr;
  3242. OptimizedModel::ModelHeader_t *pModelHdr;
  3243. OptimizedModel::ModelLODHeader_t *pModelLODHdr;
  3244. OptimizedModel::MeshHeader_t *pMeshHdr;
  3245. OptimizedModel::StripGroupHeader_t *pStripGroupHdr;
  3246. OptimizedModel::Vertex_t *pStripVertex;
  3247. int currLod;
  3248. int vertexOffset;
  3249. mstudiobodyparts_t *pStudioBodyPart;
  3250. mstudiomodel_t *pStudioModel;
  3251. int i,j,k,m,n;
  3252. int poolStart;
  3253. int VtxLen;
  3254. int newMeshVertID;
  3255. void *pVtxBuff;
  3256. VtxLen = LoadFile((char*)fileName, &pVtxBuff);
  3257. pVtxHdr = (OptimizedModel::FileHeader_t*)pVtxBuff;
  3258. // iterate all lod's windings
  3259. poolStart = 0;
  3260. for (i=0; i<pVtxHdr->numBodyParts; i++)
  3261. {
  3262. pBodyPartHdr = pVtxHdr->pBodyPart(i);
  3263. pStudioBodyPart = pStudioHdr->pBodypart(i);
  3264. for (j=0; j<pBodyPartHdr->numModels; j++)
  3265. {
  3266. pModelHdr = pBodyPartHdr->pModel(j);
  3267. pStudioModel = pStudioBodyPart->pModel(j);
  3268. // iterate all lods
  3269. for (currLod=0; currLod<pVtxHdr->numLODs; currLod++)
  3270. {
  3271. pModelLODHdr = pModelHdr->pLOD(currLod);
  3272. if (pModelLODHdr->numMeshes != pStudioModel->nummeshes)
  3273. return false;
  3274. for (k=0; k<pModelLODHdr->numMeshes; k++)
  3275. {
  3276. // track the expected relative offset into the flat vertexes
  3277. vertexOffset = 0;
  3278. for (m=0; m<poolStart+k; m++)
  3279. vertexOffset += pVertexPools[m].numVertexes;
  3280. pMeshHdr = pModelLODHdr->pMesh(k);
  3281. for (m=0; m<pMeshHdr->numStripGroups; m++)
  3282. {
  3283. pStripGroupHdr = pMeshHdr->pStripGroup(m);
  3284. for (n=0; n<pStripGroupHdr->numVerts; n++)
  3285. {
  3286. pStripVertex = pStripGroupHdr->pVertex(n);
  3287. // remap old mesh relative vertex index to absolute flat sorted list
  3288. newMeshVertID = pVertexPools[poolStart+k].pVertexMap[pStripVertex->origMeshVertID];
  3289. // map to expected final fixed vertex locations
  3290. // final fixed vertex location is performed by runtime loading code
  3291. newMeshVertID = pVertexList[newMeshVertID].finalMeshVertID;
  3292. // fixup to expected
  3293. pStripVertex->origMeshVertID = newMeshVertID;
  3294. }
  3295. }
  3296. }
  3297. }
  3298. poolStart += pStudioModel->nummeshes;
  3299. }
  3300. }
  3301. // pVtxHdr->length = VtxLen;
  3302. {
  3303. CP4AutoEditAddFile autop4( fileName, "binary" );
  3304. SaveFile((char*)fileName, pVtxBuff, VtxLen);
  3305. }
  3306. free(pVtxBuff);
  3307. return true;
  3308. }
  3309. //-----------------------------------------------------------------------------
  3310. // FixupMDLFile
  3311. //
  3312. // MDL files get flexes/vertex/tangent data offsets fixed
  3313. //-----------------------------------------------------------------------------
  3314. bool FixupMDLFile(const char *fileName, studiohdr_t *pStudioHdr, const void *pVtxBuff, const vertexPool_t *pVertexPools, int numVertexPools, const usedVertex_t *pVertexList, int numVertexes)
  3315. {
  3316. OptimizedModel::FileHeader_t *pVtxHdr;
  3317. const lodMeshInfo_t *pLodMeshInfo;
  3318. mstudiobodyparts_t *pStudioBodyPart;
  3319. mstudiomodel_t *pStudioModel;
  3320. mstudiomesh_t *pStudioMesh;
  3321. mstudioflex_t *pStudioFlex;
  3322. mstudiovertanim_t *pStudioVertAnim;
  3323. int newMeshVertID;
  3324. int i;
  3325. int j;
  3326. int m;
  3327. int n;
  3328. int p;
  3329. int numLODs;
  3330. int numMeshes;
  3331. int total;
  3332. pVtxHdr = (OptimizedModel::FileHeader_t*)pVtxBuff;
  3333. numLODs = pVtxHdr->numLODs;
  3334. numMeshes = 0;
  3335. for (i=0; i<pStudioHdr->numbodyparts; i++)
  3336. {
  3337. pStudioBodyPart = pStudioHdr->pBodypart(i);
  3338. for (j=0; j<pStudioBodyPart->nummodels; j++)
  3339. {
  3340. pStudioModel = pStudioBodyPart->pModel(j);
  3341. for (m=0; m<pStudioModel->nummeshes; m++)
  3342. {
  3343. // get each mesh
  3344. pStudioMesh = pStudioModel->pMesh(m);
  3345. pLodMeshInfo = &pVertexPools[numMeshes+m].lodMeshInfo;
  3346. for (n=0; n<numLODs; n++)
  3347. {
  3348. // the root lod, contains all the lower detail lods verts
  3349. // tally the verts that are at each lod
  3350. total = 0;
  3351. for (p=n; p<numLODs; p++)
  3352. total += pLodMeshInfo->numVertexes[p];
  3353. // embed the fixup for loader
  3354. pStudioMesh->vertexdata.numLODVertexes[n] = total;
  3355. }
  3356. for (p=n; p<MAX_NUM_LODS; p++)
  3357. {
  3358. // duplicate last valid lod to end of list
  3359. pStudioMesh->vertexdata.numLODVertexes[p] = pStudioMesh->vertexdata.numLODVertexes[numLODs-1];
  3360. }
  3361. // fix the flexes
  3362. for (n=0; n<pStudioMesh->numflexes; n++)
  3363. {
  3364. pStudioFlex = pStudioMesh->pFlex(n);
  3365. byte *pvanim = pStudioFlex->pBaseVertanim();
  3366. int nVAnimSizeBytes = pStudioFlex->VertAnimSizeBytes();
  3367. for (p=0; p<pStudioFlex->numverts; p++, pvanim += nVAnimSizeBytes )
  3368. {
  3369. pStudioVertAnim = (mstudiovertanim_t*)( pvanim );
  3370. if (pStudioVertAnim->index < 0 || pStudioVertAnim->index >= pStudioMesh->numvertices)
  3371. return false;
  3372. // remap old mesh relative vertex index to absolute flat sorted list
  3373. newMeshVertID = pVertexPools[numMeshes+m].pVertexMap[pStudioVertAnim->index];
  3374. // map to expected final fixed vertex locations
  3375. // final fixed vertex location is performed by runtime loading code
  3376. newMeshVertID = pVertexList[newMeshVertID].finalMeshVertID;
  3377. // fixup to expected
  3378. pStudioVertAnim->index = newMeshVertID;
  3379. }
  3380. }
  3381. }
  3382. numMeshes += pStudioModel->nummeshes;
  3383. }
  3384. }
  3385. {
  3386. CP4AutoEditAddFile autop4( fileName, "binary" );
  3387. SaveFile((char*)fileName, (void*)pStudioHdr, pStudioHdr->length);
  3388. }
  3389. // success
  3390. return true;
  3391. }
  3392. //-----------------------------------------------------------------------------
  3393. // FixupToSortedLODVertexes
  3394. //
  3395. // VVD files get vertexes fixed to a flat sorted order, ascending in lower detail lod usage
  3396. // VTX files get their windings remapped to the sort.
  3397. //-----------------------------------------------------------------------------
  3398. bool FixupToSortedLODVertexes(studiohdr_t *pStudioHdr)
  3399. {
  3400. char filename[MAX_PATH];
  3401. char tmpFileName[MAX_PATH];
  3402. void *pVtxBuff;
  3403. usedVertex_t *pVertexList;
  3404. vertexPool_t *pVertexPools;
  3405. int numVertexes;
  3406. int numVertexPools;
  3407. int VtxLen;
  3408. int i;
  3409. const char *vtxPrefixes[] = {".dx80.vtx", ".dx90.vtx", ".sw.vtx"};
  3410. V_strcpy_safe( filename, gamedir );
  3411. // if( *g_pPlatformName )
  3412. // {
  3413. // strcat( filename, "platform_" );
  3414. // strcat( filename, g_pPlatformName );
  3415. // strcat( filename, "/" );
  3416. // }
  3417. V_strcat_safe( filename, "models/" );
  3418. V_strcat_safe( filename, outname );
  3419. Q_StripExtension( filename, filename, sizeof( filename ) );
  3420. // determine lod usage per vertex
  3421. // all vtx files enumerate model's lod verts, but differ in their mesh makeup
  3422. // use xxx.dx80.vtx to establish which vertexes are used by each lod
  3423. V_strcpy_safe( tmpFileName, filename );
  3424. V_strcat_safe( tmpFileName, ".dx80.vtx" );
  3425. VtxLen = LoadFile( tmpFileName, &pVtxBuff );
  3426. // build the sorted vertex tables
  3427. if (!BuildSortedVertexList(pStudioHdr, pVtxBuff, &pVertexPools, &numVertexPools, &pVertexList, &numVertexes))
  3428. {
  3429. // data sync error
  3430. return false;
  3431. }
  3432. // fixup ???.vvd
  3433. V_strcpy_safe( tmpFileName, filename );
  3434. V_strcat_safe( tmpFileName, ".vvd" );
  3435. if (!FixupVVDFile(tmpFileName, pStudioHdr, pVtxBuff, pVertexPools, numVertexPools, pVertexList, numVertexes))
  3436. {
  3437. // data error
  3438. return false;
  3439. }
  3440. for (i=0; i<ARRAYSIZE(vtxPrefixes); i++)
  3441. {
  3442. // fixup ???.vtx
  3443. V_strcpy_safe( tmpFileName, filename );
  3444. V_strcat_safe( tmpFileName, vtxPrefixes[i] );
  3445. if (!FixupVTXFile(tmpFileName, pStudioHdr, pVertexPools, numVertexPools, pVertexList, numVertexes))
  3446. {
  3447. // data error
  3448. return false;
  3449. }
  3450. }
  3451. // fixup ???.mdl
  3452. V_strcpy_safe( tmpFileName, filename );
  3453. V_strcat_safe( tmpFileName, ".mdl" );
  3454. if (!FixupMDLFile(tmpFileName, pStudioHdr, pVtxBuff, pVertexPools, numVertexPools, pVertexList, numVertexes))
  3455. {
  3456. // data error
  3457. return false;
  3458. }
  3459. // free the tables
  3460. for (i=0; i<numVertexPools; i++)
  3461. {
  3462. if (pVertexPools[i].pVertexList)
  3463. free(pVertexPools[i].pVertexList);
  3464. if (pVertexPools[i].pVertexMap)
  3465. free(pVertexPools[i].pVertexMap);
  3466. }
  3467. if (numVertexPools)
  3468. free(pVertexPools);
  3469. free(pVtxBuff);
  3470. // success
  3471. return true;
  3472. }
  3473. byte IsByte( int val )
  3474. {
  3475. if (val < 0 || val > 0xFF)
  3476. {
  3477. MdlError("byte conversion out of range %d\n", val );
  3478. }
  3479. return val;
  3480. }
  3481. char IsChar( int val )
  3482. {
  3483. if (val < -0x80 || val > 0x7F)
  3484. {
  3485. MdlError("char conversion out of range %d\n", val );
  3486. }
  3487. return val;
  3488. }
  3489. int IsInt24( int val )
  3490. {
  3491. if (val < -0x800000 || val > 0x7FFFFF)
  3492. {
  3493. MdlError("int24 conversion out of range %d\n", val );
  3494. }
  3495. return val;
  3496. }
  3497. short IsShort( int val )
  3498. {
  3499. if (val < -0x8000 || val > 0x7FFF)
  3500. {
  3501. MdlError("short conversion out of range %d\n", val );
  3502. }
  3503. return val;
  3504. }
  3505. unsigned short IsUShort( int val )
  3506. {
  3507. if (val < 0 || val > 0xFFFF)
  3508. {
  3509. MdlError("ushort conversion out of range %d\n", val );
  3510. }
  3511. return val;
  3512. }
  3513. bool Clamp_MDL_LODS( const char *fileName, int rootLOD )
  3514. {
  3515. studiohdr_t *pStudioHdr;
  3516. int len;
  3517. len = LoadFile((char*)fileName, (void **)&pStudioHdr);
  3518. Studio_SetRootLOD( pStudioHdr, rootLOD );
  3519. #if 0
  3520. // shift down bone LOD masks
  3521. int iBone;
  3522. for ( iBone = 0; iBone < pStudioHdr->numbones; iBone++)
  3523. {
  3524. mstudiobone_t *pBone = pStudioHdr->pBone( iBone );
  3525. int nLodID;
  3526. for ( nLodID = 0; nLodID < rootLOD; nLodID++)
  3527. {
  3528. int iLodMask = BONE_USED_BY_VERTEX_LOD0 << nLodID;
  3529. if (pBone->flags & (BONE_USED_BY_VERTEX_LOD0 << rootLOD))
  3530. {
  3531. pBone->flags = pBone->flags | iLodMask;
  3532. }
  3533. else
  3534. {
  3535. pBone->flags = pBone->flags & (~iLodMask);
  3536. }
  3537. }
  3538. }
  3539. #endif
  3540. {
  3541. CP4AutoEditAddFile autop4( fileName, "binary" );
  3542. SaveFile( (char *)fileName, pStudioHdr, len );
  3543. }
  3544. return true;
  3545. }
  3546. bool Clamp_VVD_LODS( const char *fileName, int rootLOD )
  3547. {
  3548. vertexFileHeader_t *pTempVvdHdr;
  3549. int len;
  3550. len = LoadFile((char*)fileName, (void **)&pTempVvdHdr);
  3551. int newLength = Studio_VertexDataSize( pTempVvdHdr, rootLOD, true );
  3552. // printf("was %d now %d\n", len, newLength );
  3553. vertexFileHeader_t *pNewVvdHdr = (vertexFileHeader_t *)calloc( newLength, 1 );
  3554. Studio_LoadVertexes( pTempVvdHdr, pNewVvdHdr, rootLOD, true );
  3555. if (!g_quiet)
  3556. {
  3557. printf ("---------------------\n");
  3558. printf ("writing %s:\n", fileName);
  3559. printf( "vertices (%d vertices)\n", pNewVvdHdr->numLODVertexes[ 0 ] );
  3560. }
  3561. // pNewVvdHdr->length = newLength;
  3562. {
  3563. CP4AutoEditAddFile autop4( fileName, "binary" );
  3564. SaveFile( (char *)fileName, pNewVvdHdr, newLength );
  3565. }
  3566. return true;
  3567. }
  3568. bool Clamp_VTX_LODS( const char *fileName, int rootLOD, studiohdr_t *pStudioHdr )
  3569. {
  3570. int i, j, k, m, n;
  3571. int nLodID;
  3572. int size;
  3573. OptimizedModel::FileHeader_t *pVtxHdr;
  3574. int len;
  3575. len = LoadFile((char*)fileName, (void **)&pVtxHdr);
  3576. OptimizedModel::FileHeader_t *pNewVtxHdr = (OptimizedModel::FileHeader_t *)calloc( FILEBUFFER, 1 );
  3577. byte *pData = (byte *)pNewVtxHdr;
  3578. pData += sizeof( OptimizedModel::FileHeader_t );
  3579. ALIGN4( pData );
  3580. // header
  3581. pNewVtxHdr->version = pVtxHdr->version;
  3582. pNewVtxHdr->vertCacheSize = pVtxHdr->vertCacheSize;
  3583. pNewVtxHdr->maxBonesPerStrip = pVtxHdr->maxBonesPerStrip;
  3584. pNewVtxHdr->maxBonesPerTri = pVtxHdr->maxBonesPerTri;
  3585. pNewVtxHdr->maxBonesPerVert = pVtxHdr->maxBonesPerVert;
  3586. pNewVtxHdr->checkSum = pVtxHdr->checkSum;
  3587. pNewVtxHdr->numLODs = pVtxHdr->numLODs;
  3588. // material replacement list
  3589. pNewVtxHdr->materialReplacementListOffset = (pData - (byte *)pNewVtxHdr);
  3590. pData += pVtxHdr->numLODs * sizeof( OptimizedModel::MaterialReplacementListHeader_t );
  3591. // ALIGN4( pData );
  3592. BeginStringTable( );
  3593. // allocate replacement list arrays
  3594. for ( nLodID = rootLOD; nLodID < pVtxHdr->numLODs; nLodID++ )
  3595. {
  3596. OptimizedModel::MaterialReplacementListHeader_t *pReplacementList = pVtxHdr->pMaterialReplacementList( nLodID );
  3597. OptimizedModel::MaterialReplacementListHeader_t *pNewReplacementList = pNewVtxHdr->pMaterialReplacementList( nLodID );
  3598. pNewReplacementList->numReplacements = pReplacementList->numReplacements;
  3599. pNewReplacementList->replacementOffset = (pData - (byte *)pNewReplacementList);
  3600. pData += pNewReplacementList->numReplacements * sizeof( OptimizedModel::MaterialReplacementHeader_t );
  3601. // ALIGN4( pData );
  3602. for (i = 0; i < pReplacementList->numReplacements; i++)
  3603. {
  3604. OptimizedModel::MaterialReplacementHeader_t *pReplacement = pReplacementList->pMaterialReplacement( i );
  3605. OptimizedModel::MaterialReplacementHeader_t *pNewReplacement = pNewReplacementList->pMaterialReplacement( i );
  3606. pNewReplacement->materialID = pReplacement->materialID;
  3607. AddToStringTable( pNewReplacement, &pNewReplacement->replacementMaterialNameOffset, pReplacement->pMaterialReplacementName() );
  3608. }
  3609. }
  3610. pData = WriteStringTable( pData );
  3611. // link previous LODs to higher LODs
  3612. for ( nLodID = 0; nLodID < rootLOD; nLodID++ )
  3613. {
  3614. OptimizedModel::MaterialReplacementListHeader_t *pRootReplacementList = pNewVtxHdr->pMaterialReplacementList( rootLOD );
  3615. OptimizedModel::MaterialReplacementListHeader_t *pNewReplacementList = pNewVtxHdr->pMaterialReplacementList( nLodID );
  3616. int delta = (byte *)pRootReplacementList - (byte *)pNewReplacementList;
  3617. pNewReplacementList->numReplacements = pRootReplacementList->numReplacements;
  3618. pNewReplacementList->replacementOffset = pRootReplacementList->replacementOffset + delta;
  3619. }
  3620. // body parts
  3621. pNewVtxHdr->numBodyParts = pStudioHdr->numbodyparts;
  3622. pNewVtxHdr->bodyPartOffset = (pData - (byte *)pNewVtxHdr);
  3623. pData += pNewVtxHdr->numBodyParts * sizeof( OptimizedModel::BodyPartHeader_t );
  3624. // ALIGN4( pData );
  3625. // Iterate over every body part...
  3626. for ( i = 0; i < pStudioHdr->numbodyparts; i++ )
  3627. {
  3628. mstudiobodyparts_t* pBodyPart = pStudioHdr->pBodypart(i);
  3629. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart(i);
  3630. OptimizedModel::BodyPartHeader_t* pNewVtxBodyPart = pNewVtxHdr->pBodyPart(i);
  3631. pNewVtxBodyPart->numModels = pBodyPart->nummodels;
  3632. pNewVtxBodyPart->modelOffset = (pData - (byte *)pNewVtxBodyPart);
  3633. pData += pNewVtxBodyPart->numModels * sizeof( OptimizedModel::ModelHeader_t );
  3634. // ALIGN4( pData );
  3635. // Iterate over every submodel...
  3636. for (j = 0; j < pBodyPart->nummodels; ++j)
  3637. {
  3638. mstudiomodel_t* pModel = pBodyPart->pModel(j);
  3639. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel(j);
  3640. OptimizedModel::ModelHeader_t* pNewVtxModel = pNewVtxBodyPart->pModel(j);
  3641. pNewVtxModel->numLODs = pVtxModel->numLODs;
  3642. pNewVtxModel->lodOffset = (pData - (byte *)pNewVtxModel);
  3643. pData += pNewVtxModel->numLODs * sizeof( OptimizedModel::ModelLODHeader_t );
  3644. ALIGN4( pData );
  3645. for ( nLodID = rootLOD; nLodID < pVtxModel->numLODs; nLodID++ )
  3646. {
  3647. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLodID );
  3648. OptimizedModel::ModelLODHeader_t *pNewVtxLOD = pNewVtxModel->pLOD( nLodID );
  3649. pNewVtxLOD->numMeshes = pVtxLOD->numMeshes;
  3650. pNewVtxLOD->switchPoint = pVtxLOD->switchPoint;
  3651. pNewVtxLOD->meshOffset = (pData - (byte *)pNewVtxLOD);
  3652. pData += pNewVtxLOD->numMeshes * sizeof( OptimizedModel::MeshHeader_t );
  3653. ALIGN4( pData );
  3654. // Iterate over all the meshes....
  3655. for (k = 0; k < pModel->nummeshes; ++k)
  3656. {
  3657. Assert( pModel->nummeshes == pVtxLOD->numMeshes );
  3658. // mstudiomesh_t* pMesh = pModel->pMesh(k);
  3659. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh(k);
  3660. OptimizedModel::MeshHeader_t* pNewVtxMesh = pNewVtxLOD->pMesh(k);
  3661. pNewVtxMesh->numStripGroups = pVtxMesh->numStripGroups;
  3662. pNewVtxMesh->flags = pVtxMesh->flags;
  3663. pNewVtxMesh->stripGroupHeaderOffset = (pData - (byte *)pNewVtxMesh);
  3664. pData += pNewVtxMesh->numStripGroups * sizeof( OptimizedModel::StripGroupHeader_t );
  3665. // printf("part %d : model %d : lod %d : mesh %d : strips %d : offset %d\n", i, j, nLodID, k, pVtxMesh->numStripGroups, pVtxMesh->stripGroupHeaderOffset );
  3666. for (m = 0; m < pVtxMesh->numStripGroups; m++)
  3667. {
  3668. OptimizedModel::StripGroupHeader_t *pStripGroup = pVtxMesh->pStripGroup( m );
  3669. OptimizedModel::StripGroupHeader_t *pNewStripGroup = pNewVtxMesh->pStripGroup( m );
  3670. // int delta = ((byte *)pStripGroup - (byte *)pVtxHdr) - ((byte *)pNewStripGroup - (byte *)pNewVtxHdr);
  3671. pNewStripGroup->numVerts = pStripGroup->numVerts;
  3672. pNewStripGroup->vertOffset = (pData - (byte *)pNewStripGroup);
  3673. size = pNewStripGroup->numVerts * sizeof( OptimizedModel::Vertex_t );
  3674. memcpy( pData, pStripGroup->pVertex(0), size );
  3675. pData += size;
  3676. pNewStripGroup->numIndices = pStripGroup->numIndices;
  3677. pNewStripGroup->indexOffset = (pData - (byte *)pNewStripGroup);
  3678. size = pNewStripGroup->numIndices * sizeof( unsigned short );
  3679. memcpy( pData, pStripGroup->pIndex(0), size );
  3680. pData += size;
  3681. pNewStripGroup->numStrips = pStripGroup->numStrips;
  3682. pNewStripGroup->stripOffset = (pData - (byte *)pNewStripGroup);
  3683. size = pNewStripGroup->numStrips * sizeof( OptimizedModel::StripHeader_t );
  3684. pData += size;
  3685. pNewStripGroup->flags = pStripGroup->flags;
  3686. /*
  3687. printf("\tnumVerts %d %d :\n", pStripGroup->numVerts, pStripGroup->vertOffset );
  3688. printf("\tnumIndices %d %d :\n", pStripGroup->numIndices, pStripGroup->indexOffset );
  3689. printf("\tnumStrips %d %d :\n", pStripGroup->numStrips, pStripGroup->stripOffset );
  3690. */
  3691. for (n = 0; n < pStripGroup->numStrips; n++)
  3692. {
  3693. OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( n );
  3694. OptimizedModel::StripHeader_t *pNewStrip = pNewStripGroup->pStrip( n );
  3695. pNewStrip->numIndices = pStrip->numIndices;
  3696. pNewStrip->indexOffset = pStrip->indexOffset;
  3697. pNewStrip->numVerts = pStrip->numVerts;
  3698. pNewStrip->vertOffset = pStrip->vertOffset;
  3699. pNewStrip->numBones = pStrip->numBones;
  3700. pNewStrip->flags = pStrip->flags;
  3701. pNewStrip->numBoneStateChanges = pStrip->numBoneStateChanges;
  3702. pNewStrip->boneStateChangeOffset = (pData - (byte *)pNewStrip);
  3703. size = pNewStrip->numBoneStateChanges * sizeof( OptimizedModel::BoneStateChangeHeader_t );
  3704. memcpy( pData, pStrip->pBoneStateChange(0), size );
  3705. pData += size;
  3706. /*
  3707. printf("\t\tnumIndices %d %d :\n", pNewStrip->numIndices, pNewStrip->indexOffset );
  3708. printf("\t\tnumVerts %d %d :\n", pNewStrip->numVerts, pNewStrip->vertOffset );
  3709. printf("\t\tnumBoneStateChanges %d %d :\n", pNewStrip->numBoneStateChanges, pNewStrip->boneStateChangeOffset );
  3710. */
  3711. // printf("(%d)\n", delta );
  3712. }
  3713. // printf("(%d)\n", delta );
  3714. }
  3715. }
  3716. }
  3717. }
  3718. }
  3719. // Iterate over every body part...
  3720. for ( i = 0; i < pStudioHdr->numbodyparts; i++ )
  3721. {
  3722. mstudiobodyparts_t* pBodyPart = pStudioHdr->pBodypart(i);
  3723. // Iterate over every submodel...
  3724. for (j = 0; j < pBodyPart->nummodels; ++j)
  3725. {
  3726. // link previous LODs to higher LODs
  3727. for ( nLodID = 0; nLodID < rootLOD; nLodID++ )
  3728. {
  3729. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxHdr->pBodyPart(i)->pModel(j)->pLOD(nLodID);
  3730. OptimizedModel::ModelLODHeader_t *pRootVtxLOD = pNewVtxHdr->pBodyPart(i)->pModel(j)->pLOD(rootLOD);
  3731. OptimizedModel::ModelLODHeader_t *pNewVtxLOD = pNewVtxHdr->pBodyPart(i)->pModel(j)->pLOD(nLodID);
  3732. pNewVtxLOD->numMeshes = pRootVtxLOD->numMeshes;
  3733. pNewVtxLOD->switchPoint = pVtxLOD->switchPoint;
  3734. int delta = (byte *)pRootVtxLOD - (byte *)pNewVtxLOD;
  3735. pNewVtxLOD->meshOffset = pRootVtxLOD->meshOffset + delta;
  3736. }
  3737. }
  3738. }
  3739. int newLen = pData - (byte *)pNewVtxHdr;
  3740. // printf("len %d : %d\n", len, newLen );
  3741. // pNewVtxHdr->length = newLen;
  3742. if (!g_quiet)
  3743. {
  3744. printf ("writing %s:\n", fileName);
  3745. printf( "everything (%d bytes)\n", newLen );
  3746. }
  3747. {
  3748. CP4AutoEditAddFile autop4( fileName, "binary" );
  3749. SaveFile( (char *)fileName, pNewVtxHdr, newLen );
  3750. }
  3751. free( pNewVtxHdr );
  3752. return true;
  3753. }
  3754. bool Clamp_RootLOD( studiohdr_t *phdr )
  3755. {
  3756. char filename[MAX_PATH];
  3757. char tmpFileName[MAX_PATH];
  3758. int i;
  3759. const char *vtxPrefixes[] = {".dx80.vtx", ".dx90.vtx", ".sw.vtx"};
  3760. int rootLOD = g_minLod;
  3761. if (rootLOD > g_ScriptLODs.Size() - 1)
  3762. {
  3763. rootLOD = g_ScriptLODs.Size() -1;
  3764. }
  3765. if (rootLOD == 0)
  3766. {
  3767. return true;
  3768. }
  3769. V_strcpy_safe( filename, gamedir );
  3770. V_strcat_safe( filename, "models/" );
  3771. V_strcat_safe( filename, outname );
  3772. Q_StripExtension( filename, filename, sizeof( filename ) );
  3773. // shift the files so that g_minLod is the root LOD
  3774. V_strcpy_safe( tmpFileName, filename );
  3775. V_strcat_safe( tmpFileName, ".mdl" );
  3776. Clamp_MDL_LODS( tmpFileName, rootLOD );
  3777. V_strcpy_safe( tmpFileName, filename );
  3778. V_strcat_safe( tmpFileName, ".vvd" );
  3779. Clamp_VVD_LODS( tmpFileName, rootLOD );
  3780. for (i=0; i<ARRAYSIZE(vtxPrefixes); i++)
  3781. {
  3782. // fixup ???.vtx
  3783. V_strcpy_safe( tmpFileName, filename );
  3784. V_strcat_safe( tmpFileName, vtxPrefixes[i] );
  3785. Clamp_VTX_LODS( tmpFileName, rootLOD, phdr );
  3786. }
  3787. return true;
  3788. }
  3789. //----------------------------------------------------------------------
  3790. // For a particular .qc, converts all studiomdl generated files to big-endian format.
  3791. //----------------------------------------------------------------------
  3792. void WriteSwappedFile( char *srcname, char *outname, int(*pfnSwapFunc)(void*, const void*, int) )
  3793. {
  3794. if ( FileExists( srcname ) )
  3795. {
  3796. if( !g_quiet )
  3797. {
  3798. printf( "---------------------\n" );
  3799. printf( "Generating Xbox360 file format for \"%s\":\n", srcname );
  3800. }
  3801. void *pFileBase = NULL;
  3802. int fileSize = LoadFile( srcname, &pFileBase );
  3803. int paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
  3804. void *pOutBase = malloc( paddedSize );
  3805. int bytes = pfnSwapFunc( pOutBase, pFileBase, fileSize );
  3806. if ( bytes != 0 )
  3807. {
  3808. CP4AutoEditAddFile autop4( outname, "binary" );
  3809. SaveFile( outname, pOutBase, bytes );
  3810. }
  3811. free(pOutBase);
  3812. free(pFileBase);
  3813. if ( bytes == 0 )
  3814. {
  3815. MdlError( "Aborted byteswap on '%s':\n", srcname );
  3816. }
  3817. }
  3818. }
  3819. //----------------------------------------------------------------------
  3820. // For a particular .qc, converts all studiomdl generated files to big-endian format.
  3821. //----------------------------------------------------------------------
  3822. void WriteAllSwappedFiles( const char *filename )
  3823. {
  3824. char srcname[ MAX_PATH ];
  3825. char outname[ MAX_PATH ];
  3826. extern IPhysicsCollision *physcollision;
  3827. if ( physcollision )
  3828. {
  3829. StudioByteSwap::SetCollisionInterface( physcollision );
  3830. }
  3831. // Convert PHY
  3832. Q_StripExtension( filename, srcname, sizeof( srcname ) );
  3833. Q_strncpy( outname, srcname, sizeof( outname ) );
  3834. Q_strcat( srcname, ".phy", sizeof( srcname ) );
  3835. Q_strcat( outname, ".360.phy", sizeof( outname ) );
  3836. WriteSwappedFile( srcname, outname, StudioByteSwap::ByteswapPHY );
  3837. // Convert VVD
  3838. Q_StripExtension( filename, srcname, sizeof( srcname ) );
  3839. Q_strncpy( outname, srcname, sizeof( outname ) );
  3840. Q_strcat( srcname, ".vvd", sizeof( srcname ) );
  3841. Q_strcat( outname, ".360.vvd", sizeof( outname ) );
  3842. WriteSwappedFile( srcname, outname, StudioByteSwap::ByteswapVVD );
  3843. // Convert VTX
  3844. Q_StripExtension( filename, srcname, sizeof( srcname ) );
  3845. Q_StripExtension( srcname, srcname, sizeof( srcname ) );
  3846. Q_strncpy( outname, srcname, sizeof( outname ) );
  3847. Q_strcat( srcname, ".dx90.vtx", sizeof( srcname ) );
  3848. Q_strcat( outname, ".360.vtx", sizeof( outname ) );
  3849. WriteSwappedFile( srcname, outname, StudioByteSwap::ByteswapVTX );
  3850. // Convert MDL
  3851. Q_StripExtension( filename, srcname, sizeof( srcname ) );
  3852. Q_strncpy( outname, srcname, sizeof( outname ) );
  3853. Q_strcat( srcname, ".mdl", sizeof( srcname ) );
  3854. Q_strcat( outname, ".360.mdl", sizeof( outname ) );
  3855. WriteSwappedFile( srcname, outname, StudioByteSwap::ByteswapMDL );
  3856. }