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.

691 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: build a sheet data file and a large image out of multiple images
  4. //
  5. //===========================================================================//
  6. #include "tier0/platform.h"
  7. #include "tier0/progressbar.h"
  8. #include "bitmap/float_bm.h"
  9. #include "mathlib/mathlib.h"
  10. #include "tier2/tier2.h"
  11. #include "tier0/memdbgon.h"
  12. #include "filesystem.h"
  13. #include "tier1/utlstringmap.h"
  14. #include "tier1/strtools.h"
  15. #include "tier1/utlmap.h"
  16. #include "bitmap/float_bm.h"
  17. #include "tier2/fileutils.h"
  18. #include "stdlib.h"
  19. #include "tier0/dbg.h"
  20. #define MAX_IMAGES_PER_FRAME 4
  21. struct Sequence;
  22. struct SequenceFrame
  23. {
  24. SequenceFrame() : m_mapSequences( DefLessFunc( Sequence * ) ) {}
  25. FloatBitMap_t *m_pImage;
  26. int m_XCoord, m_YCoord; // where it ended up packed
  27. CUtlMap< Sequence *, int > m_mapSequences;
  28. };
  29. enum PackingMode_t
  30. {
  31. PCKM_INVALID = 0,
  32. PCKM_FLAT, // Default mode - every frame consumes entire RGBA space
  33. PCKM_RGB_A, // Some sequences consume RGB space and some Alpha space
  34. };
  35. static CUtlStringMap<SequenceFrame *> ImageList;
  36. static PackingMode_t s_ePackingMode = PCKM_FLAT;
  37. struct SequenceEntry
  38. {
  39. SequenceFrame *m_pSeqFrame[MAX_IMAGES_PER_FRAME];
  40. float m_fDisplayTime;
  41. };
  42. struct Sequence
  43. {
  44. enum SeqMode_t
  45. {
  46. SQM_RGBA = 0, // Sequence occupies entire RGBA space
  47. SQM_RGB = 1, // Sequence occupies only RGB space
  48. SQM_ALPHA = 2 // Sequence occupies only Alpha space
  49. };
  50. int m_nSequenceNumber;
  51. bool m_Clamp; // as opposed to loop
  52. SeqMode_t m_eMode;
  53. CUtlVector<SequenceEntry> m_Frames;
  54. Sequence( void )
  55. {
  56. m_Clamp = true;
  57. m_eMode = SQM_RGBA;
  58. }
  59. };
  60. static int GetChannelIndexFromChar( char c )
  61. {
  62. // r->0 b->1 g->2 a->3 else -1
  63. static char s_ChannelIDs[]="rgba";
  64. char const *pChanChar = strchr( s_ChannelIDs, c );
  65. if ( ! pChanChar )
  66. {
  67. printf( " bad channel name '%c'\n", c );
  68. return -1;
  69. }
  70. else
  71. return pChanChar - s_ChannelIDs;
  72. }
  73. static FloatBitMap_t *CreateFBM( const char * fname )
  74. {
  75. if ( strchr( fname, ',' ) )
  76. {
  77. // parse extended specifications
  78. CUtlVector<char *> Images;
  79. V_SplitString( fname, ",", Images);
  80. FloatBitMap_t *pBM = NULL;
  81. // now, process bitmaps, performing copy operations specified by {} syntax
  82. for(int i=0; i < Images.Count(); i++)
  83. {
  84. char fnamebuf[MAX_PATH];
  85. strcpy( fnamebuf, Images[i] );
  86. char * pBrace=strchr( fnamebuf, '{' );
  87. if ( pBrace )
  88. {
  89. *pBrace = 0; // null it
  90. pBrace++; // point at control specifier
  91. char *pEndBrace = strchr( pBrace, '}' );
  92. if ( ! pEndBrace )
  93. printf( "bad extended bitmap synax (no close brace) - %s \n", Images[i] );
  94. }
  95. FloatBitMap_t NewBM( fnamebuf );
  96. if ( ! pBM )
  97. {
  98. // first image sets size
  99. pBM = new FloatBitMap_t( &NewBM );
  100. }
  101. // now, process operation specifiers of the form "{chan=chan}" or "{chan=0}"
  102. if ( pBrace )
  103. {
  104. if ( pBrace[1] == '=' )
  105. {
  106. int nDstChan = GetChannelIndexFromChar( pBrace[0] );
  107. if ( nDstChan != -1 )
  108. {
  109. if ( pBrace[2] == '0' )
  110. {
  111. // zero the channel
  112. for(int y = 0; y < NewBM.Height; y++ )
  113. for(int x=0; x < NewBM.Width; x++ )
  114. {
  115. pBM->Pixel( x, y, nDstChan ) = 0;
  116. }
  117. }
  118. else
  119. {
  120. int nSrcChan = GetChannelIndexFromChar( pBrace[2] );
  121. if ( nSrcChan != -1 )
  122. {
  123. // perform the channel copy
  124. for(int y = 0; y < NewBM.Height; y++ )
  125. for(int x=0; x < NewBM.Width; x++ )
  126. {
  127. pBM->Pixel( x, y, nDstChan ) = NewBM.Pixel( x, y, nSrcChan );
  128. }
  129. }
  130. }
  131. }
  132. }
  133. }
  134. }
  135. return pBM;
  136. }
  137. else
  138. return new FloatBitMap_t( fname );
  139. }
  140. static CUtlVector< Sequence *> Sequences;
  141. static Sequence *pCurSequence=NULL;
  142. static int s_nWidth;
  143. static int s_nHeight;
  144. static void ApplyMacros( char * in_buf )
  145. {
  146. CUtlVector<char *> Words;
  147. V_SplitString( in_buf, " ", Words);
  148. if ( ( Words.Count() == 4 ) && (! stricmp( Words[0],"ga_frame") ) )
  149. {
  150. // ga_frame frm1 frm2 n -> frame frm1{r=a},frm1{g=a},frm1{b=a},frm2{a=a} n
  151. sprintf(in_buf, "frame %s{r=0},%s{g=a},%s{b=0},%s{a=a} %s",
  152. Words[1],Words[1],Words[1],Words[2],Words[3]);
  153. }
  154. Words.PurgeAndDeleteElements();
  155. }
  156. static void ReadTextControlFile( char const *fname )
  157. {
  158. CRequiredInputTextFile f( fname );
  159. char linebuffer[4096];
  160. bool numActualLinesRead = 0;
  161. while ( f.ReadLine( linebuffer, sizeof(linebuffer) ) )
  162. {
  163. ++ numActualLinesRead;
  164. // kill newline
  165. char *pChop=strchr( linebuffer, '\n' );
  166. if ( pChop )
  167. *pChop = 0;
  168. char *comment=Q_strstr( linebuffer, "//" );
  169. if ( comment )
  170. *comment = 0;
  171. char *in_str=linebuffer;
  172. while( ( in_str[0]==' ' ) || ( in_str[0]=='\t') )
  173. in_str++;
  174. if (in_str[0])
  175. {
  176. strlwr( in_str );
  177. ApplyMacros( in_str );
  178. CUtlVector<char *> Words;
  179. V_SplitString( in_str, " ", Words);
  180. if ( ( Words.Count() == 1) && (! stricmp( Words[0],"loop" ) ) )
  181. {
  182. if ( pCurSequence )
  183. pCurSequence->m_Clamp = false;
  184. }
  185. else if ( ( Words.Count() == 2 ) && (! stricmp( Words[0], "packmode" ) ) )
  186. {
  187. PackingMode_t eRequestedMode = PCKM_INVALID;
  188. if ( !stricmp( Words[1], "flat" ) || !stricmp( Words[1], "rgba" ) )
  189. eRequestedMode = PCKM_FLAT;
  190. else if ( !stricmp( Words[1], "rgb+a" ) )
  191. eRequestedMode = PCKM_RGB_A;
  192. if ( eRequestedMode == PCKM_INVALID )
  193. printf( "*** line %d: invalid packmode specified, allowed values are 'rgba' or 'rgb+a'!\n", numActualLinesRead ),
  194. exit( -1 );
  195. else if ( !Sequences.Count() )
  196. s_ePackingMode = eRequestedMode;
  197. else if ( s_ePackingMode != eRequestedMode )
  198. {
  199. // Allow special changes:
  200. // flat -> rgb+a
  201. if ( s_ePackingMode == PCKM_FLAT && eRequestedMode == PCKM_RGB_A )
  202. s_ePackingMode = eRequestedMode;
  203. // everything else
  204. else
  205. printf( "*** line %d: incompatible packmode change when %d sequences already defined!\n", numActualLinesRead, Sequences.Count() ),
  206. exit( -1 );
  207. }
  208. }
  209. else if ( ( Words.Count() == 2) && StringHasPrefix( Words[0], "sequence" ) )
  210. {
  211. int seq_no = atoi( Words[1] );
  212. pCurSequence = new Sequence;
  213. pCurSequence->m_nSequenceNumber = seq_no;
  214. // Figure out the sequence type
  215. char const *szSeqType = StringAfterPrefix( Words[0], "sequence" );
  216. if ( !stricmp( szSeqType, "" ) || !stricmp( szSeqType, "-rgba" ) )
  217. pCurSequence->m_eMode = Sequence::SQM_RGBA;
  218. else if ( !stricmp( szSeqType, "-rgb" ) )
  219. pCurSequence->m_eMode = Sequence::SQM_RGB;
  220. else if ( !stricmp( szSeqType, "-a" ) )
  221. pCurSequence->m_eMode = Sequence::SQM_ALPHA;
  222. else
  223. printf( "*** line %d: invalid sequence type '%s', allowed 'sequence-rgba' or 'sequence-rgb' or 'sequence-a'!\n", numActualLinesRead, Words[0] ),
  224. exit( -1 );
  225. // Validate sequence type
  226. switch ( s_ePackingMode )
  227. {
  228. case PCKM_FLAT:
  229. switch ( pCurSequence->m_eMode )
  230. {
  231. case Sequence::SQM_RGBA: break;
  232. default:
  233. printf( "*** line %d: invalid sequence type '%s', packing 'flat' allows only 'sequence-rgba'!\n", numActualLinesRead, Words[0] ),
  234. exit( -1 );
  235. }
  236. break;
  237. case PCKM_RGB_A:
  238. switch ( pCurSequence->m_eMode )
  239. {
  240. case Sequence::SQM_RGB:
  241. case Sequence::SQM_ALPHA: break;
  242. default:
  243. printf( "*** line %d: invalid sequence type '%s', packing 'rgb+a' allows only 'sequence-rgb' or 'sequence-a'!\n", numActualLinesRead, Words[0] ),
  244. exit( -1 );
  245. }
  246. break;
  247. }
  248. Sequences.AddToTail( pCurSequence );
  249. }
  250. else if ( ( Words.Count() >= 3) && (! stricmp( Words[0],"frame" ) ) )
  251. {
  252. if ( pCurSequence )
  253. {
  254. float ftime = atof( Words[ Words.Count() - 1 ] );
  255. SequenceEntry new_entry;
  256. new_entry.m_fDisplayTime = ftime;
  257. for(int i=0;i < Words.Count()-2; i++)
  258. {
  259. SequenceFrame *pBM;
  260. char * fnamebuf = Words[i+1];
  261. if ( ! ( ImageList.Defined( fnamebuf ) ) )
  262. {
  263. SequenceFrame *pNew_frm = new SequenceFrame;
  264. pNew_frm->m_pImage = CreateFBM( fnamebuf );
  265. pBM=pNew_frm;
  266. ImageList[ fnamebuf ] = pNew_frm;
  267. }
  268. else
  269. pBM = ImageList[ fnamebuf ];
  270. new_entry.m_pSeqFrame[i] = pBM;
  271. // Validate that frame packing is correct
  272. if ( s_ePackingMode == PCKM_RGB_A )
  273. {
  274. for ( uint16 idx = 0; idx < pBM->m_mapSequences.Count(); ++ idx )
  275. {
  276. Sequence *pSeq = pBM->m_mapSequences.Key( idx );
  277. if ( pSeq->m_eMode != Sequence::SQM_RGBA &&
  278. pSeq->m_eMode != pCurSequence->m_eMode )
  279. {
  280. printf( "*** line %d: 'rgb+a' packing cannot pack frame '%s' belonging to sequences %d and %d!\n", numActualLinesRead,
  281. fnamebuf,
  282. pSeq->m_nSequenceNumber, pCurSequence->m_nSequenceNumber ),
  283. exit( -1 );
  284. }
  285. }
  286. }
  287. pBM->m_mapSequences.Insert( pCurSequence, 1 );
  288. if (i == 0 )
  289. for( int j=1; j<MAX_IMAGES_PER_FRAME; j++ )
  290. new_entry.m_pSeqFrame[j]=new_entry.m_pSeqFrame[0];
  291. }
  292. pCurSequence->m_Frames.AddToTail( new_entry );
  293. }
  294. }
  295. else
  296. {
  297. printf("*** line %d: Bad command \"%s\"!\n", numActualLinesRead, in_str ),
  298. exit( -1 );
  299. }
  300. Words.PurgeAndDeleteElements();
  301. }
  302. }
  303. }
  304. inline float UCoord( int u )
  305. {
  306. float uc=u+0.5;
  307. return uc/(float) s_nWidth;
  308. }
  309. inline float VCoord( int v )
  310. {
  311. float vc=v+0.5;
  312. return vc/(float) s_nHeight;
  313. }
  314. bool PackImages_Flat( char const *pFname, int nWidth )
  315. {
  316. // !! bug !! packing algorithm is dumb and no error checking is done!
  317. FloatBitMap_t output( nWidth, 2048);
  318. int cur_line=0;
  319. int cur_column=0;
  320. int next_line=0;
  321. int max_column_written=0;
  322. for(int i=0; i < ImageList.GetNumStrings(); i++)
  323. {
  324. SequenceFrame &frm=*(ImageList[i]);
  325. if ( cur_column+frm.m_pImage->Width > output.Width )
  326. {
  327. // no room!
  328. cur_column = 0;
  329. cur_line = next_line;
  330. next_line = cur_line;
  331. }
  332. // now, pack
  333. if ( ( cur_column+frm.m_pImage->Width > output.Width ) ||
  334. ( cur_line+frm.m_pImage->Height > output.Height ) )
  335. {
  336. return false; // didn't fit! doh
  337. }
  338. frm.m_XCoord=cur_column;
  339. frm.m_YCoord=cur_line;
  340. if ( pFname ) // don't actually pack the pixel if we're not keeping them
  341. {
  342. for(int y=0;y<frm.m_pImage->Height; y++)
  343. for(int x=0;x<frm.m_pImage->Width; x++)
  344. for(int c=0;c<4;c++)
  345. {
  346. output.Pixel(x+cur_column,y+cur_line, c)=
  347. frm.m_pImage->Pixel(x, y, c);
  348. }
  349. }
  350. next_line=max(next_line, cur_line+frm.m_pImage->Height );
  351. cur_column += frm.m_pImage->Width;
  352. max_column_written=max(max_column_written, cur_column);
  353. }
  354. // now, truncate height
  355. int h=1;
  356. for(h; h<next_line; h*=2)
  357. ;
  358. // truncate width;
  359. int w=1;
  360. for(1; w<max_column_written; w*=2)
  361. ;
  362. if ( pFname )
  363. {
  364. FloatBitMap_t cropped_output( w, h);
  365. for(int y=0;y<cropped_output.Height; y++)
  366. for(int x=0;x<cropped_output.Width; x++)
  367. for(int c=0;c<4;c++)
  368. cropped_output.Pixel(x,y,c) = output.Pixel(x,y,c);
  369. bool bWritten = cropped_output.WriteTGAFile( pFname );
  370. if ( !bWritten )
  371. printf( "Error: failed to save TGA \"%s\"!\n", pFname );
  372. else
  373. printf( "Ok: successfully saved TGA \"%s\"\n", pFname );
  374. }
  375. // Store these for UV calculation later on
  376. s_nHeight = h;
  377. s_nWidth = w;
  378. return true;
  379. }
  380. bool PackImages_Rgb_A( char const *pFname, int nWidth )
  381. {
  382. // !! bug !! packing algorithm is dumb and no error checking is done!
  383. FloatBitMap_t output( nWidth, 2048);
  384. int cur_line[2] = {0};
  385. int cur_column[2] = {0};
  386. int next_line[2] = {0};
  387. int max_column_written[2] = {0};
  388. bool bPackingRGBA = true;
  389. for(int i=0; i < ImageList.GetNumStrings(); i++)
  390. {
  391. SequenceFrame &frm=*(ImageList[i]);
  392. int idxfrm;
  393. Sequence::SeqMode_t eMode = frm.m_mapSequences.Key( 0 )->m_eMode;
  394. switch ( eMode )
  395. {
  396. case Sequence::SQM_RGB: idxfrm = 0; bPackingRGBA = false; break;
  397. case Sequence::SQM_ALPHA: idxfrm = 1; bPackingRGBA = false; break;
  398. case Sequence::SQM_RGBA:
  399. if ( !bPackingRGBA )
  400. printf( "*** error when packing 'rgb+a', bad sequence %d encountered for frame '%s' after all rgba frames packed!\n", frm.m_mapSequences.Key( 0 )->m_nSequenceNumber, ImageList.String( i ) ),
  401. exit( -1 );
  402. idxfrm = 0; break;
  403. default:
  404. printf( "*** error when packing 'rgb+a', bad sequence %d encountered for frame '%s'!\n", frm.m_mapSequences.Key( 0 )->m_nSequenceNumber, ImageList.String( i ) ),
  405. exit( -1 );
  406. }
  407. if ( cur_column[idxfrm] + frm.m_pImage->Width > output.Width )
  408. {
  409. // no room!
  410. cur_column[idxfrm] = 0;
  411. cur_line[idxfrm] = next_line[idxfrm];
  412. next_line[idxfrm] = cur_line[idxfrm];
  413. }
  414. // now, pack
  415. if ( ( cur_column[idxfrm] + frm.m_pImage->Width > output.Width ) ||
  416. ( cur_line[idxfrm] + frm.m_pImage->Height > output.Height ) )
  417. {
  418. return false; // didn't fit! doh
  419. }
  420. frm.m_XCoord = cur_column[idxfrm];
  421. frm.m_YCoord = cur_line[idxfrm];
  422. if ( pFname ) // don't actually pack the pixel if we're not keeping them
  423. {
  424. for ( int y = 0; y < frm.m_pImage->Height; y++ )
  425. for (int x = 0; x < frm.m_pImage->Width; x++ )
  426. for(int c = 0; c < 4; c ++)
  427. switch ( eMode )
  428. {
  429. case Sequence::SQM_RGB: if ( c < 3 ) goto setpx; else break;
  430. case Sequence::SQM_ALPHA: if ( c == 3 ) goto setpx; else break;
  431. case Sequence::SQM_RGBA: if ( c < 4 ) goto setpx; else break;
  432. setpx:
  433. output.Pixel( x + cur_column[idxfrm], y + cur_line[idxfrm], c ) = frm.m_pImage->Pixel(x, y, c);
  434. }
  435. }
  436. next_line[idxfrm] = max( next_line[idxfrm], cur_line[idxfrm] + frm.m_pImage->Height );
  437. cur_column[idxfrm] += frm.m_pImage->Width;
  438. max_column_written[idxfrm] = max( max_column_written[idxfrm], cur_column[idxfrm] );
  439. if ( bPackingRGBA )
  440. {
  441. cur_line[1] = cur_line[0];
  442. cur_column[1] = cur_column[0];
  443. next_line[1] = next_line[0];
  444. max_column_written[1] = max_column_written[0];
  445. }
  446. }
  447. // now, truncate height
  448. int h=1;
  449. for ( int idxfrm = 0; idxfrm < 2; ++ idxfrm )
  450. for ( h; h < next_line[idxfrm]; h*=2 )
  451. continue;
  452. // truncate width;
  453. int w=1;
  454. for ( int idxfrm = 0; idxfrm < 2; ++ idxfrm )
  455. for ( w; w < max_column_written[idxfrm]; w*=2 )
  456. continue;
  457. if ( pFname )
  458. {
  459. FloatBitMap_t cropped_output( w, h );
  460. for(int y=0;y<cropped_output.Height; y++)
  461. for(int x=0;x<cropped_output.Width; x++)
  462. for(int c=0;c<4;c++)
  463. cropped_output.Pixel(x,y,c) = output.Pixel(x,y,c);
  464. bool bWritten = cropped_output.WriteTGAFile( pFname );
  465. if ( !bWritten )
  466. printf( "Error: failed to save TGA \"%s\"!\n", pFname );
  467. else
  468. printf( "Ok: successfully saved TGA \"%s\"\n", pFname );
  469. }
  470. // Store these for UV calculation later on
  471. s_nHeight = h;
  472. s_nWidth = w;
  473. return true;
  474. }
  475. bool PackImages( char const *pFname, int nWidth )
  476. {
  477. switch ( s_ePackingMode )
  478. {
  479. case PCKM_FLAT:
  480. return PackImages_Flat( pFname, nWidth );
  481. case PCKM_RGB_A:
  482. return PackImages_Rgb_A( pFname, nWidth );
  483. case PCKM_INVALID:
  484. default:
  485. return false;
  486. }
  487. }
  488. void main(int argc,char **argv)
  489. {
  490. InitCommandLineProgram( argc, argv );
  491. if ( argc < 2 || argc > 4 )
  492. {
  493. printf( "format is 'mksheet sheet.mks [output.sht] [output.tga]'\n" );
  494. return;
  495. }
  496. char pMksFileBuf[MAX_PATH];
  497. char pShtFileBuf[MAX_PATH];
  498. char pTgaFileBuf[MAX_PATH];
  499. const char *pSourceFile;
  500. const char *pShtFile;
  501. const char *pTgaFile;
  502. Q_strncpy( pMksFileBuf, argv[1], sizeof(pMksFileBuf) );
  503. Q_DefaultExtension( pMksFileBuf, ".mks", sizeof(pMksFileBuf) );
  504. pSourceFile = pMksFileBuf;
  505. if ( argc < 4 )
  506. {
  507. Q_StripExtension( pSourceFile, pTgaFileBuf, sizeof(pTgaFileBuf) );
  508. Q_SetExtension( pTgaFileBuf, ".tga", sizeof(pTgaFileBuf) );
  509. pTgaFile = pTgaFileBuf;
  510. }
  511. else
  512. {
  513. pTgaFile = argv[3];
  514. }
  515. if ( argc < 3 )
  516. {
  517. Q_StripExtension( pSourceFile, pShtFileBuf, sizeof(pShtFileBuf) );
  518. Q_SetExtension( pShtFileBuf, ".sht", sizeof(pShtFileBuf) );
  519. pShtFile = pShtFileBuf;
  520. }
  521. else
  522. {
  523. pShtFile = argv[2];
  524. }
  525. ReportProgress("reading text file",0,0);
  526. ReadTextControlFile( pSourceFile );
  527. // now, determine best packing
  528. int nBestWidth = -1;
  529. int nBestSize = (1 << 30 );
  530. int nBestSquareness = ( 1 << 30 ); // how square the texture is
  531. for( int nTryWidth = 2048 ; nTryWidth >= 64; nTryWidth >>= 1 )
  532. {
  533. bool bSuccess = PackImages( NULL, nTryWidth );
  534. if ( bSuccess )
  535. {
  536. printf( "Packing option: %dx%d (%d pixels)\n", s_nWidth, s_nHeight, s_nWidth * s_nHeight );
  537. bool bPreferThisPack = false;
  538. int thisSize = s_nHeight * s_nWidth;
  539. int thisSquareness = ( s_nWidth == s_nHeight ) ? 1 : ( s_nHeight / s_nWidth + s_nWidth / s_nHeight );
  540. if ( thisSize < nBestSize )
  541. bPreferThisPack = true;
  542. else if ( thisSize == nBestSize &&
  543. thisSquareness < nBestSquareness )
  544. bPreferThisPack = true;
  545. if ( bPreferThisPack )
  546. {
  547. nBestWidth = nTryWidth;
  548. nBestSize = thisSize;
  549. nBestSquareness = thisSquareness;
  550. }
  551. }
  552. else
  553. {
  554. break;
  555. }
  556. }
  557. if ( nBestWidth < 0 )
  558. {
  559. printf( "Packing error: failed to pack images!\n" );
  560. exit(1);
  561. }
  562. s_nWidth = nBestWidth;
  563. s_nHeight = nBestSize / nBestWidth;
  564. printf( "Best option: %dx%d (%d pixels)%s\n", s_nWidth, s_nHeight, s_nWidth * s_nHeight, ( s_nWidth == s_nHeight ) ? " : square texture" : "" );
  565. PackImages( pTgaFile, nBestWidth );
  566. // now, write ouput
  567. ReportProgress("Writing SHT output file",0,0);
  568. COutputFile Outfile( pShtFile );
  569. if ( Outfile.IsOk() )
  570. {
  571. Outfile.PutInt( 1 ); // version #
  572. Outfile.PutInt( Sequences.Count() );
  573. for(int i=0;i<Sequences.Count();i++)
  574. {
  575. Outfile.PutInt( Sequences[i]->m_nSequenceNumber );
  576. Outfile.PutInt( Sequences[i]->m_Clamp );
  577. Outfile.PutInt( Sequences[i]->m_Frames.Count() );
  578. // write total sequence length
  579. float fTotal=0.;
  580. for(int j=0;j<Sequences[i]->m_Frames.Count(); j++ )
  581. {
  582. fTotal += Sequences[i]->m_Frames[j].m_fDisplayTime;
  583. }
  584. Outfile.PutFloat( fTotal );
  585. for(int j=0;j<Sequences[i]->m_Frames.Count(); j++ )
  586. {
  587. Outfile.PutFloat( Sequences[i]->m_Frames[j].m_fDisplayTime );
  588. // output texture coordinates
  589. for(int t=0; t<MAX_IMAGES_PER_FRAME; t++)
  590. {
  591. //xmin
  592. Outfile.PutFloat( UCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_XCoord ) );
  593. //ymin
  594. Outfile.PutFloat( VCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_YCoord ) );
  595. //xmax
  596. Outfile.PutFloat(
  597. UCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_XCoord+
  598. Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_pImage->Width-1 ));
  599. //ymax
  600. Outfile.PutFloat(
  601. VCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_YCoord+
  602. Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_pImage->Height-1 ));
  603. // printf( "T %d UV1:( %.2f, %.2f ) UV2:( %.2f, %.2f )\n", t,
  604. // UCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_XCoord ),
  605. // VCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_YCoord ),
  606. // UCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_XCoord+Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_pImage->Width-1 ),
  607. // VCoord( Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_YCoord+Sequences[i]->m_Frames[j].m_pSeqFrame[t]->m_pImage->Height-1 ));
  608. }
  609. }
  610. }
  611. printf( "Ok: successfully saved SHT \"%s\"\n", pShtFile );
  612. }
  613. else
  614. {
  615. printf( "Error: failed to write SHT \"%s\"!\n", pShtFile );
  616. }
  617. }