Counter Strike : Global Offensive Source Code
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.

12759 lines
312 KiB

  1. //===== Copyright � 1996-2008, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. //
  9. // studiomdl.c: generates a studio .mdl file from a .qc script
  10. // models/<scriptname>.mdl.
  11. //
  12. #pragma warning( disable : 4244 )
  13. #pragma warning( disable : 4237 )
  14. #pragma warning( disable : 4305 )
  15. #include <windows.h>
  16. #undef GetCurrentDirectory
  17. #include <Shlwapi.h> // PathCanonicalize
  18. #pragma comment( lib, "shlwapi" )
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <sys/stat.h>
  22. #include <math.h>
  23. #include <direct.h>
  24. #include "istudiorender.h"
  25. #include "filesystem_tools.h"
  26. #include "tier2/fileutils.h"
  27. #include "cmdlib.h"
  28. #include "scriplib.h"
  29. #include "mathlib/mathlib.h"
  30. #define EXTERN
  31. #include "studio.h"
  32. #include "studiomdl.h"
  33. #include "collisionmodel.h"
  34. #include "optimize.h"
  35. #include "byteswap.h"
  36. #include "studiobyteswap.h"
  37. #include "tier1/strtools.h"
  38. #include "bspflags.h"
  39. #include "tier0/icommandline.h"
  40. #include "utldict.h"
  41. #include "tier1/utlsortvector.h"
  42. #include "bitvec.h"
  43. #include "appframework/appframework.h"
  44. #include "datamodel/idatamodel.h"
  45. #include "materialsystem/materialsystem_config.h"
  46. #include "vstdlib/cvar.h"
  47. #include "tier1/tier1.h"
  48. #include "tier2/tier2.h"
  49. #include "tier3/tier3.h"
  50. #include "datamodel/dmelementfactoryhelper.h"
  51. #include "mdlobjects/dmeboneflexdriver.h"
  52. #include "movieobjects/dmeanimationset.h"
  53. #include "movieobjects/dmemdlmakefile.h"
  54. #include "movieobjects/dmevertexdata.h"
  55. #include "movieobjects/dmecombinationoperator.h"
  56. #include "movieobjects/dmeflexrules.h"
  57. #include "dmserializers/idmserializers.h"
  58. #include "tier2/p4helpers.h"
  59. #include "p4lib/ip4.h"
  60. #include "mdllib/mdllib.h"
  61. #include "perfstats.h"
  62. #include "worldsize.h"
  63. #include "KeyValues.h"
  64. #include "compileclothproxy.h"
  65. #include "movieobjects/dmemodel.h"
  66. #include "fbxutils/dmfbxserializer.h"
  67. #include "mathlib/dynamictree.h"
  68. #include "movieobjects/dmemesh.h"
  69. #include "tier1/fmtstr.h"
  70. bool g_parseable_completion_output = false;
  71. bool g_collapse_bones_message = false;
  72. bool g_collapse_bones = false;
  73. bool g_collapse_bones_aggressive = false;
  74. bool g_quiet = false;
  75. bool g_bPreferFbx = false;
  76. bool g_badCollide = false;
  77. bool g_IHVTest = false;
  78. bool g_bCheckLengths = false;
  79. bool g_bPrintBones = false;
  80. bool g_bPerf = false;
  81. bool g_bDumpGraph = false;
  82. bool g_bMultistageGraph = false;
  83. bool g_verbose = true;
  84. bool g_bCreateMakefile = false;
  85. bool g_bHasModelName = false;
  86. bool g_bZBrush = false;
  87. bool g_bVerifyOnly = false;
  88. bool g_bUseBoneInBBox = true;
  89. bool g_bLockBoneLengths = false;
  90. bool g_bDefineBonesLockedByDefault = true;
  91. int g_minLod = 0;
  92. bool g_bFastBuild = false;
  93. int g_numAllowedRootLODs = 0;
  94. bool g_bNoWarnings = false;
  95. int g_maxWarnings = -1;
  96. bool g_bX360 = false;
  97. bool g_bBuildPreview = false;
  98. bool g_bPreserveTriangleOrder = false;
  99. bool g_bCenterBonesOnVerts = false;
  100. bool g_bDumpMaterials = false;
  101. bool g_bStripLods = false;
  102. bool g_bMakeVsi = false;
  103. float g_flDefaultMotionRollback = 0.3f;
  104. int g_minSectionFrameLimit = 30;
  105. int g_sectionFrames = 30;
  106. bool g_bNoAnimblockStall = false;
  107. float g_flPreloadTime = 1.0f;
  108. int g_nMCVersion = 0;
  109. bool g_bAnimblockHighRes = false;
  110. bool g_bAnimblockLowRes = false;
  111. int g_nMaxZeroFrames = 3; // clamped from 1..4
  112. bool g_bZeroFramesHighres = false;
  113. float g_flMinZeroFramePosDelta = 2.0f;
  114. bool g_bLocalPhysX = false;
  115. int g_maxVertexLimit = MAXSTUDIOVERTS / 3; // nasty wireframe limit
  116. int g_maxVertexClamp = MAXSTUDIOVERTS / 3; // nasty wireframe limit
  117. bool g_bLCaseAllSequences = false;
  118. bool g_bErrorOnSeqRemapFail = false;
  119. bool g_bModelIntentionallyHasZeroSequences = false;
  120. float g_flDefaultFadeInTime = 0.2f;
  121. float g_flDefaultFadeOutTime = 0.2f;
  122. float g_flCollisionPrecision = 0;
  123. char g_path[1024];
  124. Vector g_vecMinWorldspace = Vector( MIN_COORD_INTEGER, MIN_COORD_INTEGER, MIN_COORD_INTEGER );
  125. Vector g_vecMaxWorldspace = Vector( MAX_COORD_INTEGER, MAX_COORD_INTEGER, MAX_COORD_INTEGER );
  126. DmElementHandle_t g_hDmeBoneFlexDriverList = DMELEMENT_HANDLE_INVALID;
  127. CUtlVector< CUtlString > g_AllowedActivityNames;
  128. enum RunMode
  129. {
  130. RUN_MODE_BUILD,
  131. RUN_MODE_STRIP_MODEL,
  132. RUN_MODE_STRIP_VHV
  133. } g_eRunMode = RUN_MODE_BUILD;
  134. bool g_bNoP4 = false;
  135. bool g_bContentRootRelative = false;
  136. int g_numtexcoords[MAXSTUDIOTEXCOORDS];
  137. CUtlVectorAuto< Vector2D > g_texcoord[MAXSTUDIOTEXCOORDS];
  138. CUtlVector< s_hitboxset > g_hitboxsets;
  139. CUtlVector< char > g_KeyValueText;
  140. CUtlVector<s_flexcontrollerremap_t> g_FlexControllerRemap;
  141. const char* g_szInCurrentSeqName = NULL;
  142. //-----------------------------------------------------------------------------
  143. // Parsed data from a .qc or .dmx file
  144. //-----------------------------------------------------------------------------
  145. struct IKLock_t
  146. {
  147. CUtlString m_Name;
  148. float m_flPosWeight;
  149. float m_flLocalQWeight;
  150. };
  151. struct SequenceOption_t
  152. {
  153. bool m_bSnap : 1;
  154. bool m_bIsDelta : 1;
  155. bool m_bIsWorldSpace : 1;
  156. bool m_bIsPost : 1;
  157. bool m_bIsPreDelta : 1;
  158. bool m_bIsAutoplay : 1;
  159. bool m_bIsRealTime : 1;
  160. bool m_bIsHidden : 1;
  161. float m_flFadeInTime;
  162. float m_flFadeOutTime;
  163. int m_nBlendWidth;
  164. CUtlVector< CUtlString > m_AutoLayers;
  165. CUtlVector< IKLock_t > m_IKLocks;
  166. };
  167. struct CmdSequence_t
  168. {
  169. CUtlString m_Name;
  170. CUtlString m_FileName;
  171. SequenceOption_t m_Options;
  172. };
  173. //-----------------------------------------------------------------------------
  174. // Forward declarations
  175. //-----------------------------------------------------------------------------
  176. void AddBodyFlexData( s_source_t *pSource, int imodel );
  177. void AddBodyAttachments( s_source_t *pSource );
  178. void AddBodyFlexRules( s_source_t *pSource );
  179. void Option_Flexrule( s_model_t * /* pmodel */, const char *name );
  180. //-----------------------------------------------------------------------------
  181. // Stuff for writing a makefile to build models incrementally.
  182. //-----------------------------------------------------------------------------
  183. CUtlVector<CUtlSymbol> m_CreateMakefileDependencies;
  184. void CreateMakefile_AddDependency( const char *pFileName )
  185. {
  186. EnsureDependencyFileCheckedIn( pFileName );
  187. if( !g_bCreateMakefile )
  188. {
  189. return;
  190. }
  191. CUtlSymbol sym( pFileName );
  192. int i;
  193. for( i = 0; i < m_CreateMakefileDependencies.Count(); i++ )
  194. {
  195. if( m_CreateMakefileDependencies[i] == sym )
  196. {
  197. return;
  198. }
  199. }
  200. m_CreateMakefileDependencies.AddToTail( sym );
  201. }
  202. void EnsureDependencyFileCheckedIn( const char *pFileName )
  203. {
  204. // Early out: if no p4
  205. if ( g_bNoP4 )
  206. return;
  207. char pFullPath[MAX_PATH];
  208. if ( !GetGlobalFilePath( pFileName, pFullPath, sizeof(pFullPath) ) )
  209. {
  210. MdlWarning( "Model dependency file '%s' is missing.\n", pFileName );
  211. return;
  212. }
  213. Q_FixSlashes( pFullPath );
  214. char bufCanonicalPath[ MAX_PATH ] = {0};
  215. PathCanonicalize( bufCanonicalPath, pFullPath );
  216. CP4AutoAddFile p4_add_dep_file( bufCanonicalPath );
  217. }
  218. void StudioMdl_ScriptLoadedCallback( const char *pFilenameLoaded, const char *pIncludedFromFileName, int nIncludeLineNumber )
  219. {
  220. EnsureDependencyFileCheckedIn( pFilenameLoaded );
  221. }
  222. void CreateMakefile_OutputMakefile( void )
  223. {
  224. if( !g_bHasModelName )
  225. {
  226. MdlError( "Can't write makefile since a target mdl hasn't been specified!" );
  227. }
  228. FILE *fp = fopen( "makefile.tmp", "a" );
  229. if( !fp )
  230. {
  231. MdlError( "can't open makefile.tmp!\n" );
  232. }
  233. char mdlname[MAX_PATH];
  234. strcpy( mdlname, gamedir );
  235. // if( *g_pPlatformName )
  236. // {
  237. // strcat( mdlname, "platform_" );
  238. // strcat( mdlname, g_pPlatformName );
  239. // strcat( mdlname, "/" );
  240. // }
  241. strcat( mdlname, "models/" );
  242. strcat( mdlname, g_outname );
  243. Q_StripExtension( mdlname, mdlname, sizeof( mdlname ) );
  244. strcat( mdlname, ".mdl" );
  245. Q_FixSlashes( mdlname );
  246. fprintf( fp, "%s:", mdlname );
  247. int i;
  248. for( i = 0; i < m_CreateMakefileDependencies.Count(); i++ )
  249. {
  250. fprintf( fp, " %s", m_CreateMakefileDependencies[i].String() );
  251. }
  252. fprintf( fp, "\n" );
  253. char mkdirpath[MAX_PATH];
  254. strcpy( mkdirpath, mdlname );
  255. Q_StripFilename( mkdirpath );
  256. fprintf( fp, "\tmkdir \"%s\"\n", mkdirpath );
  257. fprintf( fp, "\t%s -quiet %s\n\n", CommandLine()->GetParm( 0 ), g_fullpath );
  258. fclose( fp );
  259. }
  260. //-----------------------------------------------------------------------------
  261. //
  262. //-----------------------------------------------------------------------------
  263. static bool g_bFirstWarning = true;
  264. void TokenError( const char *fmt, ... )
  265. {
  266. static char output[1024];
  267. va_list args;
  268. char *pFilename;
  269. int iLineNumber;
  270. if (GetTokenizerStatus( &pFilename, &iLineNumber ))
  271. {
  272. va_start( args, fmt );
  273. vsprintf( output, fmt, args );
  274. MdlError( "%s(%d): - %s", pFilename, iLineNumber, output );
  275. }
  276. else
  277. {
  278. va_start( args, fmt );
  279. vsprintf( output, fmt, args );
  280. MdlError( "%s", output );
  281. }
  282. }
  283. void MdlError( const char *fmt, ... )
  284. {
  285. static char output[1024];
  286. static char *knownExtensions[] = {".mdl", ".ani", ".phy", ".sw.vtx", ".dx80.vtx", ".dx90.vtx", ".vvd"};
  287. char fileName[MAX_PATH];
  288. char baseName[MAX_PATH];
  289. va_list args;
  290. Assert( 0 );
  291. if (g_quiet)
  292. {
  293. if (g_bFirstWarning)
  294. {
  295. printf("%s :\n", g_fullpath );
  296. g_bFirstWarning = false;
  297. if (p4)
  298. {
  299. CUtlVector<P4Revision_t> &revisions = p4->GetRevisionList( g_fullpath, false );
  300. if (revisions.Count() > 0)
  301. {
  302. int i = 0;
  303. printf( "%\t%s - ", p4->String( revisions[i].m_sUser ) );
  304. printf( "%04d/%02d/%02d ", revisions[i].m_nYear, revisions[i].m_nMonth, revisions[i].m_nDay);
  305. printf( "%02d:%02d:%02d\n", revisions[i].m_nHour, revisions[i].m_nMinute, revisions[i].m_nSecond);
  306. }
  307. }
  308. }
  309. printf("\t");
  310. }
  311. printf("ERROR: ");
  312. va_start( args, fmt );
  313. vprintf( fmt, args );
  314. // delete premature files
  315. // unforunately, content is built without verification
  316. // ensuring that targets are not available, prevents check-in
  317. if (g_bHasModelName)
  318. {
  319. if (g_quiet)
  320. {
  321. printf("\t");
  322. }
  323. // undescriptive errors in batch processes could be anonymous
  324. printf("ERROR: Aborted Processing on '%s'\n", g_outname);
  325. strcpy( fileName, gamedir );
  326. strcat( fileName, "models/" );
  327. strcat( fileName, g_outname );
  328. Q_FixSlashes( fileName );
  329. Q_StripExtension( fileName, baseName, sizeof( baseName ) );
  330. for (int i=0; i<ARRAYSIZE(knownExtensions); i++)
  331. {
  332. strcpy( fileName, baseName);
  333. strcat( fileName, knownExtensions[i] );
  334. // really need filesystem concept here
  335. // g_pFileSystem->RemoveFile( fileName );
  336. unlink( fileName );
  337. }
  338. }
  339. for ( int i = 0; i < g_pDataModel->NumFileIds(); ++i )
  340. {
  341. g_pDataModel->UnloadFile( g_pDataModel->GetFileId( i ) );
  342. }
  343. if ( g_parseable_completion_output )
  344. {
  345. printf("\nRESULT: ERROR\n");
  346. }
  347. exit( -1 );
  348. }
  349. void MdlWarning( const char *fmt, ... )
  350. {
  351. va_list args;
  352. static char output[1024];
  353. if (g_bNoWarnings || g_maxWarnings == 0)
  354. return;
  355. WORD old = SetConsoleTextColor( 1, 1, 0, 1 );
  356. if (g_quiet)
  357. {
  358. if (g_bFirstWarning)
  359. {
  360. printf("%s :\n", g_fullpath );
  361. g_bFirstWarning = false;
  362. if (p4)
  363. {
  364. CUtlVector<P4Revision_t> &revisions = p4->GetRevisionList( g_fullpath, false );
  365. if (revisions.Count() > 0)
  366. {
  367. int i = 0;
  368. printf( "%\t %s - ", p4->String( revisions[i].m_sUser ) );
  369. printf( "%04d/%02d/%02d ", revisions[i].m_nYear, revisions[i].m_nMonth, revisions[i].m_nDay);
  370. printf( "%02d:%02d:%02d\n", revisions[i].m_nHour, revisions[i].m_nMinute, revisions[i].m_nSecond);
  371. }
  372. }
  373. }
  374. printf("\t");
  375. }
  376. //Assert( 0 );
  377. printf("WARNING: ");
  378. va_start( args, fmt );
  379. vprintf( fmt, args );
  380. if (g_maxWarnings > 0)
  381. g_maxWarnings--;
  382. if (g_maxWarnings == 0)
  383. {
  384. if (g_quiet)
  385. {
  386. printf("\t");
  387. }
  388. printf("suppressing further warnings...\n");
  389. }
  390. RestoreConsoleTextColor( old );
  391. }
  392. class CMdlLoggingListener : public CCmdLibStandardLoggingListener
  393. {
  394. virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
  395. {
  396. if ( pContext->m_Severity == LS_MESSAGE && g_quiet )
  397. {
  398. // suppress
  399. }
  400. else if ( pContext->m_Severity == LS_WARNING )
  401. {
  402. MdlWarning( "%s", pMessage );
  403. }
  404. else
  405. {
  406. CCmdLibStandardLoggingListener::Log( pContext, pMessage );
  407. }
  408. }
  409. };
  410. static CMdlLoggingListener s_MdlLoggingListener;
  411. #ifndef _DEBUG
  412. void MdlHandleCrash( const char *pMessage, bool bAssert )
  413. {
  414. static LONG crashHandlerCount = 0;
  415. if ( InterlockedIncrement( &crashHandlerCount ) == 1 )
  416. {
  417. MdlError( "'%s' (assert: %d)\n", pMessage, bAssert );
  418. }
  419. InterlockedDecrement( &crashHandlerCount );
  420. }
  421. // This is called if we crash inside our crash handler. It just terminates the process immediately.
  422. LONG __stdcall MdlSecondExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
  423. {
  424. TerminateProcess( GetCurrentProcess(), 2 );
  425. return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway)
  426. }
  427. void MdlExceptionFilter( unsigned long code )
  428. {
  429. // This is called if we crash inside our crash handler. It just terminates the process immediately.
  430. SetUnhandledExceptionFilter( MdlSecondExceptionFilter );
  431. //DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
  432. #define ERR_RECORD( name ) { name, #name }
  433. struct
  434. {
  435. int code;
  436. char *pReason;
  437. } errors[] =
  438. {
  439. ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
  440. ERR_RECORD( EXCEPTION_ARRAY_BOUNDS_EXCEEDED ),
  441. ERR_RECORD( EXCEPTION_BREAKPOINT ),
  442. ERR_RECORD( EXCEPTION_DATATYPE_MISALIGNMENT ),
  443. ERR_RECORD( EXCEPTION_FLT_DENORMAL_OPERAND ),
  444. ERR_RECORD( EXCEPTION_FLT_DIVIDE_BY_ZERO ),
  445. ERR_RECORD( EXCEPTION_FLT_INEXACT_RESULT ),
  446. ERR_RECORD( EXCEPTION_FLT_INVALID_OPERATION ),
  447. ERR_RECORD( EXCEPTION_FLT_OVERFLOW ),
  448. ERR_RECORD( EXCEPTION_FLT_STACK_CHECK ),
  449. ERR_RECORD( EXCEPTION_FLT_UNDERFLOW ),
  450. ERR_RECORD( EXCEPTION_ILLEGAL_INSTRUCTION ),
  451. ERR_RECORD( EXCEPTION_IN_PAGE_ERROR ),
  452. ERR_RECORD( EXCEPTION_INT_DIVIDE_BY_ZERO ),
  453. ERR_RECORD( EXCEPTION_INT_OVERFLOW ),
  454. ERR_RECORD( EXCEPTION_INVALID_DISPOSITION ),
  455. ERR_RECORD( EXCEPTION_NONCONTINUABLE_EXCEPTION ),
  456. ERR_RECORD( EXCEPTION_PRIV_INSTRUCTION ),
  457. ERR_RECORD( EXCEPTION_SINGLE_STEP ),
  458. ERR_RECORD( EXCEPTION_STACK_OVERFLOW ),
  459. ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
  460. };
  461. int nErrors = sizeof( errors ) / sizeof( errors[0] );
  462. {
  463. int i;
  464. for ( i=0; i < nErrors; i++ )
  465. {
  466. if ( errors[i].code == code )
  467. MdlHandleCrash( errors[i].pReason, true );
  468. }
  469. if ( i == nErrors )
  470. {
  471. MdlHandleCrash( "Unknown reason", true );
  472. }
  473. }
  474. TerminateProcess( GetCurrentProcess(), 1 );
  475. }
  476. #endif
  477. /*
  478. =================
  479. =================
  480. */
  481. int verify_atoi( const char *token )
  482. {
  483. for ( int i=0; i<strlen(token); i++ )
  484. {
  485. if (token[i] != '-' && (token[i] < '0' || token[i] > '9'))
  486. TokenError( "expecting integer, got \"%s\"\n", token );
  487. }
  488. return atoi( token );
  489. }
  490. float verify_atof( const char *token )
  491. {
  492. for ( int i=0; i<strlen(token); i++ )
  493. {
  494. if (token[i] != '-' && token[i] != '.' && (token[i] < '0' || token[i] > '9'))
  495. TokenError( "expecting float, got \"%s\"\n", token );
  496. }
  497. return atof( token );
  498. }
  499. float verify_atof_with_null( const char *token )
  500. {
  501. if (strcmp( token, ".." ) == 0)
  502. return -1;
  503. if (token[0] != '-' && token[0] != '.' && (token[0] < '0' || token[0] > '9'))
  504. {
  505. TokenError( "expecting float, got \"%s\"\n", token );
  506. }
  507. return atof( token );
  508. }
  509. //-----------------------------------------------------------------------------
  510. // Key value block
  511. //-----------------------------------------------------------------------------
  512. static void AppendKeyValueText( CUtlVector< char > *pKeyValue, const char *pString )
  513. {
  514. int nLen = strlen(pString);
  515. int nFirst = pKeyValue->AddMultipleToTail( nLen );
  516. memcpy( pKeyValue->Base() + nFirst, pString, nLen );
  517. }
  518. int KeyValueTextSize( CUtlVector< char > *pKeyValue )
  519. {
  520. return pKeyValue->Count();
  521. }
  522. const char *KeyValueText( CUtlVector< char > *pKeyValue )
  523. {
  524. return pKeyValue->Base();
  525. }
  526. void Option_KeyValues( CUtlVector< char > *pKeyValue );
  527. //-----------------------------------------------------------------------------
  528. // Read global input into common string
  529. //-----------------------------------------------------------------------------
  530. bool GetLineInput( void )
  531. {
  532. while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
  533. {
  534. g_iLinecount++;
  535. // skip comments
  536. if (g_szLine[0] == '/' && g_szLine[1] == '/')
  537. continue;
  538. return true;
  539. }
  540. return false;
  541. }
  542. /*
  543. =================
  544. =================
  545. */
  546. int lookupControl( char *string )
  547. {
  548. if (stricmp(string,"X")==0) return STUDIO_X;
  549. if (stricmp(string,"Y")==0) return STUDIO_Y;
  550. if (stricmp(string,"Z")==0) return STUDIO_Z;
  551. if (stricmp(string,"XR")==0) return STUDIO_XR;
  552. if (stricmp(string,"YR")==0) return STUDIO_YR;
  553. if (stricmp(string,"ZR")==0) return STUDIO_ZR;
  554. if (stricmp(string,"LX")==0) return STUDIO_LX;
  555. if (stricmp(string,"LY")==0) return STUDIO_LY;
  556. if (stricmp(string,"LZ")==0) return STUDIO_LZ;
  557. if (stricmp(string,"LXR")==0) return STUDIO_LXR;
  558. if (stricmp(string,"LYR")==0) return STUDIO_LYR;
  559. if (stricmp(string,"LZR")==0) return STUDIO_LZR;
  560. if (stricmp(string,"LM")==0) return STUDIO_LINEAR;
  561. if (stricmp(string,"LQ")==0) return STUDIO_QUADRATIC_MOTION;
  562. return -1;
  563. }
  564. /*
  565. =================
  566. =================
  567. */
  568. int LookupPoseParameter( const char *name )
  569. {
  570. int i;
  571. for ( i = 0; i < g_numposeparameters; i++)
  572. {
  573. if (!stricmp( name, g_pose[i].name))
  574. {
  575. return i;
  576. }
  577. }
  578. strcpyn( g_pose[i].name, name );
  579. g_numposeparameters = i + 1;
  580. if (g_numposeparameters > MAXSTUDIOPOSEPARAM)
  581. {
  582. TokenError( "too many pose parameters (max %d)\n", MAXSTUDIOPOSEPARAM );
  583. }
  584. return i;
  585. }
  586. //-----------------------------------------------------------------------------
  587. // Stuff for writing a makefile to build models incrementally.
  588. //-----------------------------------------------------------------------------
  589. s_sourceanim_t *FindSourceAnim( s_source_t *pSource, const char *pAnimName )
  590. {
  591. int nCount = pSource->m_Animations.Count();
  592. for ( int i = 0; i < nCount; ++i )
  593. {
  594. s_sourceanim_t *pAnim = &pSource->m_Animations[i];
  595. if ( !Q_stricmp( pAnimName, pAnim->animationname ) )
  596. return pAnim;
  597. }
  598. return NULL;
  599. }
  600. const s_sourceanim_t *FindSourceAnim( const s_source_t *pSource, const char *pAnimName )
  601. {
  602. if ( !pAnimName[0] )
  603. return NULL;
  604. int nCount = pSource->m_Animations.Count();
  605. for ( int i = 0; i < nCount; ++i )
  606. {
  607. const s_sourceanim_t *pAnim = &pSource->m_Animations[i];
  608. if ( !Q_stricmp( pAnimName, pAnim->animationname ) )
  609. return pAnim;
  610. }
  611. return NULL;
  612. }
  613. s_sourceanim_t *FindOrAddSourceAnim( s_source_t *pSource, const char *pAnimName )
  614. {
  615. if ( !pAnimName[0] )
  616. return NULL;
  617. int nCount = pSource->m_Animations.Count();
  618. for ( int i = 0; i < nCount; ++i )
  619. {
  620. s_sourceanim_t *pAnim = &pSource->m_Animations[i];
  621. if ( !Q_stricmp( pAnimName, pAnim->animationname ) )
  622. return pAnim;
  623. }
  624. int nIndex = pSource->m_Animations.AddToTail();
  625. s_sourceanim_t *pAnim = &pSource->m_Animations[nIndex];
  626. memset( pAnim, 0, sizeof(s_sourceanim_t) );
  627. Q_strncpy( pAnim->animationname, pAnimName, sizeof(pAnim->animationname) );
  628. return pAnim;
  629. }
  630. //-----------------------------------------------------------------------------
  631. // Purpose: Handle the $boneflexdriver command
  632. // QC: $boneflexdriver <bone name> <tx|ty|tz> <flex controller name> <min> <max>
  633. //-----------------------------------------------------------------------------
  634. void Cmd_BoneFlexDriver()
  635. {
  636. CDisableUndoScopeGuard undoDisable; // Turn of Dme undo
  637. // Find or create the DmeBoneFlexDriverList
  638. CDmeBoneFlexDriverList *pDmeBoneFlexDriverList = GetElement< CDmeBoneFlexDriverList >( g_hDmeBoneFlexDriverList );
  639. if ( !pDmeBoneFlexDriverList )
  640. {
  641. pDmeBoneFlexDriverList = CreateElement< CDmeBoneFlexDriverList >( "boneDriverFlexList", DMFILEID_INVALID );
  642. if ( pDmeBoneFlexDriverList )
  643. {
  644. g_hDmeBoneFlexDriverList = pDmeBoneFlexDriverList->GetHandle();
  645. }
  646. }
  647. if ( !pDmeBoneFlexDriverList )
  648. {
  649. MdlError( "%s: Couldn't find or create DmeBoneDriverFlexList\n", "$boneflexdriver" );
  650. return;
  651. }
  652. // <bone name>
  653. GetToken( false );
  654. CDmeBoneFlexDriver *pDmeBoneFlexDriver = pDmeBoneFlexDriverList->FindOrCreateBoneFlexDriver( token );
  655. if ( !pDmeBoneFlexDriver )
  656. {
  657. MdlError( "%s: Couldn't find or create DmeBoneFlexDriver for bone \"%s\"\n", "$boneflexdriver", token );
  658. return;
  659. }
  660. // <tx|ty|tz|rx|ry|rz>
  661. GetToken( false );
  662. const char *ppszComponentTypeList[] = { "tx", "ty", "tz" };
  663. int nBoneComponent = -1;
  664. for ( int i = 0; i < ARRAYSIZE( ppszComponentTypeList ); ++i )
  665. {
  666. if ( StringHasPrefix( token, ppszComponentTypeList[i] ) )
  667. {
  668. nBoneComponent = i;
  669. break;
  670. }
  671. }
  672. if ( nBoneComponent < STUDIO_BONE_FLEX_TX || nBoneComponent > STUDIO_BONE_FLEX_TZ )
  673. {
  674. TokenError( "%s: Invalid bone component, must be one of <tx|ty|tz>\n", "$boneflexdriver" );
  675. return;
  676. }
  677. // <flex controller name>
  678. GetToken( false );
  679. CDmeBoneFlexDriverControl *pDmeBoneFlexDriverControl = pDmeBoneFlexDriver->FindOrCreateControl( token );
  680. if ( !pDmeBoneFlexDriverControl )
  681. {
  682. MdlError( "%s: Couldn't find or create DmeBoneFlexDriverControl for bone \"%s\"\n", "$boneflexdriver", token );
  683. return;
  684. }
  685. pDmeBoneFlexDriverControl->m_nBoneComponent = nBoneComponent;
  686. // <min>
  687. GetToken( false );
  688. pDmeBoneFlexDriverControl->m_flMin = verify_atof( token );
  689. // <max>
  690. GetToken( false );
  691. pDmeBoneFlexDriverControl->m_flMax = verify_atof( token );
  692. }
  693. void Cmd_PoseParameter( )
  694. {
  695. if ( g_numposeparameters >= MAXSTUDIOPOSEPARAM )
  696. {
  697. TokenError( "too many pose parameters (max %d)\n", MAXSTUDIOPOSEPARAM );
  698. }
  699. GetToken (false); //[wills] unless you want a pose parameter named "poseparameter", should probably GetToken here
  700. int i = LookupPoseParameter( token );
  701. strcpyn( g_pose[i].name, token );
  702. if ( TokenAvailable() )
  703. {
  704. // min
  705. GetToken (false);
  706. g_pose[i].min = verify_atof (token);
  707. }
  708. if ( TokenAvailable() )
  709. {
  710. // max
  711. GetToken (false);
  712. g_pose[i].max = verify_atof (token);
  713. }
  714. while ( TokenAvailable() )
  715. {
  716. GetToken (false);
  717. if ( !Q_stricmp( token, "wrap" ) )
  718. {
  719. g_pose[i].flags |= STUDIO_LOOPING;
  720. g_pose[i].loop = g_pose[i].max - g_pose[i].min;
  721. }
  722. else if ( !Q_stricmp( token, "loop" ) )
  723. {
  724. g_pose[i].flags |= STUDIO_LOOPING;
  725. GetToken (false);
  726. g_pose[i].loop = verify_atof( token );
  727. }
  728. }
  729. }
  730. /*
  731. =================
  732. =================
  733. */
  734. int LookupTexture( const char *pTextureName, bool bRelativePath )
  735. {
  736. char pTextureNoExt[MAX_PATH];
  737. char pTextureBase[MAX_PATH];
  738. char pTextureBase2[MAX_PATH];
  739. Q_StripExtension( pTextureName, pTextureNoExt, sizeof(pTextureNoExt) );
  740. Q_FileBase( pTextureName, pTextureBase, sizeof(pTextureBase) );
  741. int nFlags = bRelativePath ? RELATIVE_TEXTURE_PATH_SPECIFIED : 0;
  742. int i;
  743. for ( i = 0; i < g_numtextures; i++ )
  744. {
  745. if ( g_texture[i].flags == nFlags )
  746. {
  747. if ( !Q_stricmp( pTextureNoExt, g_texture[i].name ) )
  748. return i;
  749. continue;
  750. }
  751. // Comparing relative vs non-relative
  752. if ( bRelativePath )
  753. {
  754. if ( !Q_stricmp( pTextureBase, g_texture[i].name ) )
  755. return i;
  756. continue;
  757. }
  758. // Comparing non-relative vs relative
  759. Q_FileBase( g_texture[i].name, pTextureBase2, sizeof(pTextureBase2) );
  760. if ( !Q_stricmp( pTextureNoExt, pTextureBase2 ) )
  761. return i;
  762. }
  763. if ( i >= MAXSTUDIOSKINS )
  764. {
  765. MdlError("Too many materials used, max %d\n", ( int )MAXSTUDIOSKINS );
  766. }
  767. Q_strncpy( g_texture[i].name, pTextureNoExt, sizeof(g_texture[i].name) );
  768. g_texture[i].material = -1;
  769. g_texture[i].flags = nFlags;
  770. g_numtextures++;
  771. return i;
  772. }
  773. void Cmd_OverrideMaterial( void )
  774. {
  775. char to[256];
  776. GetToken( false );
  777. strcpy( to, token );
  778. Msg( "$overridematerial is replacing ALL material references with %s.\n", to );
  779. int i;
  780. for (i = 0; i < g_numtextures; i++)
  781. {
  782. strcpy( g_texture[i].name, to );
  783. }
  784. }
  785. void Cmd_RenameMaterial( void )
  786. {
  787. char from[256];
  788. char to[256];
  789. GetToken( false );
  790. strcpy( from, token );
  791. GetToken( false );
  792. strcpy( to, token );
  793. int i;
  794. for (i = 0; i < g_numtextures; i++)
  795. {
  796. if (stricmp( g_texture[i].name, from ) == 0)
  797. {
  798. strcpy( g_texture[i].name, to );
  799. return;
  800. }
  801. }
  802. for (i = 0; i < g_numtextures; i++)
  803. {
  804. if ( V_stristr( g_texture[i].name, from ) )
  805. {
  806. Msg( "$renamematerial fell back to partial match: Replacing %s with %s.\n", g_texture[i].name, to );
  807. strcpy( g_texture[i].name, to );
  808. return;
  809. }
  810. }
  811. MdlError( "unknown material \"%s\" in rename\n", from );
  812. }
  813. int UseTextureAsMaterial( int textureindex )
  814. {
  815. if ( g_texture[textureindex].material == -1 )
  816. {
  817. if (g_bDumpMaterials)
  818. {
  819. printf("material %d %d %s\n", textureindex, g_nummaterials, g_texture[textureindex].name );
  820. }
  821. g_material[g_nummaterials] = textureindex;
  822. g_texture[textureindex].material = g_nummaterials++;
  823. }
  824. return g_texture[textureindex].material;
  825. }
  826. int MaterialToTexture( int material )
  827. {
  828. int i;
  829. for (i = 0; i < g_numtextures; i++)
  830. {
  831. if (g_texture[i].material == material)
  832. {
  833. return i;
  834. }
  835. }
  836. return -1;
  837. }
  838. //Wrong name for the use of it.
  839. void scale_vertex( Vector &org )
  840. {
  841. org[0] = org[0] * g_currentscale;
  842. org[1] = org[1] * g_currentscale;
  843. org[2] = org[2] * g_currentscale;
  844. }
  845. void SetSkinValues( )
  846. {
  847. int i, j;
  848. int index;
  849. // Check all textures to see if we have relative paths specified
  850. for (i = 0; i < g_numtextures; i++)
  851. {
  852. if ( g_texture[i].flags & RELATIVE_TEXTURE_PATH_SPECIFIED )
  853. {
  854. // Add an empty path to prepend if anything specifies a relative path
  855. cdtextures[numcdtextures] = 0;
  856. ++numcdtextures;
  857. break;
  858. }
  859. }
  860. if ( numcdtextures == 0 )
  861. {
  862. char szName[256];
  863. // strip down till it finds "models"
  864. strcpyn( szName, g_fullpath );
  865. while (szName[0] != '\0' && strnicmp( "models", szName, 6 ) != 0)
  866. {
  867. strcpy( &szName[0], &szName[1] );
  868. }
  869. if (szName[0] != '\0')
  870. {
  871. Q_StripFilename( szName );
  872. strcat( szName, "/" );
  873. }
  874. else
  875. {
  876. // if( *g_pPlatformName )
  877. // {
  878. // strcat( szName, "platform_" );
  879. // strcat( szName, g_pPlatformName );
  880. // strcat( szName, "/" );
  881. // }
  882. strcpy( szName, "models/" );
  883. strcat( szName, g_outname );
  884. Q_StripExtension( szName, szName, sizeof( szName ) );
  885. strcat( szName, "/" );
  886. }
  887. cdtextures[0] = strdup( szName );
  888. numcdtextures = 1;
  889. }
  890. for (i = 0; i < g_numtextures; i++)
  891. {
  892. char szName[256];
  893. Q_StripExtension( g_texture[i].name, szName, sizeof( szName ) );
  894. Q_strncpy( g_texture[i].name, szName, sizeof( g_texture[i].name ) );
  895. }
  896. // build texture groups
  897. for (i = 0; i < MAXSTUDIOSKINS; i++)
  898. {
  899. for (j = 0; j < MAXSTUDIOSKINS; j++)
  900. {
  901. g_skinref[i][j] = j;
  902. }
  903. }
  904. index = 0;
  905. for (i = 0; i < g_numtexturelayers[0]; i++)
  906. {
  907. for (j = 0; j < g_numtexturereps[0]; j++)
  908. {
  909. g_skinref[i][g_texturegroup[0][0][j]] = g_texturegroup[0][i][j];
  910. }
  911. }
  912. if (i != 0)
  913. {
  914. g_numskinfamilies = i;
  915. }
  916. else
  917. {
  918. g_numskinfamilies = 1;
  919. }
  920. g_numskinref = g_numtextures;
  921. // printf ("width: %i height: %i\n",width, height);
  922. /*
  923. printf ("adjusted width: %i height: %i top : %i left: %i\n",
  924. pmesh->skinwidth, pmesh->skinheight, pmesh->skintop, pmesh->skinleft );
  925. */
  926. }
  927. /*
  928. =================
  929. =================
  930. */
  931. int LookupXNode( const char *name )
  932. {
  933. int i;
  934. for ( i = 1; i <= g_numxnodes; i++)
  935. {
  936. if (stricmp( name, g_xnodename[i] ) == 0)
  937. {
  938. return i;
  939. }
  940. }
  941. g_xnodename[i] = strdup( name );
  942. g_numxnodes = i;
  943. return i;
  944. }
  945. /*
  946. =================
  947. =================
  948. */
  949. char g_szFilename[1024];
  950. FILE *g_fpInput;
  951. char g_szLine[4096];
  952. int g_iLinecount;
  953. void Build_Reference( s_source_t *pSource, const char *pAnimName )
  954. {
  955. int i, parent;
  956. Vector angle;
  957. s_sourceanim_t *pReferenceAnim = FindSourceAnim( pSource, pAnimName );
  958. for (i = 0; i < pSource->numbones; i++)
  959. {
  960. matrix3x4_t m;
  961. if ( pReferenceAnim )
  962. {
  963. AngleMatrix( pReferenceAnim->rawanim[0][i].rot, m );
  964. m[0][3] = pReferenceAnim->rawanim[0][i].pos[0];
  965. m[1][3] = pReferenceAnim->rawanim[0][i].pos[1];
  966. m[2][3] = pReferenceAnim->rawanim[0][i].pos[2];
  967. }
  968. else
  969. {
  970. SetIdentityMatrix( m );
  971. }
  972. parent = pSource->localBone[i].parent;
  973. if (parent == -1)
  974. {
  975. // scale the done pos.
  976. // calc rotational matrices
  977. MatrixCopy( m, pSource->boneToPose[i] );
  978. }
  979. else
  980. {
  981. // calc compound rotational matrices
  982. // FIXME : Hey, it's orthogical so inv(A) == transpose(A)
  983. Assert( parent < i );
  984. ConcatTransforms( pSource->boneToPose[parent], m, pSource->boneToPose[i] );
  985. }
  986. // printf("%3d %f %f %f\n", i, psource->bonefixup[i].worldorg[0], psource->bonefixup[i].worldorg[1], psource->bonefixup[i].worldorg[2] );
  987. /*
  988. AngleMatrix( angle, m );
  989. printf("%8.4f %8.4f %8.4f\n", m[0][0], m[1][0], m[2][0] );
  990. printf("%8.4f %8.4f %8.4f\n", m[0][1], m[1][1], m[2][1] );
  991. printf("%8.4f %8.4f %8.4f\n", m[0][2], m[1][2], m[2][2] );
  992. */
  993. }
  994. }
  995. int Grab_Nodes( s_node_t *pnodes )
  996. {
  997. int index;
  998. char name[1024];
  999. int parent;
  1000. int numbones = 0;
  1001. for (index = 0; index < MAXSTUDIOSRCBONES; index++)
  1002. {
  1003. pnodes[index].parent = -1;
  1004. }
  1005. while (GetLineInput())
  1006. {
  1007. if (sscanf( g_szLine, "%d \"%[^\"]\" %d", &index, name, &parent ) == 3)
  1008. {
  1009. // check for duplicated bones
  1010. /*
  1011. if (strlen(pnodes[index].name) != 0)
  1012. {
  1013. MdlError( "bone \"%s\" exists more than once\n", name );
  1014. }
  1015. */
  1016. strcpyn( pnodes[index].name, name );
  1017. pnodes[index].parent = parent;
  1018. if (index > numbones)
  1019. {
  1020. numbones = index;
  1021. }
  1022. }
  1023. else
  1024. {
  1025. return numbones + 1;
  1026. }
  1027. }
  1028. MdlError( "Unexpected EOF at line %d\n", g_iLinecount );
  1029. return 0;
  1030. }
  1031. void clip_rotations( RadianEuler& rot )
  1032. {
  1033. int j;
  1034. // clip everything to : -M_PI <= x < M_PI
  1035. for (j = 0; j < 3; j++) {
  1036. while (rot[j] >= M_PI)
  1037. rot[j] -= M_PI*2;
  1038. while (rot[j] < -M_PI)
  1039. rot[j] += M_PI*2;
  1040. }
  1041. }
  1042. void clip_rotations( Vector& rot )
  1043. {
  1044. int j;
  1045. // clip everything to : -180 <= x < 180
  1046. for (j = 0; j < 3; j++) {
  1047. while (rot[j] >= 180)
  1048. rot[j] -= 180*2;
  1049. while (rot[j] < -180)
  1050. rot[j] += 180*2;
  1051. }
  1052. }
  1053. /*
  1054. =================
  1055. Cmd_Eyeposition
  1056. =================
  1057. */
  1058. void Cmd_Eyeposition (void)
  1059. {
  1060. // rotate points into frame of reference so g_model points down the positive x
  1061. // axis
  1062. // FIXME: these coords are bogus
  1063. GetToken (false);
  1064. eyeposition[1] = verify_atof (token);
  1065. GetToken (false);
  1066. eyeposition[0] = -verify_atof (token);
  1067. GetToken (false);
  1068. eyeposition[2] = verify_atof (token);
  1069. }
  1070. //-----------------------------------------------------------------------------
  1071. // Cmd_MaxEyeDeflection
  1072. //-----------------------------------------------------------------------------
  1073. void Cmd_MaxEyeDeflection()
  1074. {
  1075. GetToken( false );
  1076. g_flMaxEyeDeflection = cosf( verify_atof( token ) * M_PI / 180.0f );
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. // Cmd_AddSearchDir: add the custom defined path to an array that we will add to the search paths
  1080. //-----------------------------------------------------------------------------
  1081. void Cmd_AddSearchDir()
  1082. {
  1083. GetToken ( false );
  1084. if (!g_quiet)
  1085. {
  1086. printf ( "New search path: %s\n", token );
  1087. }
  1088. CmdLib_AddNewSearchPath ( token );
  1089. }
  1090. //-----------------------------------------------------------------------------
  1091. // Cmd_Illumposition
  1092. //-----------------------------------------------------------------------------
  1093. void Cmd_Illumposition( void )
  1094. {
  1095. GetToken( false );
  1096. illumposition[0] = verify_atof( token );
  1097. GetToken( false );
  1098. illumposition[1] = verify_atof( token );
  1099. GetToken( false );
  1100. illumposition[2] = verify_atof( token );
  1101. if ( TokenAvailable() )
  1102. {
  1103. GetToken( false );
  1104. Q_strncpy( g_attachment[g_numattachments].name, "__illumPosition", sizeof(g_attachment[g_numattachments].name) );
  1105. Q_strncpy( g_attachment[g_numattachments].bonename, token, sizeof(g_attachment[g_numattachments].bonename) );
  1106. AngleMatrix( QAngle( 0, 0, 0 ), illumposition, g_attachment[g_numattachments].local );
  1107. g_attachment[g_numattachments].type |= IS_RIGID;
  1108. g_illumpositionattachment = g_numattachments + 1;
  1109. ++g_numattachments;
  1110. }
  1111. else
  1112. {
  1113. g_illumpositionattachment = 0;
  1114. // rotate points into frame of reference so
  1115. // g_model points down the positive x axis
  1116. // FIXME: these coords are bogus
  1117. float flTemp = illumposition[0];
  1118. illumposition[0] = -illumposition[1];
  1119. illumposition[1] = flTemp;
  1120. }
  1121. illumpositionset = true;
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. // Process Cmd_Modelname
  1125. //-----------------------------------------------------------------------------
  1126. void ProcessModelName( const char *pModelName )
  1127. {
  1128. // Abort early if modelname is too big
  1129. const int nModelNameLen = Q_strlen( pModelName );
  1130. char *pTmpBuf = reinterpret_cast< char * >( _alloca( ( nModelNameLen + 1 ) * sizeof( char ) ) );
  1131. Q_StripExtension( pModelName, pTmpBuf, nModelNameLen+1 );
  1132. // write.cpp strips extension then adds .mdl and writes that into studiohdr_t::name
  1133. // Need one for sizeof operation to work...
  1134. studiohdr_t shdr;
  1135. if ( Q_strlen( pTmpBuf ) + 4 >= ( sizeof( g_outname ) / sizeof( g_outname[ 0 ] ) ) )
  1136. {
  1137. MdlError( "Model Name \"%s.mdl\" Too Big, %d Characters, Max %d Characters\n",
  1138. pTmpBuf,
  1139. Q_strlen( pTmpBuf ) + 4,
  1140. ( sizeof( g_outname ) / sizeof( g_outname ) ) - 1 );
  1141. }
  1142. g_bHasModelName = true;
  1143. Q_strncpy( g_outname, pModelName, sizeof( g_outname ) );
  1144. }
  1145. //-----------------------------------------------------------------------------
  1146. // Parse Cmd_Modelname
  1147. //-----------------------------------------------------------------------------
  1148. void Cmd_Modelname (void)
  1149. {
  1150. GetToken (false);
  1151. if ( token[0] == '/' || token[0] == '\\' )
  1152. {
  1153. MdlWarning( "$modelname key has slash as first character. Removing.\n" );
  1154. ProcessModelName( &token[1] );
  1155. }
  1156. else
  1157. {
  1158. ProcessModelName( token );
  1159. }
  1160. }
  1161. void Cmd_InternalName( void )
  1162. {
  1163. GetToken( false );
  1164. Q_strncpy( g_szInternalName, token, sizeof( g_szInternalName ) );
  1165. }
  1166. void Cmd_Phyname (void)
  1167. {
  1168. GetToken (false);
  1169. CollisionModel_SetName( token );
  1170. }
  1171. void Cmd_PreserveTriangleOrder( void )
  1172. {
  1173. g_bPreserveTriangleOrder = true;
  1174. }
  1175. void Cmd_Autocenter()
  1176. {
  1177. g_centerstaticprop = true;
  1178. }
  1179. /*
  1180. ===============
  1181. ===============
  1182. */
  1183. //-----------------------------------------------------------------------------
  1184. // Parse the body command from a .qc file
  1185. //-----------------------------------------------------------------------------
  1186. void ProcessOptionStudio( s_model_t *pmodel, const char *pFullPath, float flScale, bool bFlipTriangles, bool bQuadSubd )
  1187. {
  1188. Q_strncpy( pmodel->filename, pFullPath, sizeof(pmodel->filename) );
  1189. if ( flScale != 0.0f )
  1190. {
  1191. pmodel->scale = g_currentscale = flScale;
  1192. }
  1193. else
  1194. {
  1195. pmodel->scale = g_currentscale = g_defaultscale;
  1196. }
  1197. g_pCurrentModel = pmodel;
  1198. // load source
  1199. pmodel->source = Load_Source( pmodel->filename, "", bFlipTriangles, true );
  1200. g_pCurrentModel = NULL;
  1201. // Reset currentscale to whatever global we currently have set
  1202. // g_defaultscale gets set in Cmd_ScaleUp everytime the $scale command is used.
  1203. g_currentscale = g_defaultscale;
  1204. }
  1205. //-----------------------------------------------------------------------------
  1206. // Parse the studio options from a .qc file
  1207. //-----------------------------------------------------------------------------
  1208. bool ParseOptionStudio( CDmeSourceSkin *pSkin )
  1209. {
  1210. if ( !GetToken( false ) )
  1211. return false;
  1212. pSkin->SetRelativeFileName( token );
  1213. while ( TokenAvailable() )
  1214. {
  1215. GetToken(false);
  1216. if ( !Q_stricmp( "reverse", token ) )
  1217. {
  1218. pSkin->m_bFlipTriangles = true;
  1219. continue;
  1220. }
  1221. if ( !Q_stricmp( "scale", token ) )
  1222. {
  1223. GetToken(false);
  1224. pSkin->m_flScale = verify_atof( token );
  1225. continue;
  1226. }
  1227. if ( !Q_stricmp( "faces", token ) )
  1228. {
  1229. GetToken( false );
  1230. GetToken( false );
  1231. continue;
  1232. }
  1233. if ( !Q_stricmp( "bias", token ) )
  1234. {
  1235. GetToken( false );
  1236. continue;
  1237. }
  1238. if ( !Q_stricmp( "subd", token ) )
  1239. {
  1240. pSkin->m_bQuadSubd = true;
  1241. continue;
  1242. }
  1243. if ( !Q_stricmp( "{", token ) )
  1244. {
  1245. UnGetToken( );
  1246. break;
  1247. }
  1248. MdlError("unknown command \"%s\"\n", token );
  1249. return false;
  1250. }
  1251. return true;
  1252. }
  1253. //-----------------------------------------------------------------------------
  1254. // Parse + process the studio options from a .qc file
  1255. //-----------------------------------------------------------------------------
  1256. void Option_Studio( s_model_t *pmodel )
  1257. {
  1258. CDmeSourceSkin *pSourceSkin = CreateElement< CDmeSourceSkin >( "", DMFILEID_INVALID );
  1259. // Set defaults
  1260. pSourceSkin->m_flScale = g_defaultscale;
  1261. pSourceSkin->m_bQuadSubd = false;
  1262. pSourceSkin->m_bFlipTriangles = false;
  1263. if ( ParseOptionStudio( pSourceSkin ) )
  1264. {
  1265. ProcessOptionStudio( pmodel, pSourceSkin->GetRelativeFileName(), pSourceSkin->m_flScale, pSourceSkin->m_bFlipTriangles, pSourceSkin->m_bQuadSubd );
  1266. }
  1267. DestroyElement( pSourceSkin );
  1268. }
  1269. int Option_Blank( )
  1270. {
  1271. g_model[g_nummodels] = (s_model_t *)calloc( 1, sizeof( s_model_t ) );
  1272. g_source[g_numsources] = (s_source_t *)calloc( 1, sizeof( s_source_t ) );
  1273. g_model[g_nummodels]->source = g_source[g_numsources];
  1274. g_numsources++;
  1275. g_bodypart[g_numbodyparts].pmodel[g_bodypart[g_numbodyparts].nummodels] = g_model[g_nummodels];
  1276. strcpyn( g_model[g_nummodels]->name, "blank" );
  1277. g_bodypart[g_numbodyparts].nummodels++;
  1278. g_nummodels++;
  1279. return 0;
  1280. }
  1281. void Cmd_Bodygroup( )
  1282. {
  1283. int is_started = 0;
  1284. if ( !GetToken( false ) )
  1285. return;
  1286. g_bodypart[g_numbodyparts].base = 1;
  1287. if (g_numbodyparts != 0)
  1288. {
  1289. g_bodypart[g_numbodyparts].base = g_bodypart[g_numbodyparts-1].base * g_bodypart[g_numbodyparts-1].nummodels;
  1290. }
  1291. strcpyn( g_bodypart[g_numbodyparts].name, token );
  1292. do
  1293. {
  1294. GetToken (true);
  1295. if (endofscript)
  1296. return;
  1297. else if (token[0] == '{')
  1298. {
  1299. is_started = 1;
  1300. }
  1301. else if (token[0] == '}')
  1302. {
  1303. break;
  1304. }
  1305. else if (stricmp("studio", token ) == 0)
  1306. {
  1307. g_model[g_nummodels] = (s_model_t *)calloc( 1, sizeof( s_model_t ) );
  1308. g_bodypart[g_numbodyparts].pmodel[g_bodypart[g_numbodyparts].nummodels] = g_model[g_nummodels];
  1309. g_bodypart[g_numbodyparts].nummodels++;
  1310. Option_Studio( g_model[g_nummodels] );
  1311. // Body command should add any flex commands in the source loaded
  1312. PostProcessSource( g_model[g_nummodels]->source, g_nummodels );
  1313. g_nummodels++;
  1314. }
  1315. else if (stricmp("blank", token ) == 0)
  1316. {
  1317. Option_Blank( );
  1318. }
  1319. else
  1320. {
  1321. MdlError("unknown bodygroup option: \"%s\"\n", token );
  1322. }
  1323. } while (1);
  1324. g_numbodyparts++;
  1325. return;
  1326. }
  1327. void Cmd_AppendBlankBodygroup( )
  1328. {
  1329. // stick a blank bodygroup on the end of the current part
  1330. g_numbodyparts--;
  1331. Option_Blank( );
  1332. g_numbodyparts++;
  1333. return;
  1334. }
  1335. void Cmd_BodygroupPreset( )
  1336. {
  1337. if ( !GetToken( false ) )
  1338. return;
  1339. // make sure this name is unused
  1340. for ( int i=0; i<g_numbodygrouppresets; i++ )
  1341. {
  1342. if ( !V_strcmp( token, g_bodygrouppresets[i].name ) )
  1343. {
  1344. MdlError( "Error: bodygroup preset \"%s\" already exists.\n", token );
  1345. return;
  1346. }
  1347. }
  1348. s_bodygrouppreset_t newpreset;
  1349. V_strcpy_safe( newpreset.name, token );
  1350. int nAccumValue = 0;
  1351. int nAccumMask = 0;
  1352. do
  1353. {
  1354. GetToken (true);
  1355. if (endofscript)
  1356. {
  1357. return;
  1358. }
  1359. else if (token[0] == '{')
  1360. {
  1361. }
  1362. else if (token[0] == '}')
  1363. {
  1364. break;
  1365. }
  1366. else
  1367. {
  1368. //gather up name:value pairs into a baked bodygroup value and mask
  1369. bool bFoundPart = false;
  1370. for ( int i=0; i<g_numbodyparts; i++ )
  1371. {
  1372. if ( !V_strcmp( g_bodypart[i].name, token ) )
  1373. {
  1374. GetToken (true);
  1375. int iValue = atoi( token );
  1376. if ( iValue >= 0 && iValue < g_bodypart[i].nummodels )
  1377. {
  1378. int iCurrentVal = (nAccumValue / g_bodypart[i].base) % g_bodypart[i].nummodels;
  1379. nAccumValue = (nAccumValue - (iCurrentVal * g_bodypart[i].base) + (iValue * g_bodypart[i].base));
  1380. int iCurrentMask = (nAccumMask / g_bodypart[i].base) % g_bodypart[i].nummodels;
  1381. nAccumMask = (nAccumMask - (iCurrentMask * g_bodypart[i].base) + (1 * g_bodypart[i].base));
  1382. }
  1383. else
  1384. {
  1385. MdlError( "Error: can't assign value \"%i\" to bodygroup preset \"%s\" (out of available range).\n", iValue, newpreset.name );
  1386. return;
  1387. }
  1388. bFoundPart = true;
  1389. }
  1390. }
  1391. if ( !bFoundPart )
  1392. {
  1393. MdlError( "Error: can't find any bodygroups named \"%s\".\n", token );
  1394. return;
  1395. }
  1396. }
  1397. } while (1);
  1398. newpreset.iValue = nAccumValue;
  1399. newpreset.iMask = nAccumMask;
  1400. //Msg( "Built bodygroup preset: %s, value: %i, mask:%i\n", newpreset.name, newpreset.iValue, newpreset.iMask );
  1401. g_bodygrouppresets.AddToTail( newpreset );
  1402. g_numbodygrouppresets++;
  1403. }
  1404. //-----------------------------------------------------------------------------
  1405. // Add A Body Flex Rule
  1406. //-----------------------------------------------------------------------------
  1407. void AddBodyFlexFetchRule(
  1408. s_source_t *pSource,
  1409. s_flexrule_t *pRule,
  1410. int rawIndex,
  1411. const CUtlVector< int > &pRawIndexToRemapSourceIndex,
  1412. const CUtlVector< int > &pRawIndexToRemapLocalIndex,
  1413. const CUtlVector< int > &pRemapSourceIndexToGlobalFlexControllerIndex )
  1414. {
  1415. // Lookup the various indices of the requested input to fetch
  1416. // Relative to the remapped controls in the current s_source_t
  1417. const int remapSourceIndex = pRawIndexToRemapSourceIndex[ rawIndex ];
  1418. // Relative to the specific remapped control
  1419. const int remapLocalIndex = pRawIndexToRemapLocalIndex[ rawIndex ];
  1420. // The global flex controller index that the user ultimately twiddles
  1421. const int globalFlexControllerIndex = pRemapSourceIndexToGlobalFlexControllerIndex[ remapSourceIndex ];
  1422. // Get the Remap record
  1423. s_flexcontrollerremap_t &remap = pSource->m_FlexControllerRemaps[ remapSourceIndex ];
  1424. switch ( remap.m_RemapType )
  1425. {
  1426. case FLEXCONTROLLER_REMAP_PASSTHRU:
  1427. // Easy As!
  1428. pRule->op[ pRule->numops ].op = STUDIO_FETCH1;
  1429. pRule->op[ pRule->numops ].d.index = globalFlexControllerIndex;
  1430. pRule->numops++;
  1431. break;
  1432. case FLEXCONTROLLER_REMAP_EYELID:
  1433. if ( remapLocalIndex == 0 )
  1434. {
  1435. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1436. pRule->op[ pRule->numops ].d.value = remap.m_EyesUpDownFlexController >= 0 ? remap.m_EyesUpDownFlexController : -1;
  1437. pRule->numops++;
  1438. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1439. pRule->op[ pRule->numops ].d.value = remap.m_BlinkController >= 0 ? remap.m_BlinkController : -1;
  1440. pRule->numops++;
  1441. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1442. pRule->op[ pRule->numops ].d.value = globalFlexControllerIndex; // CloseLid
  1443. pRule->numops++;
  1444. pRule->op[ pRule->numops ].op = STUDIO_DME_LOWER_EYELID;
  1445. pRule->op[ pRule->numops ].d.index = remap.m_MultiIndex; // CloseLidV
  1446. pRule->numops++;
  1447. }
  1448. else
  1449. {
  1450. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1451. pRule->op[ pRule->numops ].d.value = remap.m_EyesUpDownFlexController >= 0 ? remap.m_EyesUpDownFlexController : -1;
  1452. pRule->numops++;
  1453. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1454. pRule->op[ pRule->numops ].d.value = remap.m_BlinkController >= 0 ? remap.m_BlinkController : -1;
  1455. pRule->numops++;
  1456. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1457. pRule->op[ pRule->numops ].d.value = globalFlexControllerIndex; // CloseLid
  1458. pRule->numops++;
  1459. pRule->op[ pRule->numops ].op = STUDIO_DME_UPPER_EYELID;
  1460. pRule->op[ pRule->numops ].d.index = remap.m_MultiIndex; // CloseLidV
  1461. pRule->numops++;
  1462. }
  1463. break;
  1464. case FLEXCONTROLLER_REMAP_2WAY:
  1465. // A little trickier... local index 0 is on the left, local index 1 is on the right
  1466. // Left Equivalent RemapVal( -1.0, 0.0, 0.0, 1.0 )
  1467. // Right Equivalent RemapVal( 0.0, 1.0, 0.0, 1.0 )
  1468. if ( remapLocalIndex == 0 )
  1469. {
  1470. pRule->op[ pRule->numops ].op = STUDIO_2WAY_0;
  1471. pRule->op[ pRule->numops ].d.index = globalFlexControllerIndex;
  1472. pRule->numops++;
  1473. }
  1474. else
  1475. {
  1476. pRule->op[ pRule->numops ].op = STUDIO_2WAY_1;
  1477. pRule->op[ pRule->numops ].d.index = globalFlexControllerIndex;
  1478. pRule->numops++;
  1479. }
  1480. break;
  1481. case FLEXCONTROLLER_REMAP_NWAY:
  1482. {
  1483. int nRemapCount = remap.m_RawControls.Count();
  1484. float flStep = ( nRemapCount > 2 ) ? 2.0f / ( nRemapCount - 1 ) : 0.0f;
  1485. if ( remapLocalIndex == 0 )
  1486. {
  1487. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1488. pRule->op[ pRule->numops ].d.value = -11.0f;
  1489. pRule->numops++;
  1490. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1491. pRule->op[ pRule->numops ].d.value = -10.0f;
  1492. pRule->numops++;
  1493. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1494. pRule->op[ pRule->numops ].d.value = -1.0f;
  1495. pRule->numops++;
  1496. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1497. pRule->op[ pRule->numops ].d.value = -1.0f + flStep;
  1498. pRule->numops++;
  1499. }
  1500. else if ( remapLocalIndex == nRemapCount - 1 )
  1501. {
  1502. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1503. pRule->op[ pRule->numops ].d.value = 1.0f - flStep;
  1504. pRule->numops++;
  1505. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1506. pRule->op[ pRule->numops ].d.value = 1.0f;
  1507. pRule->numops++;
  1508. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1509. pRule->op[ pRule->numops ].d.value = 10.0f;
  1510. pRule->numops++;
  1511. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1512. pRule->op[ pRule->numops ].d.value = 11.0f;
  1513. pRule->numops++;
  1514. }
  1515. else
  1516. {
  1517. float flPeak = remapLocalIndex * flStep - 1.0f;
  1518. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1519. pRule->op[ pRule->numops ].d.value = flPeak - flStep;
  1520. pRule->numops++;
  1521. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1522. pRule->op[ pRule->numops ].d.value = flPeak;
  1523. pRule->numops++;
  1524. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1525. pRule->op[ pRule->numops ].d.value = flPeak;
  1526. pRule->numops++;
  1527. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1528. pRule->op[ pRule->numops ].d.value = flPeak + flStep;
  1529. pRule->numops++;
  1530. }
  1531. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1532. pRule->op[ pRule->numops ].d.value = remap.m_MultiIndex;
  1533. pRule->numops++;
  1534. pRule->op[ pRule->numops ].op = STUDIO_NWAY;
  1535. pRule->op[ pRule->numops ].d.index = globalFlexControllerIndex;
  1536. pRule->numops++;
  1537. }
  1538. break;
  1539. default:
  1540. Assert( 0 );
  1541. // This is an error condition
  1542. pRule->op[ pRule->numops ].op = STUDIO_CONST;
  1543. pRule->op[ pRule->numops ].d.value = 1.0f;
  1544. pRule->numops++;
  1545. break;
  1546. }
  1547. }
  1548. //-----------------------------------------------------------------------------
  1549. // Add A Body Flex Rule
  1550. //-----------------------------------------------------------------------------
  1551. void AddBodyFlexRule(
  1552. s_source_t *pSource,
  1553. s_combinationrule_t &rule,
  1554. int nFlexDesc,
  1555. const CUtlVector< int > &pRawIndexToRemapSourceIndex,
  1556. const CUtlVector< int > &pRawIndexToRemapLocalIndex,
  1557. const CUtlVector< int > &pRemapSourceIndexToGlobalFlexControllerIndex )
  1558. {
  1559. if ( g_numflexrules >= MAXSTUDIOFLEXRULES )
  1560. MdlError( "Line %d: Too many flex rules, max %d",
  1561. g_iLinecount, MAXSTUDIOFLEXRULES );
  1562. s_flexrule_t *pRule = &g_flexrule[g_numflexrules++];
  1563. pRule->flex = nFlexDesc;
  1564. // This will multiply the combination together
  1565. const int nCombinationCount = rule.m_Combination.Count();
  1566. if ( nCombinationCount )
  1567. {
  1568. for ( int j = 0; j < nCombinationCount; ++j )
  1569. {
  1570. // Handle any controller remapping
  1571. AddBodyFlexFetchRule( pSource, pRule, rule.m_Combination[ j ],
  1572. pRawIndexToRemapSourceIndex, pRawIndexToRemapLocalIndex,
  1573. pRemapSourceIndexToGlobalFlexControllerIndex );
  1574. }
  1575. if ( nCombinationCount > 1 )
  1576. {
  1577. pRule->op[ pRule->numops ].op = STUDIO_COMBO;
  1578. pRule->op[ pRule->numops ].d.index = nCombinationCount;
  1579. pRule->numops++;
  1580. }
  1581. }
  1582. // This will multiply in the suppressors
  1583. int nDominators = rule.m_Dominators.Count();
  1584. for ( int j = 0; j < nDominators; ++j )
  1585. {
  1586. const int nFactorCount = rule.m_Dominators[j].Count();
  1587. if ( nFactorCount )
  1588. {
  1589. for ( int k = 0; k < nFactorCount; ++k )
  1590. {
  1591. AddBodyFlexFetchRule( pSource, pRule, rule.m_Dominators[ j ][ k ],
  1592. pRawIndexToRemapSourceIndex, pRawIndexToRemapLocalIndex,
  1593. pRemapSourceIndexToGlobalFlexControllerIndex );
  1594. }
  1595. pRule->op[ pRule->numops ].op = STUDIO_DOMINATE;
  1596. pRule->op[ pRule->numops ].d.index = nFactorCount;
  1597. pRule->numops++;
  1598. }
  1599. }
  1600. }
  1601. //-----------------------------------------------------------------------------
  1602. // Adds flex controller data to a particular source
  1603. //-----------------------------------------------------------------------------
  1604. void AddFlexControllers(
  1605. s_source_t *pSource )
  1606. {
  1607. CUtlVector< int > &r2s = pSource->m_rawIndexToRemapSourceIndex;
  1608. CUtlVector< int > &r2l = pSource->m_rawIndexToRemapLocalIndex;
  1609. CUtlVector< int > &l2i = pSource->m_leftRemapIndexToGlobalFlexControllIndex;
  1610. CUtlVector< int > &r2i = pSource->m_rightRemapIndexToGlobalFlexControllIndex;
  1611. // Number of Raw controls in this source
  1612. const int nRawControlCount = pSource->m_CombinationControls.Count();
  1613. // Initialize rawToRemapIndices
  1614. r2s.SetSize( nRawControlCount );
  1615. r2l.SetSize( nRawControlCount );
  1616. for ( int i = 0; i < nRawControlCount; ++i )
  1617. {
  1618. r2s[ i ] = -1;
  1619. r2l[ i ] = -1;
  1620. }
  1621. // Number of Remapped Controls in this source
  1622. const int nRemappedControlCount = pSource->m_FlexControllerRemaps.Count();
  1623. l2i.SetSize( nRemappedControlCount );
  1624. r2i.SetSize( nRemappedControlCount );
  1625. for ( int i = 0; i < nRemappedControlCount; ++i )
  1626. {
  1627. s_flexcontrollerremap_t &remapControl = pSource->m_FlexControllerRemaps[ i ];
  1628. // Number of Raw Controls In This Remapped Control
  1629. const int nRemappedRawControlCount = remapControl.m_RawControls.Count();
  1630. // Figure out the mapping from raw to remapped
  1631. for ( int j = 0; j < nRemappedRawControlCount; ++j )
  1632. {
  1633. for ( int k = 0; k < nRawControlCount; ++k )
  1634. {
  1635. if ( remapControl.m_RawControls[ j ] == pSource->m_CombinationControls[ k ].name )
  1636. {
  1637. Assert( r2s[ k ] == -1 );
  1638. Assert( r2l[ k ] == -1 );
  1639. r2s[ k ] = i; // The index of the remapped control
  1640. r2l[ k ] = j; // The index of which control this is in the remap
  1641. break;
  1642. }
  1643. }
  1644. }
  1645. if ( remapControl.m_bIsStereo )
  1646. {
  1647. // The controls have to be named 'right_' and 'left_' and right has to be first for
  1648. // hlfaceposer to recognize them
  1649. // See if we can add two more flex controllers
  1650. if ( ( g_numflexcontrollers + 1 ) >= MAXSTUDIOFLEXCTRL)
  1651. MdlError( "Line %d: Too many flex controllers, max %d, cannot add split control %s from source %s",
  1652. g_iLinecount, MAXSTUDIOFLEXCTRL, remapControl.m_Name.Get(), pSource->filename );
  1653. s_flexcontroller_t *pController;
  1654. int nLen = remapControl.m_Name.Length();
  1655. char *pTemp = (char*)_alloca( nLen + 7 ); // 'left_' && 'right_'
  1656. memcpy( pTemp + 6, remapControl.m_Name.Get(), nLen + 1 );
  1657. memcpy( pTemp, "right_", 6 );
  1658. pTemp[nLen + 6] = '\0';
  1659. remapControl.m_RightIndex = g_numflexcontrollers;
  1660. r2i[ i ] = g_numflexcontrollers;
  1661. pController = &g_flexcontroller[g_numflexcontrollers++];
  1662. Q_strncpy( pController->name, pTemp, sizeof( pController->name ) );
  1663. Q_strncpy( pController->type, pTemp, sizeof( pController->type ) );
  1664. if ( remapControl.m_RemapType == FLEXCONTROLLER_REMAP_2WAY || remapControl.m_RemapType == FLEXCONTROLLER_REMAP_EYELID )
  1665. {
  1666. pController->min = -1.0f;
  1667. pController->max = 1.0f;
  1668. }
  1669. else
  1670. {
  1671. pController->min = 0.0f;
  1672. pController->max = 1.0f;
  1673. }
  1674. memcpy( pTemp + 5, remapControl.m_Name.Get(), nLen + 1 );
  1675. memcpy( pTemp, "left_", 5 );
  1676. pTemp[nLen + 5] = '\0';
  1677. remapControl.m_LeftIndex = g_numflexcontrollers;
  1678. l2i[ i ] = g_numflexcontrollers;
  1679. pController = &g_flexcontroller[g_numflexcontrollers++];
  1680. Q_strncpy( pController->name, pTemp, sizeof( pController->name ) );
  1681. Q_strncpy( pController->type, pTemp, sizeof( pController->type ) );
  1682. if ( remapControl.m_RemapType == FLEXCONTROLLER_REMAP_2WAY || remapControl.m_RemapType == FLEXCONTROLLER_REMAP_EYELID )
  1683. {
  1684. pController->min = -1.0f;
  1685. pController->max = 1.0f;
  1686. }
  1687. else
  1688. {
  1689. pController->min = 0.0f;
  1690. pController->max = 1.0f;
  1691. }
  1692. }
  1693. else
  1694. {
  1695. // See if we can add one more flex controller
  1696. if ( g_numflexcontrollers >= MAXSTUDIOFLEXCTRL)
  1697. MdlError( "Line %d: Too many flex controllers, max %d, cannot add control %s from source %s",
  1698. g_iLinecount, MAXSTUDIOFLEXCTRL, remapControl.m_Name.Get(), pSource->filename );
  1699. remapControl.m_Index = g_numflexcontrollers;
  1700. r2i[ i ] = g_numflexcontrollers;
  1701. l2i[ i ] = g_numflexcontrollers;
  1702. s_flexcontroller_t *pController = &g_flexcontroller[g_numflexcontrollers++];
  1703. Q_strncpy( pController->name, remapControl.m_Name.Get(), sizeof( pController->name ) );
  1704. Q_strncpy( pController->type, remapControl.m_Name.Get(), sizeof( pController->type ) );
  1705. if ( remapControl.m_RemapType == FLEXCONTROLLER_REMAP_2WAY || remapControl.m_RemapType == FLEXCONTROLLER_REMAP_EYELID )
  1706. {
  1707. pController->min = -1.0f;
  1708. pController->max = 1.0f;
  1709. }
  1710. else
  1711. {
  1712. pController->min = 0.0f;
  1713. pController->max = 1.0f;
  1714. }
  1715. }
  1716. if ( remapControl.m_RemapType == FLEXCONTROLLER_REMAP_NWAY || remapControl.m_RemapType == FLEXCONTROLLER_REMAP_EYELID )
  1717. {
  1718. if ( g_numflexcontrollers >= MAXSTUDIOFLEXCTRL)
  1719. MdlError( "Line %d: Too many flex controllers, max %d, cannot add value control for nWay %s from source %s",
  1720. g_iLinecount, MAXSTUDIOFLEXCTRL, remapControl.m_Name.Get(), pSource->filename );
  1721. remapControl.m_MultiIndex = g_numflexcontrollers;
  1722. s_flexcontroller_t *pController = &g_flexcontroller[g_numflexcontrollers++];
  1723. const int nLen = remapControl.m_Name.Length();
  1724. char *pTemp = ( char * )_alloca( nLen + 6 + 1 ); // 'multi_' + 1 for the NULL
  1725. memcpy( pTemp, "multi_", 6 );
  1726. memcpy( pTemp + 6, remapControl.m_Name.Get(), nLen + 1 );
  1727. pTemp[nLen+6] = '\0';
  1728. Q_strncpy( pController->name, pTemp, sizeof( pController->name ) );
  1729. Q_strncpy( pController->type, pTemp, sizeof( pController->type ) );
  1730. pController->min = -1.0f;
  1731. pController->max = 1.0f;
  1732. }
  1733. if ( remapControl.m_RemapType == FLEXCONTROLLER_REMAP_EYELID )
  1734. {
  1735. // Make a blink controller
  1736. if ( g_numflexcontrollers >= MAXSTUDIOFLEXCTRL)
  1737. MdlError( "Line %d: Too many flex controllers, max %d, cannot add value control for nWay %s from source %s",
  1738. g_iLinecount, MAXSTUDIOFLEXCTRL, remapControl.m_Name.Get(), pSource->filename );
  1739. remapControl.m_BlinkController = g_numflexcontrollers;
  1740. s_flexcontroller_t *pController = &g_flexcontroller[g_numflexcontrollers++];
  1741. Q_strncpy( pController->name, "blink", sizeof( pController->name ) );
  1742. Q_strncpy( pController->type, "blink", sizeof( pController->type ) );
  1743. pController->min = 0.0f;
  1744. pController->max = 1.0f;
  1745. }
  1746. }
  1747. #ifdef _DEBUG
  1748. for ( int j = 0; j != nRawControlCount; ++j )
  1749. {
  1750. Assert( r2s[ j ] != -1 );
  1751. Assert( r2l[ j ] != -1 );
  1752. }
  1753. #endif // def _DEBUG
  1754. }
  1755. //-----------------------------------------------------------------------------
  1756. // Adds flex controller remappers
  1757. //-----------------------------------------------------------------------------
  1758. void AddBodyFlexRemaps( s_source_t *pSource )
  1759. {
  1760. int nCount = pSource->m_FlexControllerRemaps.Count();
  1761. for( int i = 0; i < nCount; ++i )
  1762. {
  1763. int k = g_FlexControllerRemap.AddToTail();
  1764. s_flexcontrollerremap_t &remap = g_FlexControllerRemap[k];
  1765. remap = pSource->m_FlexControllerRemaps[i];
  1766. }
  1767. }
  1768. //-----------------------------------------------------------------------------
  1769. //
  1770. //-----------------------------------------------------------------------------
  1771. void AddBodyFlexRules( s_source_t *pSource )
  1772. {
  1773. const int nRemapCount = pSource->m_FlexControllerRemaps.Count();
  1774. for ( int i = 0; i < nRemapCount; ++i )
  1775. {
  1776. s_flexcontrollerremap_t &remap = pSource->m_FlexControllerRemaps[ i ];
  1777. if ( remap.m_RemapType == FLEXCONTROLLER_REMAP_EYELID && !remap.m_EyesUpDownFlexName.IsEmpty() )
  1778. {
  1779. for ( int j = 0; j < g_numflexcontrollers; ++j )
  1780. {
  1781. if ( !Q_strcmp( g_flexcontroller[ j ].name, remap.m_EyesUpDownFlexName.Get() ) )
  1782. {
  1783. Assert( remap.m_EyesUpDownFlexController == -1 );
  1784. remap.m_EyesUpDownFlexController = j;
  1785. break;
  1786. }
  1787. }
  1788. }
  1789. }
  1790. const int nCount = pSource->m_CombinationRules.Count();
  1791. for ( int i = 0; i < nCount; ++i )
  1792. {
  1793. s_combinationrule_t &rule = pSource->m_CombinationRules[i];
  1794. s_flexkey_t &flexKey = g_flexkey[ pSource->m_nKeyStartIndex + rule.m_nFlex ];
  1795. AddBodyFlexRule( pSource, rule, flexKey.flexdesc,
  1796. pSource->m_rawIndexToRemapSourceIndex, pSource->m_rawIndexToRemapLocalIndex, pSource->m_leftRemapIndexToGlobalFlexControllIndex );
  1797. if ( flexKey.flexpair != 0 )
  1798. {
  1799. AddBodyFlexRule( pSource, rule, flexKey.flexpair,
  1800. pSource->m_rawIndexToRemapSourceIndex, pSource->m_rawIndexToRemapLocalIndex, pSource->m_rightRemapIndexToGlobalFlexControllIndex );
  1801. }
  1802. }
  1803. }
  1804. //-----------------------------------------------------------------------------
  1805. // Process a body command
  1806. //-----------------------------------------------------------------------------
  1807. void AddBodyFlexData( s_source_t *pSource, int imodel )
  1808. {
  1809. pSource->m_nKeyStartIndex = g_numflexkeys;
  1810. // Add flex keys
  1811. int nCount = pSource->m_FlexKeys.Count();
  1812. for ( int i = 0; i < nCount; ++i )
  1813. {
  1814. s_flexkey_t &key = pSource->m_FlexKeys[i];
  1815. if ( g_numflexkeys >= MAXSTUDIOFLEXKEYS )
  1816. MdlError( "Line %d: Too many flex keys, max %d, cannot add flexKey %s from source %s",
  1817. g_iLinecount, MAXSTUDIOFLEXKEYS, key.animationname, pSource->filename );
  1818. memcpy( &g_flexkey[g_numflexkeys], &key, sizeof(s_flexkey_t) );
  1819. g_flexkey[g_numflexkeys].imodel = imodel;
  1820. // flexpair was set up in AddFlexKey
  1821. if ( key.flexpair )
  1822. {
  1823. char mod[512];
  1824. Q_snprintf( mod, sizeof(mod), "%sL", key.animationname );
  1825. g_flexkey[g_numflexkeys].flexdesc = Add_Flexdesc( mod );
  1826. Q_snprintf( mod, sizeof(mod), "%sR", key.animationname );
  1827. g_flexkey[g_numflexkeys].flexpair = Add_Flexdesc( mod );
  1828. }
  1829. else
  1830. {
  1831. g_flexkey[g_numflexkeys].flexdesc = Add_Flexdesc( key.animationname );
  1832. g_flexkey[g_numflexkeys].flexpair = 0;
  1833. }
  1834. ++g_numflexkeys;
  1835. }
  1836. AddFlexControllers( pSource );
  1837. AddBodyFlexRemaps( pSource );
  1838. }
  1839. //-----------------------------------------------------------------------------
  1840. // Comparison operator for s_attachment_t
  1841. //-----------------------------------------------------------------------------
  1842. bool s_attachment_t::operator==( const s_attachment_t &rhs ) const
  1843. {
  1844. if ( Q_strcmp( name, rhs.name ) )
  1845. return false;
  1846. if ( Q_stricmp( bonename, rhs.bonename ) ||
  1847. bone != rhs.bone ||
  1848. type != rhs.type ||
  1849. flags != rhs.flags ||
  1850. Q_memcmp( local.Base(), rhs.local.Base(), sizeof( local ) ) )
  1851. {
  1852. RadianEuler iEuler, jEuler;
  1853. Vector iPos, jPos;
  1854. MatrixAngles( local, iEuler, iPos );
  1855. MatrixAngles( rhs.local, jEuler, jPos );
  1856. MdlWarning(
  1857. "Attachments with the same name but different parameters found\n"
  1858. " %s: ParentBone: %s Type: %d Flags: 0x%08x P: %6.2f %6.2f %6.2f R: %6.2f %6.2f %6.2f\n"
  1859. " %s: ParentBone: %s Type: %d Flags: 0x%08x P: %6.2f %6.2f %6.2f R: %6.2f %6.2f %6.2f\n",
  1860. name, bonename, type, flags,
  1861. iPos.x, iPos.y, iPos.z, RAD2DEG( iEuler.x ), RAD2DEG( iEuler.y ), RAD2DEG( iEuler.z ),
  1862. rhs.name, rhs.bonename, rhs.type, rhs.flags,
  1863. jPos.x, jPos.y, jPos.z, RAD2DEG( jEuler.x ), RAD2DEG( jEuler.y ), RAD2DEG( jEuler.z ) );
  1864. return false;
  1865. }
  1866. return true;
  1867. }
  1868. //-----------------------------------------------------------------------------
  1869. //
  1870. //-----------------------------------------------------------------------------
  1871. bool s_constraintbonetarget_t::operator==( const s_constraintbonetarget_t &rhs ) const
  1872. {
  1873. if ( V_strcmp( m_szBoneName, rhs.m_szBoneName ) )
  1874. return false;
  1875. if ( m_flWeight != rhs.m_flWeight ||
  1876. !VectorsAreEqual( m_vOffset, rhs.m_vOffset ) ||
  1877. !QuaternionsAreEqual( m_qOffset, rhs.m_qOffset, 1.0e-4 ) )
  1878. {
  1879. const RadianEuler e( m_qOffset );
  1880. const RadianEuler eRhs( rhs.m_qOffset );
  1881. MdlWarning(
  1882. "Constraint bones with same target but different target parameters found\n"
  1883. " Target %s: W: %6.2f VO: %6.2f %6.2f %6.2f RO: %6.2f %6.2f %6.2f\n"
  1884. " Target %s: W: %6.2f VO: %6.2f %6.2f %6.2f RO: %6.2f %6.2f %6.2f\n",
  1885. m_szBoneName, m_flWeight, m_vOffset.x, m_vOffset.y, m_vOffset.z, RAD2DEG( e.x ), RAD2DEG( e.y ), RAD2DEG( e.z ),
  1886. rhs.m_szBoneName, rhs.m_flWeight, rhs.m_vOffset.x, rhs.m_vOffset.y, rhs.m_vOffset.z, RAD2DEG( eRhs.x ), RAD2DEG( eRhs.y ), RAD2DEG( eRhs.z ) );
  1887. }
  1888. return true;
  1889. }
  1890. //-----------------------------------------------------------------------------
  1891. //
  1892. //-----------------------------------------------------------------------------
  1893. bool s_constraintboneslave_t::operator==( const s_constraintboneslave_t &rhs ) const
  1894. {
  1895. if ( V_strcmp( m_szBoneName, rhs.m_szBoneName ) )
  1896. return false;
  1897. if ( !VectorsAreEqual( m_vBaseTranslate, rhs.m_vBaseTranslate ) ||
  1898. !QuaternionsAreEqual( m_qBaseRotation, rhs.m_qBaseRotation, 1.0e-4 ) )
  1899. {
  1900. const RadianEuler e( m_qBaseRotation );
  1901. const RadianEuler eRhs( rhs.m_qBaseRotation );
  1902. MdlWarning(
  1903. "Constraint bones with same target but different slave parameters found\n"
  1904. " Target %s: VO: %6.2f %6.2f %6.2f RO: %6.2f %6.2f %6.2f\n"
  1905. " Target %s: VO: %6.2f %6.2f %6.2f RO: %6.2f %6.2f %6.2f\n",
  1906. m_szBoneName, m_vBaseTranslate.x, m_vBaseTranslate.y, m_vBaseTranslate.z, RAD2DEG( e.x ), RAD2DEG( e.y ), RAD2DEG( e.z ),
  1907. rhs.m_szBoneName, rhs.m_vBaseTranslate.x, rhs.m_vBaseTranslate.y, rhs.m_vBaseTranslate.z, RAD2DEG( eRhs.x ), RAD2DEG( eRhs.y ), RAD2DEG( eRhs.z ) );
  1908. }
  1909. return true;
  1910. }
  1911. //-----------------------------------------------------------------------------
  1912. //
  1913. //-----------------------------------------------------------------------------
  1914. bool CConstraintBoneBase::operator==( const CConstraintBoneBase &rhs ) const
  1915. {
  1916. if ( m_slave != rhs.m_slave )
  1917. return false;
  1918. if ( m_targets.Count() != rhs.m_targets.Count() )
  1919. return false;
  1920. for ( int i = 0; i < m_targets.Count(); ++i )
  1921. {
  1922. if ( m_targets[i] != rhs.m_targets[i] )
  1923. return false;
  1924. }
  1925. {
  1926. // TODO: Add a static type field
  1927. const CAimConstraint *pAimThis = dynamic_cast< const CAimConstraint * >( this );
  1928. if ( pAimThis )
  1929. {
  1930. if ( !dynamic_cast< const CAimConstraint * >( &rhs ) )
  1931. return false;
  1932. }
  1933. }
  1934. return true;
  1935. }
  1936. #ifdef MDLCOMPILE
  1937. //-----------------------------------------------------------------------------
  1938. // Add attachments from the s_source_t that aren't already present in the
  1939. // global attachment list. At this point, the attachments aren't linked
  1940. // to the bone, but since that is done by string matching on the bone name
  1941. // the test for an attachment being a duplicate is still valid this early.
  1942. // Only doing it this way for mdlcompile though.
  1943. //-----------------------------------------------------------------------------
  1944. void AddBodyAttachments( s_source_t *pSource )
  1945. {
  1946. for ( int i = 0; i < pSource->m_Attachments.Count(); ++i )
  1947. {
  1948. const s_attachment_t &sourceAtt = pSource->m_Attachments[i];
  1949. bool bDuplicate = false;
  1950. for ( int j = 0; j < g_numattachments; ++j )
  1951. {
  1952. if ( sourceAtt == g_attachment[j] )
  1953. {
  1954. bDuplicate = true;
  1955. break;
  1956. }
  1957. }
  1958. if ( bDuplicate )
  1959. continue;
  1960. if ( g_numattachments >= ARRAYSIZE( g_attachment ) )
  1961. {
  1962. MdlWarning( "Too Many Attachments (Max %d), Ignoring Attachment %s:%s\n",
  1963. ARRAYSIZE( g_attachment ), pSource->filename, pSource->m_Attachments[i].name );
  1964. continue;;
  1965. }
  1966. memcpy( &g_attachment[g_numattachments], &( pSource->m_Attachments[i] ), sizeof( s_attachment_t ) );
  1967. ++g_numattachments;
  1968. }
  1969. }
  1970. #else
  1971. //-----------------------------------------------------------------------------
  1972. // Add all attachments from the source to the global attachment list
  1973. // stopping when the g_attachment array is full. Duplicate attachments
  1974. // will be purged later after they are linked to bones
  1975. //-----------------------------------------------------------------------------
  1976. void AddBodyAttachments( s_source_t *pSource )
  1977. {
  1978. for ( int i = 0; i < pSource->m_Attachments.Count(); ++i )
  1979. {
  1980. if ( g_numattachments >= ARRAYSIZE( g_attachment ) )
  1981. {
  1982. MdlWarning( "Too Many Attachments (Max %d), Ignoring Attachment %s:%s\n",
  1983. ARRAYSIZE( g_attachment ), pSource->filename, pSource->m_Attachments[i].name );
  1984. continue;;
  1985. }
  1986. memcpy( &g_attachment[g_numattachments], &( pSource->m_Attachments[i] ), sizeof( s_attachment_t ) );
  1987. ++g_numattachments;
  1988. }
  1989. }
  1990. #endif
  1991. //-----------------------------------------------------------------------------
  1992. // Post-processes a source (used when loading preprocessed files)
  1993. //-----------------------------------------------------------------------------
  1994. void PostProcessSource( s_source_t *pSource, int imodel )
  1995. {
  1996. if ( pSource )
  1997. {
  1998. AddBodyFlexData( pSource, imodel );
  1999. AddBodyAttachments( pSource );
  2000. AddBodyFlexRules( pSource );
  2001. }
  2002. }
  2003. //-----------------------------------------------------------------------------
  2004. // Process a body command
  2005. //-----------------------------------------------------------------------------
  2006. void ProcessCmdBody( const char *pFullPath, const char *pBodyPartName, float flScale, bool bFlipTriangles, bool bQuadSubd )
  2007. {
  2008. g_bodypart[g_numbodyparts].base = 1;
  2009. if ( g_numbodyparts != 0 )
  2010. {
  2011. g_bodypart[g_numbodyparts].base = g_bodypart[g_numbodyparts-1].base * g_bodypart[g_numbodyparts-1].nummodels;
  2012. }
  2013. Q_strncpy( g_bodypart[g_numbodyparts].name, pBodyPartName, sizeof(g_bodypart[g_numbodyparts].name) );
  2014. g_model[g_nummodels] = (s_model_t *)calloc( 1, sizeof( s_model_t ) );
  2015. g_bodypart[g_numbodyparts].pmodel[0] = g_model[g_nummodels];
  2016. g_bodypart[g_numbodyparts].nummodels = 1;
  2017. ProcessOptionStudio( g_model[g_nummodels], pFullPath, flScale, bFlipTriangles, bQuadSubd );
  2018. // Body command should add any flex commands in the source loaded
  2019. PostProcessSource( g_model[g_nummodels]->source, g_nummodels );
  2020. g_nummodels++;
  2021. g_numbodyparts++;
  2022. }
  2023. //-----------------------------------------------------------------------------
  2024. // Parse the body command from a .qc file
  2025. //-----------------------------------------------------------------------------
  2026. void Cmd_Body( )
  2027. {
  2028. if ( !GetToken(false) )
  2029. return;
  2030. CDmeSourceSkin *pSourceSkin = CreateElement< CDmeSourceSkin >( "", DMFILEID_INVALID );
  2031. // Set defaults
  2032. pSourceSkin->m_flScale = g_defaultscale;
  2033. pSourceSkin->m_SkinName = token;
  2034. if ( ParseOptionStudio( pSourceSkin ) )
  2035. {
  2036. ProcessCmdBody( pSourceSkin->GetRelativeFileName(), pSourceSkin->m_SkinName.Get(),
  2037. pSourceSkin->m_flScale, pSourceSkin->m_bFlipTriangles, pSourceSkin->m_bQuadSubd );
  2038. }
  2039. DestroyElement( pSourceSkin );
  2040. }
  2041. void Cmd_PreferFbx()
  2042. {
  2043. g_bPreferFbx = true;
  2044. }
  2045. /*
  2046. ===============
  2047. ===============
  2048. */
  2049. void Grab_Animation( s_source_t *pSource, const char *pAnimName )
  2050. {
  2051. Vector pos;
  2052. RadianEuler rot;
  2053. char cmd[1024];
  2054. int index;
  2055. int t = -99999999;
  2056. int size;
  2057. s_sourceanim_t *pAnim = FindOrAddSourceAnim( pSource, pAnimName );
  2058. pAnim->startframe = -1;
  2059. size = pSource->numbones * sizeof( s_bone_t );
  2060. while ( GetLineInput() )
  2061. {
  2062. if ( sscanf( g_szLine, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &rot[0], &rot[1], &rot[2] ) == 7 )
  2063. {
  2064. if ( pAnim->startframe < 0 )
  2065. {
  2066. MdlError( "Missing frame start(%d) : %s", g_iLinecount, g_szLine );
  2067. }
  2068. scale_vertex( pos );
  2069. VectorCopy( pos, pAnim->rawanim[t][index].pos );
  2070. VectorCopy( rot, pAnim->rawanim[t][index].rot );
  2071. clip_rotations( rot ); // !!!
  2072. continue;
  2073. }
  2074. if ( sscanf( g_szLine, "%1023s %d", cmd, &index ) == 0 )
  2075. {
  2076. MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
  2077. continue;
  2078. }
  2079. if ( !Q_stricmp( cmd, "time" ) )
  2080. {
  2081. t = index;
  2082. if ( pAnim->startframe == -1 )
  2083. {
  2084. pAnim->startframe = t;
  2085. }
  2086. if ( t < pAnim->startframe )
  2087. {
  2088. MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine );
  2089. }
  2090. if ( t > pAnim->endframe )
  2091. {
  2092. pAnim->endframe = t;
  2093. }
  2094. t -= pAnim->startframe;
  2095. if ( t >= pAnim->rawanim.Count())
  2096. {
  2097. s_bone_t *ptr = NULL;
  2098. pAnim->rawanim.AddMultipleToTail( t - pAnim->rawanim.Count() + 1, &ptr );
  2099. }
  2100. if ( pAnim->rawanim[t] != NULL )
  2101. {
  2102. continue;
  2103. }
  2104. pAnim->rawanim[t] = (s_bone_t *)calloc( 1, size );
  2105. // duplicate previous frames keys
  2106. if ( t > 0 && pAnim->rawanim[t-1] )
  2107. {
  2108. for ( int j = 0; j < pSource->numbones; j++ )
  2109. {
  2110. VectorCopy( pAnim->rawanim[t-1][j].pos, pAnim->rawanim[t][j].pos );
  2111. VectorCopy( pAnim->rawanim[t-1][j].rot, pAnim->rawanim[t][j].rot );
  2112. }
  2113. }
  2114. continue;
  2115. }
  2116. if ( !Q_stricmp( cmd, "end" ) )
  2117. {
  2118. pAnim->numframes = pAnim->endframe - pAnim->startframe + 1;
  2119. for ( t = 0; t < pAnim->numframes; t++ )
  2120. {
  2121. if ( pAnim->rawanim[t] == NULL)
  2122. {
  2123. MdlError( "%s is missing frame %d\n", pSource->filename, t + pAnim->startframe );
  2124. }
  2125. }
  2126. Build_Reference( pSource, pAnimName );
  2127. return;
  2128. }
  2129. MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
  2130. }
  2131. MdlError( "unexpected EOF: %s\n", pSource->filename );
  2132. }
  2133. int Option_Activity( s_sequence_t *psequence )
  2134. {
  2135. qboolean found;
  2136. found = false;
  2137. GetToken(false);
  2138. strcpy( psequence->activityname, token );
  2139. if ( g_AllowedActivityNames.Count() && g_AllowedActivityNames.Find( token ) == -1 )
  2140. {
  2141. MdlError( "Unknown sequence activity \"%s\" in \"%s\".", token, psequence->name );
  2142. }
  2143. GetToken(false);
  2144. psequence->actweight = verify_atoi(token);
  2145. if ( psequence->actweight == 0 )
  2146. {
  2147. TokenError( "Activity %s has a zero weight (weights must be integers > 0)\n", psequence->activityname );
  2148. }
  2149. return 0;
  2150. }
  2151. int Option_ActivityModifier( s_sequence_t *psequence )
  2152. {
  2153. GetToken(false);
  2154. if (token[0] == '{')
  2155. {
  2156. while ( TokenAvailable() )
  2157. {
  2158. GetToken( true );
  2159. if (stricmp("}", token ) == 0)
  2160. break;
  2161. strlwr(token);
  2162. strcpyn( psequence->activitymodifier[ psequence->numactivitymodifiers++ ].name, token );
  2163. }
  2164. }
  2165. else
  2166. {
  2167. strlwr(token);
  2168. strcpyn( psequence->activitymodifier[ psequence->numactivitymodifiers++ ].name, token );
  2169. }
  2170. return 0;
  2171. }
  2172. /*
  2173. ===============
  2174. ===============
  2175. */
  2176. int Option_AnimTag ( s_sequence_t *psequence )
  2177. {
  2178. if (psequence->numanimtags + 1 >= MAXSTUDIOTAGS)
  2179. {
  2180. TokenError("too many animtags\n");
  2181. }
  2182. GetToken (false);
  2183. strcpy( psequence->animtags[psequence->numanimtags].tagname, token );
  2184. GetToken( false );
  2185. psequence->animtags[psequence->numanimtags].cycle = verify_atof( token );
  2186. psequence->numanimtags++;
  2187. return 0;
  2188. }
  2189. int Option_Event ( s_sequence_t *psequence )
  2190. {
  2191. if (psequence->numevents + 1 >= MAXSTUDIOEVENTS)
  2192. {
  2193. TokenError("too many events\n");
  2194. }
  2195. GetToken (false);
  2196. strcpy( psequence->event[psequence->numevents].eventname, token );
  2197. GetToken( false );
  2198. psequence->event[psequence->numevents].frame = verify_atoi( token );
  2199. psequence->numevents++;
  2200. // option token
  2201. if (TokenAvailable())
  2202. {
  2203. GetToken( false );
  2204. if (token[0] == '}') // opps, hit the end
  2205. return 1;
  2206. // found an option
  2207. strcpyn( psequence->event[psequence->numevents-1].options, token );
  2208. }
  2209. return 0;
  2210. }
  2211. void Option_IKRule( s_ikrule_t *pRule )
  2212. {
  2213. // chain
  2214. GetToken( false );
  2215. int i;
  2216. for ( i = 0; i < g_numikchains; i++)
  2217. {
  2218. if (stricmp( token, g_ikchain[i].name ) == 0)
  2219. {
  2220. break;
  2221. }
  2222. }
  2223. if (i >= g_numikchains)
  2224. {
  2225. TokenError( "unknown chain \"%s\" in ikrule\n", token );
  2226. }
  2227. pRule->chain = i;
  2228. // default slot
  2229. pRule->slot = i;
  2230. // type
  2231. GetToken( false );
  2232. if (stricmp( token, "autosteps" ) == 0)
  2233. {
  2234. GetToken( false );
  2235. pRule->end = verify_atoi( token );
  2236. GetToken( false );
  2237. strcpyn( pRule->bonename, token );
  2238. pRule->type = IK_GROUND;
  2239. pRule->height = g_ikchain[pRule->chain].height;
  2240. pRule->floor = g_ikchain[pRule->chain].floor;
  2241. pRule->radius = g_ikchain[pRule->chain].radius;
  2242. pRule->start = -2;
  2243. pRule->peak = -1;
  2244. pRule->tail = -1;
  2245. }
  2246. else if (stricmp( token, "touch" ) == 0)
  2247. {
  2248. pRule->type = IK_SELF;
  2249. // bone
  2250. GetToken( false );
  2251. strcpyn( pRule->bonename, token );
  2252. }
  2253. else if (stricmp( token, "footstep" ) == 0)
  2254. {
  2255. pRule->type = IK_GROUND;
  2256. pRule->height = g_ikchain[pRule->chain].height;
  2257. pRule->floor = g_ikchain[pRule->chain].floor;
  2258. pRule->radius = g_ikchain[pRule->chain].radius;
  2259. }
  2260. else if (stricmp( token, "attachment" ) == 0)
  2261. {
  2262. pRule->type = IK_ATTACHMENT;
  2263. // name of attachment
  2264. GetToken( false );
  2265. strcpyn( pRule->attachment, token );
  2266. }
  2267. else if (stricmp( token, "release" ) == 0)
  2268. {
  2269. pRule->type = IK_RELEASE;
  2270. }
  2271. else if (stricmp( token, "unlatch" ) == 0)
  2272. {
  2273. pRule->type = IK_UNLATCH;
  2274. }
  2275. pRule->contact = -1;
  2276. while (TokenAvailable())
  2277. {
  2278. GetToken( false );
  2279. if (stricmp( token, "height" ) == 0)
  2280. {
  2281. GetToken( false );
  2282. pRule->height = verify_atof( token );
  2283. }
  2284. else if (stricmp( token, "target" ) == 0)
  2285. {
  2286. // slot
  2287. GetToken( false );
  2288. pRule->slot = verify_atoi( token );
  2289. }
  2290. else if (stricmp( token, "range" ) == 0)
  2291. {
  2292. // ramp
  2293. GetToken( false );
  2294. if (token[0] == '.')
  2295. pRule->start = -1;
  2296. else
  2297. pRule->start = verify_atoi( token );
  2298. GetToken( false );
  2299. if (token[0] == '.')
  2300. pRule->peak = -1;
  2301. else
  2302. pRule->peak = verify_atoi( token );
  2303. GetToken( false );
  2304. if (token[0] == '.')
  2305. pRule->tail = -1;
  2306. else
  2307. pRule->tail = verify_atoi( token );
  2308. GetToken( false );
  2309. if (token[0] == '.')
  2310. pRule->end = -1;
  2311. else
  2312. pRule->end = verify_atoi( token );
  2313. }
  2314. else if (stricmp( token, "floor" ) == 0)
  2315. {
  2316. GetToken( false );
  2317. pRule->floor = verify_atof( token );
  2318. }
  2319. else if (stricmp( token, "pad" ) == 0)
  2320. {
  2321. GetToken( false );
  2322. pRule->radius = verify_atof( token ) / 2.0f;
  2323. }
  2324. else if (stricmp( token, "radius" ) == 0)
  2325. {
  2326. GetToken( false );
  2327. pRule->radius = verify_atof( token );
  2328. }
  2329. else if (stricmp( token, "contact" ) == 0)
  2330. {
  2331. GetToken( false );
  2332. pRule->contact = verify_atoi( token );
  2333. }
  2334. else if (stricmp( token, "usesequence" ) == 0)
  2335. {
  2336. pRule->usesequence = true;
  2337. pRule->usesource = false;
  2338. }
  2339. else if (stricmp( token, "usesource" ) == 0)
  2340. {
  2341. pRule->usesequence = false;
  2342. pRule->usesource = true;
  2343. }
  2344. else if (stricmp( token, "fakeorigin" ) == 0)
  2345. {
  2346. GetToken( false );
  2347. pRule->pos.x = verify_atof( token );
  2348. GetToken( false );
  2349. pRule->pos.y = verify_atof( token );
  2350. GetToken( false );
  2351. pRule->pos.z = verify_atof( token );
  2352. pRule->bone = -1;
  2353. }
  2354. else if (stricmp( token, "fakerotate" ) == 0)
  2355. {
  2356. QAngle ang;
  2357. GetToken( false );
  2358. ang.x = verify_atof( token );
  2359. GetToken( false );
  2360. ang.y = verify_atof( token );
  2361. GetToken( false );
  2362. ang.z = verify_atof( token );
  2363. AngleQuaternion( ang, pRule->q );
  2364. pRule->bone = -1;
  2365. }
  2366. else if (stricmp( token, "bone" ) == 0)
  2367. {
  2368. strcpy( pRule->bonename, token );
  2369. }
  2370. else
  2371. {
  2372. UnGetToken();
  2373. return;
  2374. }
  2375. }
  2376. }
  2377. /*
  2378. =================
  2379. Cmd_Origin
  2380. =================
  2381. */
  2382. void Cmd_Origin (void)
  2383. {
  2384. GetToken (false);
  2385. g_defaultadjust.x = verify_atof (token);
  2386. GetToken (false);
  2387. g_defaultadjust.y = verify_atof (token);
  2388. GetToken (false);
  2389. g_defaultadjust.z = verify_atof (token);
  2390. if (TokenAvailable())
  2391. {
  2392. GetToken (false);
  2393. g_defaultrotation.z = DEG2RAD( verify_atof( token ) + 90);
  2394. }
  2395. }
  2396. //-----------------------------------------------------------------------------
  2397. // Purpose: Set the default root rotation so that the Y axis is up instead of the Z axis (for Maya)
  2398. //-----------------------------------------------------------------------------
  2399. void ProcessUpAxis( const RadianEuler &angles )
  2400. {
  2401. g_defaultrotation = angles;
  2402. }
  2403. //-----------------------------------------------------------------------------
  2404. // Purpose: Set the default root rotation so that the Y axis is up instead of the Z axis (for Maya)
  2405. //-----------------------------------------------------------------------------
  2406. void Cmd_UpAxis( void )
  2407. {
  2408. // We want to create a rotation that rotates from the art space
  2409. // (specified by the up direction) to a z up space
  2410. // Note: x, -x, -y are untested
  2411. RadianEuler angles( 0.0f, 0.0f, M_PI / 2.0f );
  2412. GetToken (false);
  2413. if (!Q_stricmp( token, "x" ))
  2414. {
  2415. // rotate 90 degrees around y to move x into z
  2416. angles.x = 0.0f;
  2417. angles.y = M_PI / 2.0f;
  2418. }
  2419. else if (!Q_stricmp( token, "-x" ))
  2420. {
  2421. // untested
  2422. angles.x = 0.0f;
  2423. angles.y = -M_PI / 2.0f;
  2424. }
  2425. else if (!Q_stricmp( token, "y" ))
  2426. {
  2427. // rotate 90 degrees around x to move y into z
  2428. angles.x = M_PI / 2.0f;
  2429. angles.y = 0.0f;
  2430. }
  2431. else if (!Q_stricmp( token, "-y" ))
  2432. {
  2433. // untested
  2434. angles.x = -M_PI / 2.0f;
  2435. angles.y = 0.0f;
  2436. }
  2437. else if (!Q_stricmp( token, "z" ))
  2438. {
  2439. // there's still a built in 90 degree Z rotation :(
  2440. angles.x = 0.0f;
  2441. angles.y = 0.0f;
  2442. }
  2443. else if (!Q_stricmp( token, "-z" ))
  2444. {
  2445. // there's still a built in 90 degree Z rotation :(
  2446. angles.x = 0.0f;
  2447. angles.y = 0.0f;
  2448. }
  2449. else
  2450. {
  2451. TokenError( "unknown $upaxis option: \"%s\"\n", token );
  2452. return;
  2453. }
  2454. ProcessUpAxis( angles );
  2455. }
  2456. /*
  2457. =================
  2458. =================
  2459. */
  2460. void Cmd_ScaleUp (void)
  2461. {
  2462. GetToken (false);
  2463. g_defaultscale = verify_atof (token);
  2464. g_currentscale = g_defaultscale;
  2465. }
  2466. //-----------------------------------------------------------------------------
  2467. // Purpose: Sets how what size chunks to cut the animations into
  2468. //-----------------------------------------------------------------------------
  2469. void Cmd_AnimBlockSize( void )
  2470. {
  2471. GetToken( false );
  2472. g_animblocksize = verify_atoi( token );
  2473. if (g_animblocksize < 1024)
  2474. {
  2475. g_animblocksize *= 1024;
  2476. }
  2477. while (TokenAvailable())
  2478. {
  2479. GetToken( false );
  2480. if (!Q_stricmp( token, "nostall" ))
  2481. {
  2482. g_bNoAnimblockStall = true;
  2483. }
  2484. else if (!Q_stricmp( token, "highres" ))
  2485. {
  2486. g_bAnimblockHighRes = true;
  2487. g_bAnimblockLowRes = false;
  2488. }
  2489. else if (!Q_stricmp( token, "lowres" ))
  2490. {
  2491. g_bAnimblockLowRes = true;
  2492. g_bAnimblockHighRes = false;
  2493. }
  2494. else if (!Q_stricmp( token, "numframes" ))
  2495. {
  2496. GetToken( false );
  2497. g_nMaxZeroFrames = clamp( atoi( token ), 1, 4 );
  2498. }
  2499. else if (!Q_stricmp( token, "cachehighres" ))
  2500. {
  2501. g_bZeroFramesHighres = true;
  2502. }
  2503. else if (!Q_stricmp( token, "posdelta" ))
  2504. {
  2505. GetToken( false );
  2506. g_flMinZeroFramePosDelta = atof( token );
  2507. }
  2508. else
  2509. {
  2510. MdlError("unknown option \"%s\" on $animblocksize command\n");
  2511. }
  2512. }
  2513. }
  2514. //-----------------------------------------------------------------------------
  2515. //
  2516. //-----------------------------------------------------------------------------
  2517. static void FlipFacing( s_source_t *pSrc )
  2518. {
  2519. unsigned short tmp;
  2520. int i, j;
  2521. for( i = 0; i < pSrc->nummeshes; i++ )
  2522. {
  2523. s_mesh_t *pMesh = &pSrc->mesh[i];
  2524. for( j = 0; j < pMesh->numfaces; j++ )
  2525. {
  2526. s_face_t &f = pSrc->face[pMesh->faceoffset + j];
  2527. tmp = f.b; f.b = f.c; f.c = tmp;
  2528. }
  2529. }
  2530. }
  2531. // Processes source comment line and extracts information about the data file
  2532. void ProcessSourceComment( s_source_t *psource, const char *pCommentString )
  2533. {
  2534. if ( const char *szSceneComment = StringAfterPrefix( pCommentString, "// SCENE=" ) )
  2535. {
  2536. char szScene[1024];
  2537. Q_strncpy( szScene, szSceneComment, ARRAYSIZE( szScene ) );
  2538. Q_FixSlashes( szScene );
  2539. ProcessOriginalContentFile( psource->filename, szScene );
  2540. }
  2541. }
  2542. // Processes original content file "szOriginalContentFile" that was used to generate
  2543. // data file "szDataFile"
  2544. void ProcessOriginalContentFile( const char *szDataFile, const char *szOriginalContentFile )
  2545. {
  2546. // Early out if no p4
  2547. if ( g_bNoP4 )
  2548. return;
  2549. const char *szContentDirRootEnd = strstr( szDataFile, "\\content\\" );
  2550. const char *szSceneName = strstr( szOriginalContentFile, "\\content\\" );
  2551. if ( szContentDirRootEnd && szSceneName )
  2552. {
  2553. char chScenePath[ MAX_PATH ] = {0};
  2554. Q_snprintf( chScenePath, sizeof( chScenePath ) - 1, "%.*s%s",
  2555. szContentDirRootEnd - szDataFile, szDataFile, szSceneName );
  2556. EnsureDependencyFileCheckedIn( chScenePath );
  2557. }
  2558. else if ( szContentDirRootEnd && !szSceneName )
  2559. {
  2560. // Assume relative path
  2561. char chScenePath[ MAX_PATH ] = {0};
  2562. Q_snprintf( chScenePath, sizeof( chScenePath ) - 1, "%.*s%s",
  2563. max( strrchr( szDataFile, '\\' ), strrchr( szDataFile, '/' ) ) + 1 - szDataFile,
  2564. szDataFile, szOriginalContentFile );
  2565. EnsureDependencyFileCheckedIn( chScenePath );
  2566. }
  2567. else
  2568. {
  2569. MdlWarning( "ProcessOriginalContentFile for '%s' cannot detect scene source file from '%s'!\n", szDataFile, szOriginalContentFile );
  2570. }
  2571. }
  2572. //-----------------------------------------------------------------------------
  2573. // Checks to see if the model source was already loaded
  2574. //-----------------------------------------------------------------------------
  2575. static s_source_t *FindCachedSource( const char* name, const char* xext )
  2576. {
  2577. int i;
  2578. if( xext[0] )
  2579. {
  2580. // we know what extension is necessary. . look for it.
  2581. Q_snprintf( g_szFilename, sizeof(g_szFilename), "%s%s.%s", cddir[numdirs], name, xext );
  2582. for (i = 0; i < g_numsources; i++)
  2583. {
  2584. if ( !Q_stricmp( g_szFilename, g_source[i]->filename ) )
  2585. return g_source[i];
  2586. }
  2587. }
  2588. else
  2589. {
  2590. // we don't know what extension to use, so look for all of 'em.
  2591. Q_snprintf( g_szFilename, sizeof(g_szFilename), "%s%s.vrm", cddir[numdirs], name );
  2592. for (i = 0; i < g_numsources; i++)
  2593. {
  2594. if ( !Q_stricmp( g_szFilename, g_source[i]->filename ) )
  2595. return g_source[i];
  2596. }
  2597. Q_snprintf (g_szFilename, sizeof(g_szFilename), "%s%s.dmx", cddir[numdirs], name );
  2598. for (i = 0; i < g_numsources; i++)
  2599. {
  2600. if ( !Q_stricmp( g_szFilename, g_source[i]->filename ) )
  2601. return g_source[i];
  2602. }
  2603. Q_snprintf (g_szFilename, sizeof(g_szFilename), "%s%s.smd", cddir[numdirs], name );
  2604. for (i = 0; i < g_numsources; i++)
  2605. {
  2606. if ( !Q_stricmp( g_szFilename, g_source[i]->filename ) )
  2607. return g_source[i];
  2608. }
  2609. Q_snprintf (g_szFilename, sizeof(g_szFilename), "%s%s.xml", cddir[numdirs], name );
  2610. for (i = 0; i < g_numsources; i++)
  2611. {
  2612. if ( !Q_stricmp( g_szFilename, g_source[i]->filename ) )
  2613. return g_source[i];
  2614. }
  2615. Q_snprintf (g_szFilename, sizeof(g_szFilename), "%s%s.obj", cddir[numdirs], name );
  2616. for (i = 0; i < g_numsources; i++)
  2617. {
  2618. if ( !Q_stricmp( g_szFilename, g_source[i]->filename ) )
  2619. return g_source[i];
  2620. }
  2621. /*
  2622. sprintf (g_szFilename, "%s%s.vta", cddir[numdirs], name );
  2623. for (i = 0; i < g_numsources; i++)
  2624. {
  2625. if (stricmp( g_szFilename, g_source[i]->filename ) == 0)
  2626. return g_source[i];
  2627. }
  2628. */
  2629. }
  2630. // Not found
  2631. return 0;
  2632. }
  2633. //-----------------------------------------------------------------------------
  2634. // Clamp meshes into N vertex sizes so as to not overrun
  2635. //-----------------------------------------------------------------------------
  2636. // TODO: It may be better to go ahead and create a new "source", since there's other limits besides just vertices per mesh, such as total verts per model.
  2637. class CClampedSource
  2638. {
  2639. public:
  2640. CClampedSource( ):m_nummeshes(0) {};
  2641. void Init( int numvertices )
  2642. {
  2643. m_nOrigMap.EnsureCount( numvertices );
  2644. for (int v = 0; v < numvertices; v++ )
  2645. {
  2646. m_nOrigMap[v] = -1;
  2647. }
  2648. for (int m = 0; m < MAXSTUDIOSKINS; m++)
  2649. {
  2650. m_mesh[m].numvertices = 0;
  2651. m_mesh[m].vertexoffset = 0;
  2652. m_mesh[m].numfaces = 0;
  2653. m_mesh[m].faceoffset = 0;
  2654. }
  2655. };
  2656. // per material mesh
  2657. int m_nummeshes;
  2658. int m_meshindex[MAXSTUDIOSKINS]; // mesh to skin index
  2659. s_mesh_t m_mesh[MAXSTUDIOSKINS];
  2660. // vertices defined in "local" space (not remapped to global bones)
  2661. CUtlVector< int > m_nOrigMap; // maps the original index to the new index
  2662. CUtlVector< s_vertexinfo_t > m_vertex;
  2663. CUtlVector< s_face_t > m_face;
  2664. CUtlVector< s_sourceanim_t > m_Animations;
  2665. int AddNewVert( s_source_t *pOrigSource, int nVert, int nSrcMesh, int nDstMesh, int nPreOffset = 0 );
  2666. void AddAnimations( const s_source_t *pOrigSource );
  2667. void DestroyAnimations( s_source_t *pNewSource );
  2668. void Copy( s_source_t *pOrigSource );
  2669. void CopyFlexKeys( const s_source_t *pOrigSource, s_source_t *pNewSource, int imodel );
  2670. };
  2671. int CClampedSource::AddNewVert( s_source_t *pOrigSource, int nVert, int nSrcMesh, int nDstMesh, int nPreOffset )
  2672. {
  2673. nVert += ( nPreOffset + pOrigSource->mesh[nSrcMesh].vertexoffset );
  2674. if (m_nOrigMap[nVert] == -1)
  2675. {
  2676. m_nOrigMap[nVert] = m_vertex.AddToTail( pOrigSource->vertex[nVert - nPreOffset] );
  2677. if (m_mesh[nDstMesh].numvertices == 0)
  2678. {
  2679. m_mesh[nDstMesh].vertexoffset = m_nOrigMap[nVert];
  2680. }
  2681. m_mesh[nDstMesh].numvertices++;
  2682. }
  2683. return m_nOrigMap[nVert] - m_mesh[nDstMesh].vertexoffset;
  2684. }
  2685. void CClampedSource::AddAnimations( const s_source_t *pOrigSource )
  2686. {
  2687. // invert the vertex mapping (maps the new index to the original index)
  2688. CUtlVector< int > nReverseMap;
  2689. int numvertices = m_vertex.Count();
  2690. nReverseMap.AddMultipleToTail( numvertices );
  2691. if ( numvertices > 0 )
  2692. {
  2693. memset( nReverseMap.Base(), 0xffffffff, numvertices*sizeof( int ) );
  2694. for ( int i = 0; i < pOrigSource->numvertices; i++ )
  2695. {
  2696. if ( m_nOrigMap[ i ] != -1 )
  2697. {
  2698. Assert( nReverseMap[ m_nOrigMap[ i ] ] == -1 );
  2699. nReverseMap[ m_nOrigMap[ i ] ] = i;
  2700. }
  2701. }
  2702. for ( int i = 0; i < numvertices; i++ )
  2703. {
  2704. Assert( nReverseMap[ i ] != -1 );
  2705. }
  2706. }
  2707. // copy animations
  2708. int nAnimations = pOrigSource->m_Animations.Count();
  2709. for ( int nAnim = 0; nAnim < nAnimations; nAnim++ )
  2710. {
  2711. const s_sourceanim_t &srcAnim = pOrigSource->m_Animations[ nAnim ];
  2712. if ( srcAnim.vanim_mapcount || srcAnim.vanim_map || srcAnim.vanim_flag )
  2713. {
  2714. Warning( "Cannot split SMD model with vertex animations... discarding animation\n" );
  2715. Assert( 0 );
  2716. continue;
  2717. }
  2718. s_sourceanim_t &dstAnim = m_Animations[ m_Animations.AddToTail() ];
  2719. memset( &dstAnim, 0, sizeof( dstAnim ) );
  2720. // bone anims can be copied as-is
  2721. memcpy( dstAnim.animationname, srcAnim.animationname, sizeof( dstAnim.animationname ) );
  2722. dstAnim.numframes = srcAnim.numframes;
  2723. dstAnim.startframe = srcAnim.startframe;
  2724. dstAnim.endframe = srcAnim.endframe;
  2725. dstAnim.rawanim.RemoveAll();
  2726. dstAnim.rawanim.AddMultipleToTail( srcAnim.rawanim.Count() );
  2727. for ( int i = 0; i < srcAnim.rawanim.Count(); i++ )
  2728. {
  2729. dstAnim.rawanim[ i ] = new s_bone_t[ pOrigSource->numbones ];
  2730. memcpy( dstAnim.rawanim.Element( i ), srcAnim.rawanim.Element( i ), pOrigSource->numbones*sizeof( s_bone_t ) );
  2731. }
  2732. // vertex animations need remapping
  2733. dstAnim.newStyleVertexAnimations = srcAnim.newStyleVertexAnimations;
  2734. if ( !srcAnim.newStyleVertexAnimations )
  2735. return;
  2736. for ( int i = 0; i < MAXSTUDIOANIMFRAMES; i++ )
  2737. {
  2738. // Count the number of verts which apply to this sub-model...
  2739. for ( int j = 0; j < srcAnim.numvanims[i]; j++ )
  2740. {
  2741. int nMappedVert = m_nOrigMap[ srcAnim.vanim[ i ][ j ].vertex ];
  2742. if ( nMappedVert != -1 )
  2743. dstAnim.numvanims[i]++;
  2744. }
  2745. // ...and just copy those verts:
  2746. if ( dstAnim.numvanims[i] )
  2747. {
  2748. dstAnim.vanim[i] = new s_vertanim_t[ dstAnim.numvanims[i] ];
  2749. int nvanim = 0;
  2750. for ( int j = 0; j < srcAnim.numvanims[i]; j++ )
  2751. {
  2752. int nMappedVert = m_nOrigMap[ srcAnim.vanim[ i ][ j ].vertex ];
  2753. if ( nMappedVert != -1 )
  2754. {
  2755. memcpy( &dstAnim.vanim[ i ][ nvanim ], &srcAnim.vanim[ i ][ j ], sizeof( s_vertanim_t ) );
  2756. dstAnim.vanim[ i ][ nvanim ].vertex = nMappedVert;
  2757. nvanim++;
  2758. }
  2759. }
  2760. }
  2761. }
  2762. }
  2763. }
  2764. void CClampedSource::Copy( s_source_t *pNewSource )
  2765. {
  2766. // copy over new meshes
  2767. pNewSource->numfaces = m_face.Count();
  2768. pNewSource->face = ( s_face_t * )calloc( pNewSource->numfaces, sizeof( s_face_t ) );
  2769. for (int i = 0; i < pNewSource->numfaces; i++ )
  2770. {
  2771. pNewSource->face[i] = m_face[i];
  2772. }
  2773. pNewSource->numvertices = m_vertex.Count();
  2774. pNewSource->vertex = ( s_vertexinfo_t * )calloc( pNewSource->numvertices, sizeof( s_vertexinfo_t ) );
  2775. for (int i = 0; i < pNewSource->numvertices; i++ )
  2776. {
  2777. pNewSource->vertex[i] = m_vertex[i];
  2778. }
  2779. pNewSource->nummeshes = m_nummeshes;
  2780. for (int i = 0; i < MAXSTUDIOSKINS-1; i++ )
  2781. {
  2782. pNewSource->mesh[i] = m_mesh[i];
  2783. pNewSource->meshindex[i] = m_meshindex[i];
  2784. }
  2785. // copy over new animations (just copy the structs, pointers and all)
  2786. int nAnimations = m_Animations.Count();
  2787. pNewSource->m_Animations.RemoveAll(); // NOTE: this leaks, but we just dont care
  2788. pNewSource->m_Animations.SetCount( nAnimations );
  2789. for ( int i = 0; i < nAnimations; i++ )
  2790. {
  2791. memcpy( &pNewSource->m_Animations[ i ], &m_Animations[ i ], sizeof( s_sourceanim_t ) );
  2792. // Clear the source structure so its embedded CUtlVectorAuto thinks it's empty upon destruction:
  2793. memset( &m_Animations[ i ], 0, sizeof( s_sourceanim_t ) );
  2794. }
  2795. m_Animations.RemoveAll();
  2796. }
  2797. void CClampedSource::CopyFlexKeys( const s_source_t *pOrigSource, s_source_t *pNewSource, int imodel )
  2798. {
  2799. // TODO: this produces many useless flex keys in HLMV, and it can fail if 'numSubmodels*numFlexKeys' exceeds the supported maximum
  2800. // (this would happen for sure if a character's face got cut up, for example), so:
  2801. // - in CClampedSource::AddAnimations, we can detect flex animations which do not apply to a submodel and cull them
  2802. // - we would need to build up a mapping table from pre-culled indices to post-culled indices (and vice versa), so that in here we can copy just those
  2803. // elements of m_FlexKeys/m_CombinationControls/m_CombinationRules/m_FlexControllerRemaps which were not culled (these arrays are all parallel)
  2804. // - the copied m_CombinationRules values would need to be remapped using the mapping table
  2805. // - if a flex key should be culled but it is referred to (via m_CombinationRules) by a non-culled flex key, then we can't cull it
  2806. // If characters are the only failure cases, a simpler alternative may be to just split models into flexed/unflexed parts (given only the faces are flexed)
  2807. if ( pOrigSource == pNewSource )
  2808. return;
  2809. // TODO: need to change g_defaultflexkey so it works with this (set a flag on the default flexkey (error if the user sets two defaults), duplicate the flag with that flexkey in here, update RemapVertexAnimations to use the flag)
  2810. pNewSource->m_FlexKeys.SetCount( pOrigSource->m_FlexKeys.Count() );
  2811. for ( int i = 0; i < pOrigSource->m_FlexKeys.Count(); i++ )
  2812. {
  2813. pNewSource->m_FlexKeys[ i ] = pOrigSource->m_FlexKeys[ i ];
  2814. pNewSource->m_FlexKeys[ i ].source = pNewSource;
  2815. }
  2816. pNewSource->m_CombinationControls.SetCount( pOrigSource->m_CombinationControls.Count() );
  2817. for ( int i = 0; i < pOrigSource->m_CombinationControls.Count(); i++ )
  2818. {
  2819. pNewSource->m_CombinationControls[ i ] = pOrigSource->m_CombinationControls[ i ];
  2820. }
  2821. pNewSource->m_CombinationRules.SetCount( pOrigSource->m_CombinationRules.Count() );
  2822. for ( int i = 0; i < pOrigSource->m_CombinationRules.Count(); i++ )
  2823. {
  2824. pNewSource->m_CombinationRules[ i ] = pOrigSource->m_CombinationRules[ i ];
  2825. }
  2826. pNewSource->m_FlexControllerRemaps.SetCount( pOrigSource->m_FlexControllerRemaps.Count() );
  2827. for ( int i = 0; i < pOrigSource->m_FlexControllerRemaps.Count(); i++ )
  2828. {
  2829. pNewSource->m_FlexControllerRemaps[ i ] = pOrigSource->m_FlexControllerRemaps[ i ];
  2830. }
  2831. // Emulate post-processing of flex data done by Cmd_Bodygroup, via PostProcessSource:
  2832. // Calling AddBodyFlexData will update:
  2833. // - g_flexkey, g_numflexkeys, g_flexcontroller, g_numflexcontrollers, g_FlexControllerRemap
  2834. // - pNewSource->( m_nKeyStartIndex, m_rawIndexToRemapSourceIndex, m_rawIndexToRemapLocalIndex, m_leftRemapIndexToGlobalFlexControllIndex, m_rightRemapIndexToGlobalFlexControllIndex )
  2835. // Calling AddBodyFlexRules will update:
  2836. // - pSource->m_FlexControllerRemaps
  2837. // - g_flexrule, g_numflexrules
  2838. // NOTE: we dont call AddBodyAttachments, since we're not duplicating those
  2839. AddBodyFlexData( pNewSource, imodel );
  2840. AddBodyFlexRules( pNewSource );
  2841. }
  2842. void ApplyOffsetToSrcVerts( s_source_t *pModel, matrix3x4_t matOffset )
  2843. {
  2844. if ( MatrixIsIdentity(matOffset) )
  2845. return;
  2846. for ( int v = 0; v < pModel->numvertices; v++ )
  2847. {
  2848. VectorTransform( pModel->vertex[v].position, matOffset, pModel->vertex[v].position );
  2849. VectorRotate( pModel->vertex[v].normal, matOffset, pModel->vertex[v].normal );
  2850. VectorRotate( pModel->vertex[v].tangentS.AsVector3D(), matOffset, pModel->vertex[v].tangentS.AsVector3D() );
  2851. }
  2852. }
  2853. void AddSrcToSrc( s_source_t *pOrigSource, s_source_t *pAppendSource, matrix3x4_t matOffset )
  2854. {
  2855. // steps are:
  2856. // only A exists
  2857. // make a new source C
  2858. // append A to C
  2859. // append B to C
  2860. // replace A with C
  2861. CClampedSource newSource;
  2862. newSource.Init( pOrigSource->numvertices + pAppendSource->numvertices );
  2863. bool bDone[MAXSTUDIOSKINS];
  2864. for (int m = 0; m < MAXSTUDIOSKINS; m++ )
  2865. {
  2866. newSource.m_meshindex[m] = 0;
  2867. bDone[m] = false;
  2868. }
  2869. for (int m = 0; m < MAXSTUDIOSKINS; m++ )
  2870. {
  2871. int nSrcMeshIndex = pOrigSource->meshindex[m];
  2872. s_mesh_t *pOrigMesh = &pOrigSource->mesh[nSrcMeshIndex];
  2873. if ( pOrigMesh->numvertices == 0 || bDone[nSrcMeshIndex] )
  2874. continue;
  2875. bDone[nSrcMeshIndex] = true;
  2876. // copy all origmesh faces into newsource
  2877. for ( int f = pOrigMesh->faceoffset; f < pOrigMesh->faceoffset + pOrigMesh->numfaces; f++ )
  2878. {
  2879. s_face_t face;
  2880. face.a = newSource.AddNewVert( pOrigSource, pOrigSource->face[f].a, nSrcMeshIndex, 0 );
  2881. face.b = newSource.AddNewVert( pOrigSource, pOrigSource->face[f].b, nSrcMeshIndex, 0 );
  2882. face.c = newSource.AddNewVert( pOrigSource, pOrigSource->face[f].c, nSrcMeshIndex, 0 );
  2883. if ( pOrigSource->face[f].d != 0 )
  2884. face.d = newSource.AddNewVert( pOrigSource, pOrigSource->face[f].d, nSrcMeshIndex, 0 );
  2885. else
  2886. face.d = 0;
  2887. //if ( newSource.m_mesh[0].numfaces == 0 )
  2888. //{
  2889. // newSource.m_mesh[0].faceoffset = newSource.m_face.Count();
  2890. //}
  2891. newSource.m_face.AddToTail( face );
  2892. newSource.m_mesh[0].numfaces++;
  2893. }
  2894. }
  2895. // just use src anim - we don't really care because it's for static props
  2896. newSource.AddAnimations( pOrigSource );
  2897. newSource.m_nummeshes = 1;//pOrigSource->nummeshes;// + pAppendSource->nummeshes;
  2898. // apply offset to appended vertices
  2899. ApplyOffsetToSrcVerts( pAppendSource, matOffset ); // no-op if matOffset is identity
  2900. // append the vertices to the new source
  2901. for (int m = 0; m < MAXSTUDIOSKINS; m++ )
  2902. {
  2903. bDone[m] = false;
  2904. }
  2905. for (int m = 0; m < pAppendSource->nummeshes; m++ )
  2906. {
  2907. int nCurMesh = pAppendSource->meshindex[m];
  2908. s_mesh_t *pAppendMesh = &pAppendSource->mesh[nCurMesh];
  2909. if ( bDone[nCurMesh] )
  2910. continue;
  2911. bDone[nCurMesh] = true;
  2912. //if ( newSource.m_mesh[nDestMesh].numvertices + 4 > g_maxVertexLimit )
  2913. // nDestMesh++;
  2914. if ( pAppendMesh && pAppendMesh->numvertices > 0 )
  2915. {
  2916. for ( int f = pAppendMesh->faceoffset; f < pAppendMesh->faceoffset + pAppendMesh->numfaces; f++ )
  2917. {
  2918. s_face_t face;
  2919. face.a = newSource.AddNewVert( pAppendSource, pAppendSource->face[f].a, nCurMesh, 0, pOrigSource->numvertices );
  2920. face.b = newSource.AddNewVert( pAppendSource, pAppendSource->face[f].b, nCurMesh, 0, pOrigSource->numvertices );
  2921. face.c = newSource.AddNewVert( pAppendSource, pAppendSource->face[f].c, nCurMesh, 0, pOrigSource->numvertices );
  2922. if ( pAppendSource->face[f].d != 0 )
  2923. face.d = newSource.AddNewVert( pAppendSource, pAppendSource->face[f].d, nCurMesh, 0, pOrigSource->numvertices );
  2924. else
  2925. face.d = 0;
  2926. newSource.m_face.AddToTail( face );
  2927. newSource.m_mesh[0].numfaces++;
  2928. }
  2929. }
  2930. }
  2931. free( pOrigSource->face );
  2932. free( pOrigSource->vertex );
  2933. newSource.Copy( pOrigSource );
  2934. }
  2935. void AddSrcToSrc( s_source_t *pOrigSource, s_source_t *pAppendSource )
  2936. {
  2937. matrix3x4_t matNoop; matNoop.SetToIdentity();
  2938. AddSrcToSrc( pOrigSource, pAppendSource, matNoop );
  2939. }
  2940. void Cmd_AppendSource( )
  2941. {
  2942. if ( !GetToken(false) )
  2943. return;
  2944. s_source_t *pOrigSource = g_model[ 0 ]->source;
  2945. s_source_t *pAppendSource = Load_Source( token, "", false, false, false /* don't use cached lookup, since this might be a dup of the starting src */ );
  2946. matrix3x4_t matTemp; matTemp.SetToIdentity();
  2947. matTemp.ScaleUpper3x3Matrix( g_currentscale );
  2948. if ( TokenAvailable() )
  2949. {
  2950. GetToken(false);
  2951. if ( !V_strncmp( token, "offset", 6 ) )
  2952. {
  2953. Vector vecOffsetPosition; vecOffsetPosition.Init();
  2954. QAngle angOffsetAngle; angOffsetAngle.Init();
  2955. float flScale = 1;
  2956. int nCount = sscanf( token, "offset pos[ %f %f %f ] angle[ %f %f %f ] scale[ %f ]",
  2957. &vecOffsetPosition.x, &vecOffsetPosition.y, &vecOffsetPosition.z,
  2958. &angOffsetAngle.x, &angOffsetAngle.y, &angOffsetAngle.z,
  2959. &flScale );
  2960. if ( nCount == 7 )
  2961. {
  2962. AngleMatrix( angOffsetAngle, vecOffsetPosition, matTemp );
  2963. matTemp.ScaleUpper3x3Matrix( flScale * (1.0f / g_currentscale) );
  2964. }
  2965. else
  2966. {
  2967. MdlError( "Malformed offset parameters to $appendsource." );
  2968. return;
  2969. }
  2970. }
  2971. else
  2972. {
  2973. UnGetToken();
  2974. }
  2975. }
  2976. AddSrcToSrc( pOrigSource, pAppendSource, matTemp );
  2977. }
  2978. void ClampMaxVerticesPerModel( s_source_t *pOrigSource )
  2979. {
  2980. // check for overage
  2981. if ( pOrigSource->numvertices < g_maxVertexLimit )
  2982. return;
  2983. MdlWarning( "model has too many verts, cutting into multiple models\n", pOrigSource->numvertices );
  2984. CUtlVector< CClampedSource > newSource;
  2985. int ns = newSource.AddToTail( );
  2986. newSource[ns].Init( pOrigSource->numvertices );
  2987. for (int m = 0; m < pOrigSource->nummeshes; m++ )
  2988. {
  2989. s_mesh_t *pOrigMesh = &pOrigSource->mesh[m];
  2990. for ( int f = pOrigMesh->faceoffset; f < pOrigMesh->faceoffset + pOrigMesh->numfaces; f++ )
  2991. {
  2992. // make sure all the total for all the meshes in the model don't go over limit
  2993. int nVertsInFace = ( pOrigSource->face[f].d == 0 ) ? 3 : 4;
  2994. if ( ( newSource[ns].m_vertex.Count() + nVertsInFace ) > g_maxVertexClamp )
  2995. {
  2996. // go to the next model
  2997. ns = newSource.AddToTail();
  2998. newSource[ns].Init( pOrigSource->numvertices );
  2999. }
  3000. // build face
  3001. s_face_t face;
  3002. face.a = newSource[ns].AddNewVert( pOrigSource, pOrigSource->face[f].a, m, m );
  3003. face.b = newSource[ns].AddNewVert( pOrigSource, pOrigSource->face[f].b, m, m );
  3004. face.c = newSource[ns].AddNewVert( pOrigSource, pOrigSource->face[f].c, m, m );
  3005. if ( pOrigSource->face[f].d != 0 )
  3006. face.d = newSource[ns].AddNewVert( pOrigSource, pOrigSource->face[f].d, m, m );
  3007. else
  3008. face.d = 0;
  3009. if (newSource[ns].m_mesh[m].numfaces == 0)
  3010. {
  3011. newSource[ns].m_mesh[m].faceoffset = newSource[ns].m_face.Count();
  3012. }
  3013. newSource[ns].m_face.AddToTail( face );
  3014. newSource[ns].m_mesh[m].numfaces++;
  3015. }
  3016. }
  3017. // Split animations into the new sub-models
  3018. for (int n = 0; n < newSource.Count(); n++)
  3019. {
  3020. newSource[n].AddAnimations( pOrigSource );
  3021. newSource[n].m_nummeshes = pOrigSource->nummeshes;
  3022. }
  3023. // copy over new meshes and animations back into initial source
  3024. free( pOrigSource->face );
  3025. free( pOrigSource->vertex );
  3026. newSource[0].Copy( pOrigSource );
  3027. for (int n = 1; n < newSource.Count(); n++)
  3028. {
  3029. // create a new internal "source"
  3030. s_source_t *pSource = (s_source_t *)calloc( 1, sizeof( s_source_t ) );
  3031. g_source[g_numsources++] = pSource;
  3032. // copy all the members, in order
  3033. memcpy( &(pSource->filename[0]), &(pOrigSource->filename[0]), sizeof( pSource->filename ) );
  3034. // copy over the faces/vertices/animations
  3035. newSource[n].Copy( pSource );
  3036. // copy settings
  3037. pSource->isActiveModel = true;
  3038. // copy skeleton
  3039. pSource->numbones = pOrigSource->numbones;
  3040. for (int i = 0; i < pSource->numbones; i++)
  3041. {
  3042. pSource->localBone[i] = pOrigSource->localBone[i];
  3043. pSource->boneToPose[i] = pOrigSource->boneToPose[i];
  3044. }
  3045. // The following members are set up later on in the process, so we don't need to copy them here:
  3046. // pSource->boneflags
  3047. // pSource->boneref
  3048. // pSource->boneLocalToGlobal
  3049. // pSource->boneGlobalToLocal
  3050. // pSource->m_GlobalVertices
  3051. // copy mesh data
  3052. for (int i = 0; i < pSource->nummeshes; i++)
  3053. {
  3054. pSource->texmap[i] = pOrigSource->texmap[i];
  3055. pSource->meshindex[i] = pOrigSource->meshindex[i];
  3056. }
  3057. // copy settings
  3058. pSource->adjust = pOrigSource->adjust;
  3059. pSource->scale = pOrigSource->scale;
  3060. pSource->rotation = pOrigSource->rotation;
  3061. pSource->bNoAutoDMXRules = pOrigSource->bNoAutoDMXRules;
  3062. // allocate a model
  3063. s_model_t *pModel = (s_model_t *)calloc( 1, sizeof( s_model_t ) );
  3064. pModel->source = pSource;
  3065. sprintf( pModel->name, "%s%d", "clamped", n );
  3066. int imodel = g_nummodels++;
  3067. g_model[imodel] = pModel;
  3068. // make it a new bodypart
  3069. g_bodypart[g_numbodyparts].nummodels = 1;
  3070. g_bodypart[g_numbodyparts].base = g_bodypart[g_numbodyparts-1].base * g_bodypart[g_numbodyparts-1].nummodels;
  3071. sprintf( g_bodypart[g_numbodyparts].name, "%s%d", "clamped", n );
  3072. g_bodypart[g_numbodyparts].pmodel[0] = pModel;
  3073. g_numbodyparts++;
  3074. // finally, copy flex keys
  3075. newSource[n].CopyFlexKeys( pOrigSource, pSource, imodel );
  3076. // NOTE: we leave attachments on the first sub-model, we don't want to duplicate those
  3077. }
  3078. }
  3079. //-----------------------------------------------------------------------------
  3080. // Purpose: insert a virtual bone between a child and parent (currently unsupported)
  3081. //-----------------------------------------------------------------------------
  3082. void Cmd_maxVerts( )
  3083. {
  3084. // first limit
  3085. GetToken( false );
  3086. g_maxVertexLimit = clamp( atoi( token ), 1024, MAXSTUDIOVERTS );
  3087. g_maxVertexClamp = MIN( g_maxVertexLimit, MAXSTUDIOVERTS / 2 );
  3088. if (TokenAvailable())
  3089. {
  3090. // actual target limit
  3091. GetToken( false );
  3092. g_maxVertexClamp = clamp( atoi( token ), 1024, MAXSTUDIOVERTS );
  3093. }
  3094. }
  3095. //-----------------------------------------------------------------------------
  3096. // Loads an animation/model source
  3097. //-----------------------------------------------------------------------------
  3098. s_source_t *Load_Source( const char *name, const char *ext, bool reverse, bool isActiveModel, bool bUseCache )
  3099. {
  3100. if ( g_numsources >= MAXSTUDIOSEQUENCES )
  3101. {
  3102. TokenError( "Load_Source( %s ) - overflowed g_numsources.", name );
  3103. }
  3104. Assert(name);
  3105. int namelen = Q_strlen(name) + 1;
  3106. char* pTempName = (char*)stackalloc( namelen );
  3107. char xext[32];
  3108. int result = false;
  3109. strcpy( pTempName, name );
  3110. Q_ExtractFileExtension( pTempName, xext, sizeof( xext ) );
  3111. if (xext[0] == '\0')
  3112. {
  3113. Q_strncpy( xext, ext, sizeof(xext) );
  3114. }
  3115. else
  3116. {
  3117. Q_StripExtension( pTempName, pTempName, namelen );
  3118. }
  3119. s_source_t* pSource = NULL;
  3120. if ( bUseCache )
  3121. {
  3122. pSource = FindCachedSource( pTempName, xext );
  3123. if ( pSource )
  3124. {
  3125. if (isActiveModel)
  3126. {
  3127. pSource->isActiveModel = true;
  3128. }
  3129. return pSource;
  3130. }
  3131. }
  3132. // NOTE: The load proc can potentially add other sources (for the MPP format)
  3133. // So we have to deal with setting everything up in this source prior to
  3134. // calling the load func, and we cannot reference g_source anywhere below
  3135. pSource = (s_source_t *)calloc( 1, sizeof( s_source_t ) );
  3136. g_source[g_numsources++] = pSource;
  3137. if ( isActiveModel )
  3138. {
  3139. pSource->isActiveModel = true;
  3140. }
  3141. // copy over default settings of when the model was loaded
  3142. // (since there's no actual animation for some of the systems)
  3143. VectorCopy( g_defaultadjust, pSource->adjust );
  3144. pSource->scale = 1.0f;
  3145. pSource->rotation = g_defaultrotation;
  3146. const char * load_extensions[] = { "fbx", "vrm", "dmx", "mpp", "smd", "sma", "phys", "vta", "obj", "xml", "fbx" };
  3147. int( *load_procs[] )( s_source_t * ) = { Load_FBX, Load_VRM, Load_DMX, Load_DMX, Load_SMD, Load_SMD, Load_SMD, Load_VTA, Load_OBJ, Load_DMX, Load_FBX };
  3148. COMPILE_TIME_ASSERT( ARRAYSIZE(load_extensions) == ARRAYSIZE(load_procs) );
  3149. for ( int kk = ( g_bPreferFbx ? 0 : 1 ); kk < ARRAYSIZE( load_extensions ); ++ kk )
  3150. {
  3151. if ( ( !result && xext[0] == '\0' ) || Q_stricmp( xext, load_extensions[kk] ) == 0)
  3152. {
  3153. Q_snprintf( g_szFilename, sizeof(g_szFilename), "%s%s.%s", cddir[numdirs], pTempName, load_extensions[kk] );
  3154. Q_strncpy( pSource->filename, g_szFilename, sizeof(pSource->filename) );
  3155. result = (load_procs[kk])( pSource );
  3156. // Don't check in the mpp file
  3157. if ( result && Q_stricmp( load_extensions[kk], "mpp" ) )
  3158. {
  3159. EnsureDependencyFileCheckedIn( pSource->filename );
  3160. }
  3161. }
  3162. }
  3163. if ( !g_bCreateMakefile && !result )
  3164. {
  3165. if (xext[0] == '\0')
  3166. {
  3167. TokenError( "could not load file '%s%s'\n", cddir[numdirs], pTempName );
  3168. }
  3169. else
  3170. {
  3171. TokenError( "could not load file '%s%s.%s'\n", cddir[numdirs], pTempName, xext );
  3172. }
  3173. }
  3174. if ( pSource->numbones == 0 )
  3175. {
  3176. TokenError( "missing all bones in file '%s'\n", pSource->filename );
  3177. }
  3178. if( reverse )
  3179. {
  3180. FlipFacing( pSource );
  3181. }
  3182. return pSource;
  3183. }
  3184. s_sequence_t *LookupSequence( const char *name )
  3185. {
  3186. int i;
  3187. for ( i = 0; i < g_sequence.Count(); ++i )
  3188. {
  3189. if ( !Q_stricmp( g_sequence[i].name, name ) )
  3190. return &g_sequence[i];
  3191. }
  3192. return NULL;
  3193. }
  3194. s_animation_t *LookupAnimation( const char *name, int nFallbackRecursionDepth )
  3195. {
  3196. int i;
  3197. for ( i = 0; i < g_numani; i++)
  3198. {
  3199. if ( !Q_stricmp( g_panimation[i]->name, name ) )
  3200. return g_panimation[i];
  3201. }
  3202. s_sequence_t *pseq = LookupSequence( name );
  3203. // Used to just return pseq->panim[0][0] but pseq->panim is
  3204. // a CUtlVectorAuto which expands the array on access as necessary
  3205. // but seems to fill it with random data on expansion, so prevent
  3206. // that here because we're doing a lookup to see if something
  3207. // already exists
  3208. if ( pseq && pseq->panim.Count() > 0 )
  3209. {
  3210. CUtlVectorAuto< s_animation_t * > &animList = pseq->panim[0];
  3211. if ( animList.Count() > 0 )
  3212. return animList[0];
  3213. }
  3214. // check optional fallbacks or reserved name syntax
  3215. if ( nFallbackRecursionDepth == 0 && !V_strcmp( name, "this" ) )
  3216. {
  3217. return LookupAnimation( g_szInCurrentSeqName, 1 );
  3218. }
  3219. return NULL;
  3220. }
  3221. s_animation_t *LookupAnimation( const char *name )
  3222. {
  3223. return LookupAnimation( name, 0 );
  3224. }
  3225. //-----------------------------------------------------------------------------
  3226. // Purpose: parse order dependant s_animcmd_t token for $animations
  3227. //-----------------------------------------------------------------------------
  3228. int ParseCmdlistToken( int &numcmds, s_animcmd_t *cmds )
  3229. {
  3230. if (numcmds >= MAXSTUDIOCMDS)
  3231. {
  3232. return false;
  3233. }
  3234. s_animcmd_t *pcmd = &cmds[numcmds];
  3235. if (stricmp("fixuploop", token ) == 0)
  3236. {
  3237. pcmd->cmd = CMD_FIXUP;
  3238. GetToken( false );
  3239. pcmd->u.fixuploop.start = verify_atoi( token );
  3240. GetToken( false );
  3241. pcmd->u.fixuploop.end = verify_atoi( token );
  3242. }
  3243. else if (strnicmp("weightlist", token, 6 ) == 0)
  3244. {
  3245. GetToken( false );
  3246. int i;
  3247. for ( i = 1; i < g_numweightlist; i++)
  3248. {
  3249. if (stricmp( g_weightlist[i].name, token ) == 0)
  3250. {
  3251. break;
  3252. }
  3253. }
  3254. if (i == g_numweightlist)
  3255. {
  3256. TokenError( "unknown weightlist '%s\'\n", token );
  3257. }
  3258. pcmd->cmd = CMD_WEIGHTS;
  3259. pcmd->u.weightlist.index = i;
  3260. }
  3261. else if (stricmp("subtract", token ) == 0)
  3262. {
  3263. pcmd->cmd = CMD_SUBTRACT;
  3264. GetToken( false );
  3265. s_animation_t *extanim = LookupAnimation( token );
  3266. if (extanim == NULL)
  3267. {
  3268. TokenError( "unknown subtract animation '%s\'\n", token );
  3269. }
  3270. pcmd->u.subtract.ref = extanim;
  3271. GetToken( false );
  3272. pcmd->u.subtract.frame = verify_atoi( token );
  3273. pcmd->u.subtract.flags |= STUDIO_POST;
  3274. }
  3275. else if (stricmp("presubtract", token ) == 0) // FIXME: rename this to something better
  3276. {
  3277. pcmd->cmd = CMD_SUBTRACT;
  3278. GetToken( false );
  3279. s_animation_t *extanim = LookupAnimation( token );
  3280. if (extanim == NULL)
  3281. {
  3282. TokenError( "unknown presubtract animation '%s\'\n", token );
  3283. }
  3284. pcmd->u.subtract.ref = extanim;
  3285. GetToken( false );
  3286. pcmd->u.subtract.frame = verify_atoi( token );
  3287. }
  3288. else if (stricmp( "alignto", token ) == 0)
  3289. {
  3290. pcmd->cmd = CMD_AO;
  3291. pcmd->u.ao.pBonename = NULL;
  3292. GetToken( false );
  3293. s_animation_t *extanim = LookupAnimation( token );
  3294. if (extanim == NULL)
  3295. {
  3296. TokenError( "unknown alignto animation '%s\'\n", token );
  3297. }
  3298. pcmd->u.ao.ref = extanim;
  3299. pcmd->u.ao.motiontype = STUDIO_X | STUDIO_Y;
  3300. pcmd->u.ao.srcframe = 0;
  3301. pcmd->u.ao.destframe = 0;
  3302. }
  3303. else if (stricmp( "align", token ) == 0)
  3304. {
  3305. pcmd->cmd = CMD_AO;
  3306. pcmd->u.ao.pBonename = NULL;
  3307. GetToken( false );
  3308. s_animation_t *extanim = LookupAnimation( token );
  3309. if (extanim == NULL)
  3310. {
  3311. TokenError( "unknown align animation '%s\'\n", token );
  3312. }
  3313. pcmd->u.ao.ref = extanim;
  3314. // motion type to match
  3315. pcmd->u.ao.motiontype = 0;
  3316. GetToken( false );
  3317. int ctrl;
  3318. while ((ctrl = lookupControl( token )) != -1)
  3319. {
  3320. pcmd->u.ao.motiontype |= ctrl;
  3321. GetToken( false );
  3322. }
  3323. if (pcmd->u.ao.motiontype == 0)
  3324. {
  3325. TokenError( "missing controls on align\n" );
  3326. }
  3327. // frame of reference animation to match
  3328. pcmd->u.ao.srcframe = verify_atoi( token );
  3329. // against what frame of the current animation
  3330. GetToken( false );
  3331. pcmd->u.ao.destframe = verify_atoi( token );
  3332. }
  3333. else if (stricmp( "alignboneto", token ) == 0)
  3334. {
  3335. pcmd->cmd = CMD_AO;
  3336. GetToken( false );
  3337. pcmd->u.ao.pBonename = strdup( token );
  3338. GetToken( false );
  3339. s_animation_t *extanim = LookupAnimation( token );
  3340. if (extanim == NULL)
  3341. {
  3342. TokenError( "unknown alignboneto animation '%s\'\n", token );
  3343. }
  3344. pcmd->u.ao.ref = extanim;
  3345. pcmd->u.ao.motiontype = STUDIO_X | STUDIO_Y;
  3346. pcmd->u.ao.srcframe = 0;
  3347. pcmd->u.ao.destframe = 0;
  3348. }
  3349. else if (stricmp( "alignbone", token ) == 0)
  3350. {
  3351. pcmd->cmd = CMD_AO;
  3352. GetToken( false );
  3353. pcmd->u.ao.pBonename = strdup( token );
  3354. GetToken( false );
  3355. s_animation_t *extanim = LookupAnimation( token );
  3356. if (extanim == NULL)
  3357. {
  3358. TokenError( "unknown alignboneto animation '%s\'\n", token );
  3359. }
  3360. pcmd->u.ao.ref = extanim;
  3361. // motion type to match
  3362. pcmd->u.ao.motiontype = 0;
  3363. GetToken( false );
  3364. int ctrl;
  3365. while ((ctrl = lookupControl( token )) != -1)
  3366. {
  3367. pcmd->u.ao.motiontype |= ctrl;
  3368. GetToken( false );
  3369. }
  3370. if (pcmd->u.ao.motiontype == 0)
  3371. {
  3372. TokenError( "missing controls on align\n" );
  3373. }
  3374. // frame of reference animation to match
  3375. pcmd->u.ao.srcframe = verify_atoi( token );
  3376. // against what frame of the current animation
  3377. GetToken( false );
  3378. pcmd->u.ao.destframe = verify_atoi( token );
  3379. }
  3380. else if (stricmp( "match", token ) == 0)
  3381. {
  3382. pcmd->cmd = CMD_MATCH;
  3383. GetToken( false );
  3384. s_animation_t *extanim = LookupAnimation( token );
  3385. if (extanim == NULL)
  3386. {
  3387. TokenError( "unknown match animation '%s\'\n", token );
  3388. }
  3389. pcmd->u.match.ref = extanim;
  3390. }
  3391. else if (stricmp( "matchblend", token ) == 0)
  3392. {
  3393. pcmd->cmd = CMD_MATCHBLEND;
  3394. GetToken( false );
  3395. s_animation_t *extanim = LookupAnimation( token );
  3396. if (extanim == NULL)
  3397. {
  3398. MdlError( "unknown match animation '%s\'\n", token );
  3399. }
  3400. pcmd->u.match.ref = extanim;
  3401. // frame of reference animation to match
  3402. GetToken( false );
  3403. pcmd->u.match.srcframe = verify_atoi( token );
  3404. // against what frame of the current animation
  3405. GetToken( false );
  3406. pcmd->u.match.destframe = verify_atoi( token );
  3407. // backup and starting match in here
  3408. GetToken( false );
  3409. pcmd->u.match.destpre = verify_atoi( token );
  3410. // continue blending match till here
  3411. GetToken( false );
  3412. pcmd->u.match.destpost = verify_atoi( token );
  3413. }
  3414. else if (stricmp( "worldspaceblend", token ) == 0)
  3415. {
  3416. pcmd->cmd = CMD_WORLDSPACEBLEND;
  3417. GetToken( false );
  3418. s_animation_t *extanim = LookupAnimation( token );
  3419. if (extanim == NULL)
  3420. {
  3421. TokenError( "unknown worldspaceblend animation '%s\'\n", token );
  3422. }
  3423. pcmd->u.world.ref = extanim;
  3424. pcmd->u.world.startframe = 0;
  3425. pcmd->u.world.loops = false;
  3426. }
  3427. else if (stricmp( "worldspaceblendloop", token ) == 0)
  3428. {
  3429. pcmd->cmd = CMD_WORLDSPACEBLEND;
  3430. GetToken( false );
  3431. s_animation_t *extanim = LookupAnimation( token );
  3432. if (extanim == NULL)
  3433. {
  3434. TokenError( "unknown worldspaceblend animation '%s\'\n", token );
  3435. }
  3436. pcmd->u.world.ref = extanim;
  3437. GetToken( false );
  3438. pcmd->u.world.startframe = atoi( token );
  3439. pcmd->u.world.loops = true;
  3440. }
  3441. else if (stricmp( "rotateto", token ) == 0)
  3442. {
  3443. pcmd->cmd = CMD_ANGLE;
  3444. GetToken( false );
  3445. pcmd->u.angle.angle = verify_atof( token );
  3446. }
  3447. else if (stricmp( "ikrule", token ) == 0)
  3448. {
  3449. pcmd->cmd = CMD_IKRULE;
  3450. pcmd->u.ikrule.pRule = (s_ikrule_t *)calloc( 1, sizeof( s_ikrule_t ) );
  3451. Option_IKRule( pcmd->u.ikrule.pRule );
  3452. }
  3453. else if (stricmp( "ikfixup", token ) == 0)
  3454. {
  3455. pcmd->cmd = CMD_IKFIXUP;
  3456. pcmd->u.ikfixup.pRule = (s_ikrule_t *)calloc( 1, sizeof( s_ikrule_t ) );
  3457. Option_IKRule( pcmd->u.ikrule.pRule );
  3458. }
  3459. else if (stricmp( "walkframe", token ) == 0)
  3460. {
  3461. pcmd->cmd = CMD_MOTION;
  3462. // frame
  3463. GetToken( false );
  3464. pcmd->u.motion.iEndFrame = verify_atoi( token );
  3465. // motion type to match
  3466. pcmd->u.motion.motiontype = 0;
  3467. while (TokenAvailable())
  3468. {
  3469. GetToken( false );
  3470. int ctrl = lookupControl( token );
  3471. if (ctrl != -1)
  3472. {
  3473. pcmd->u.motion.motiontype |= ctrl;
  3474. }
  3475. else
  3476. {
  3477. UnGetToken();
  3478. break;
  3479. }
  3480. }
  3481. /*
  3482. GetToken( false ); // X
  3483. pcmd->u.motion.x = verify_atof( token );
  3484. GetToken( false ); // Y
  3485. pcmd->u.motion.y = verify_atof( token );
  3486. GetToken( false ); // A
  3487. pcmd->u.motion.zr = verify_atof( token );
  3488. */
  3489. }
  3490. else if (stricmp( "walkalignto", token ) == 0)
  3491. {
  3492. pcmd->cmd = CMD_REFMOTION;
  3493. GetToken( false );
  3494. pcmd->u.motion.iEndFrame = verify_atoi( token );
  3495. pcmd->u.motion.iSrcFrame = pcmd->u.motion.iEndFrame;
  3496. GetToken( false ); // reference animation
  3497. s_animation_t *extanim = LookupAnimation( token );
  3498. if (extanim == NULL)
  3499. {
  3500. TokenError( "unknown alignto animation '%s\'\n", token );
  3501. }
  3502. pcmd->u.motion.pRefAnim = extanim;
  3503. pcmd->u.motion.iRefFrame = 0;
  3504. // motion type to match
  3505. pcmd->u.motion.motiontype = 0;
  3506. while (TokenAvailable())
  3507. {
  3508. GetToken( false );
  3509. int ctrl = lookupControl( token );
  3510. if (ctrl != -1)
  3511. {
  3512. pcmd->u.motion.motiontype |= ctrl;
  3513. }
  3514. else
  3515. {
  3516. UnGetToken();
  3517. break;
  3518. }
  3519. }
  3520. /*
  3521. GetToken( false ); // X
  3522. pcmd->u.motion.x = verify_atof( token );
  3523. GetToken( false ); // Y
  3524. pcmd->u.motion.y = verify_atof( token );
  3525. GetToken( false ); // A
  3526. pcmd->u.motion.zr = verify_atof( token );
  3527. */
  3528. }
  3529. else if (stricmp( "walkalign", token ) == 0)
  3530. {
  3531. pcmd->cmd = CMD_REFMOTION;
  3532. // end frame to apply motion over
  3533. GetToken( false );
  3534. pcmd->u.motion.iEndFrame = verify_atoi( token );
  3535. // reference animation
  3536. GetToken( false );
  3537. s_animation_t *extanim = LookupAnimation( token );
  3538. if (extanim == NULL)
  3539. {
  3540. TokenError( "unknown alignto animation '%s\'\n", token );
  3541. }
  3542. pcmd->u.motion.pRefAnim = extanim;
  3543. // motion type to match
  3544. pcmd->u.motion.motiontype = 0;
  3545. while (TokenAvailable())
  3546. {
  3547. GetToken( false );
  3548. int ctrl = lookupControl( token );
  3549. if (ctrl != -1)
  3550. {
  3551. pcmd->u.motion.motiontype |= ctrl;
  3552. }
  3553. else
  3554. {
  3555. break;
  3556. }
  3557. }
  3558. if (pcmd->u.motion.motiontype == 0)
  3559. {
  3560. TokenError( "missing controls on walkalign\n" );
  3561. }
  3562. // frame of reference animation to match
  3563. pcmd->u.motion.iRefFrame = verify_atoi( token );
  3564. // against what frame of the current animation
  3565. GetToken( false );
  3566. pcmd->u.motion.iSrcFrame = verify_atoi( token );
  3567. }
  3568. else if (stricmp("derivative", token ) == 0)
  3569. {
  3570. pcmd->cmd = CMD_DERIVATIVE;
  3571. // get scale
  3572. GetToken( false );
  3573. pcmd->u.derivative.scale = verify_atof( token );
  3574. }
  3575. else if (stricmp("noanimation", token ) == 0)
  3576. {
  3577. pcmd->cmd = CMD_NOANIMATION;
  3578. }
  3579. else if (stricmp("noanim_keepduration", token ) == 0)
  3580. {
  3581. pcmd->cmd = CMD_NOANIM_KEEPDURATION;
  3582. }
  3583. else if (stricmp("lineardelta", token ) == 0)
  3584. {
  3585. pcmd->cmd = CMD_LINEARDELTA;
  3586. pcmd->u.linear.flags |= STUDIO_AL_POST;
  3587. }
  3588. else if (stricmp("splinedelta", token ) == 0)
  3589. {
  3590. pcmd->cmd = CMD_LINEARDELTA;
  3591. pcmd->u.linear.flags |= STUDIO_AL_POST;
  3592. pcmd->u.linear.flags |= STUDIO_AL_SPLINE;
  3593. }
  3594. else if (stricmp("compress", token ) == 0)
  3595. {
  3596. pcmd->cmd = CMD_COMPRESS;
  3597. // get frames to skip
  3598. GetToken( false );
  3599. pcmd->u.compress.frames = verify_atoi( token );
  3600. }
  3601. else if (stricmp("numframes", token ) == 0)
  3602. {
  3603. pcmd->cmd = CMD_NUMFRAMES;
  3604. // get frames to force
  3605. GetToken( false );
  3606. pcmd->u.compress.frames = verify_atoi( token );
  3607. }
  3608. else if (stricmp("counterrotate", token ) == 0)
  3609. {
  3610. pcmd->cmd = CMD_COUNTERROTATE;
  3611. // get bone name
  3612. GetToken( false );
  3613. pcmd->u.counterrotate.pBonename = strdup( token );
  3614. }
  3615. else if (stricmp("counterrotateto", token ) == 0)
  3616. {
  3617. pcmd->cmd = CMD_COUNTERROTATE;
  3618. pcmd->u.counterrotate.bHasTarget = true;
  3619. // get pitch
  3620. GetToken( false );
  3621. pcmd->u.counterrotate.targetAngle[0] = verify_atof( token );
  3622. // get yaw
  3623. GetToken( false );
  3624. pcmd->u.counterrotate.targetAngle[1] = verify_atof( token );
  3625. // get roll
  3626. GetToken( false );
  3627. pcmd->u.counterrotate.targetAngle[2] = verify_atof( token );
  3628. // get bone name
  3629. GetToken( false );
  3630. pcmd->u.counterrotate.pBonename = strdup( token );
  3631. }
  3632. else if (stricmp("localhierarchy", token ) == 0)
  3633. {
  3634. pcmd->cmd = CMD_LOCALHIERARCHY;
  3635. // get bone name
  3636. GetToken( false );
  3637. pcmd->u.localhierarchy.pBonename = strdup( token );
  3638. // get parent name
  3639. GetToken( false );
  3640. pcmd->u.localhierarchy.pParentname = strdup( token );
  3641. pcmd->u.localhierarchy.start = -1;
  3642. pcmd->u.localhierarchy.peak = -1;
  3643. pcmd->u.localhierarchy.tail = -1;
  3644. pcmd->u.localhierarchy.end = -1;
  3645. if (TokenAvailable())
  3646. {
  3647. GetToken( false );
  3648. if (stricmp( token, "range" ) == 0)
  3649. {
  3650. //
  3651. GetToken( false );
  3652. pcmd->u.localhierarchy.start = verify_atof_with_null( token );
  3653. //
  3654. GetToken( false );
  3655. pcmd->u.localhierarchy.peak = verify_atof_with_null( token );
  3656. //
  3657. GetToken( false );
  3658. pcmd->u.localhierarchy.tail = verify_atof_with_null( token );
  3659. //
  3660. GetToken( false );
  3661. pcmd->u.localhierarchy.end = verify_atof_with_null( token );
  3662. }
  3663. else
  3664. {
  3665. UnGetToken();
  3666. }
  3667. }
  3668. }
  3669. else if (stricmp("forceboneposrot", token ) == 0)
  3670. {
  3671. pcmd->cmd = CMD_FORCEBONEPOSROT;
  3672. // get bone name
  3673. GetToken( false );
  3674. pcmd->u.forceboneposrot.pBonename = strdup( token );
  3675. pcmd->u.forceboneposrot.bDoPos = false;
  3676. pcmd->u.forceboneposrot.bDoRot = false;
  3677. pcmd->u.forceboneposrot.pos[0] = 0;
  3678. pcmd->u.forceboneposrot.pos[1] = 0;
  3679. pcmd->u.forceboneposrot.pos[2] = 0;
  3680. pcmd->u.forceboneposrot.rot[0] = 0;
  3681. pcmd->u.forceboneposrot.rot[1] = 0;
  3682. pcmd->u.forceboneposrot.rot[2] = 0;
  3683. if (TokenAvailable())
  3684. {
  3685. GetToken( false );
  3686. if (stricmp( token, "pos" ) == 0)
  3687. {
  3688. pcmd->u.forceboneposrot.bDoPos = true;
  3689. GetToken( false );
  3690. pcmd->u.forceboneposrot.pos[0] = verify_atof_with_null( token );
  3691. GetToken( false );
  3692. pcmd->u.forceboneposrot.pos[1] = verify_atof_with_null( token );
  3693. GetToken( false );
  3694. pcmd->u.forceboneposrot.pos[2] = verify_atof_with_null( token );
  3695. }
  3696. else
  3697. {
  3698. UnGetToken();
  3699. }
  3700. if ( TokenAvailable() )
  3701. {
  3702. GetToken( false );
  3703. if (stricmp( token, "rot" ) == 0)
  3704. {
  3705. pcmd->u.forceboneposrot.bDoRot = true;
  3706. GetToken( false );
  3707. pcmd->u.forceboneposrot.rot[0] = verify_atof_with_null( token );
  3708. GetToken( false );
  3709. pcmd->u.forceboneposrot.rot[1] = verify_atof_with_null( token );
  3710. GetToken( false );
  3711. pcmd->u.forceboneposrot.rot[2] = verify_atof_with_null( token );
  3712. pcmd->u.forceboneposrot.bRotIsLocal = false;
  3713. if ( TokenAvailable() )
  3714. {
  3715. GetToken( false );
  3716. if (stricmp( token, "local" ) == 0)
  3717. {
  3718. pcmd->u.forceboneposrot.bRotIsLocal = true;
  3719. }
  3720. else
  3721. {
  3722. UnGetToken();
  3723. }
  3724. }
  3725. }
  3726. else
  3727. {
  3728. UnGetToken();
  3729. }
  3730. }
  3731. }
  3732. }
  3733. else if (stricmp("bonedriver", token ) == 0)
  3734. {
  3735. pcmd->cmd = CMD_BONEDRIVER;
  3736. pcmd->u.bonedriver.iAxis = 0;
  3737. pcmd->u.bonedriver.value = 1.0f;
  3738. pcmd->u.bonedriver.all = true;
  3739. // get bone name
  3740. GetToken( false );
  3741. pcmd->u.bonedriver.pBonename = strdup( token );
  3742. if ( TokenAvailable() )
  3743. {
  3744. GetToken( false );
  3745. if (stricmp( token, "axis" ) == 0)
  3746. {
  3747. GetToken( false );
  3748. if (stricmp( token, "x" ) == 0)
  3749. {
  3750. pcmd->u.bonedriver.iAxis = 0;
  3751. }
  3752. else if (stricmp( token, "y" ) == 0)
  3753. {
  3754. pcmd->u.bonedriver.iAxis = 1;
  3755. }
  3756. else if (stricmp( token, "z" ) == 0)
  3757. {
  3758. pcmd->u.bonedriver.iAxis = 2;
  3759. }
  3760. else
  3761. {
  3762. TokenError( "Unknown bonedriver axis.\n" );
  3763. }
  3764. }
  3765. else
  3766. {
  3767. UnGetToken();
  3768. }
  3769. }
  3770. if ( TokenAvailable() )
  3771. {
  3772. GetToken( false );
  3773. if (stricmp( token, "value" ) == 0)
  3774. {
  3775. GetToken( false );
  3776. pcmd->u.bonedriver.value = verify_atof_with_null( token );
  3777. }
  3778. else
  3779. {
  3780. UnGetToken();
  3781. }
  3782. }
  3783. if ( TokenAvailable() )
  3784. {
  3785. GetToken( false );
  3786. if (stricmp( token, "range" ) == 0)
  3787. {
  3788. pcmd->u.bonedriver.all = false;
  3789. GetToken( false );
  3790. pcmd->u.bonedriver.start = verify_atoi( token );
  3791. GetToken( false );
  3792. pcmd->u.bonedriver.peak = verify_atoi( token );
  3793. GetToken( false );
  3794. pcmd->u.bonedriver.tail = verify_atoi( token );
  3795. GetToken( false );
  3796. pcmd->u.bonedriver.end = verify_atoi( token );
  3797. }
  3798. else
  3799. {
  3800. UnGetToken();
  3801. }
  3802. }
  3803. }
  3804. else if (stricmp("reverse", token ) == 0)
  3805. {
  3806. pcmd->cmd = CMD_REVERSE;
  3807. }
  3808. else if (stricmp("appendanim", token ) == 0)
  3809. {
  3810. pcmd->cmd = CMD_APPENDANIM;
  3811. GetToken( false ); // reference animation
  3812. s_animation_t *extanim = LookupAnimation( token );
  3813. if (extanim == NULL)
  3814. {
  3815. TokenError( "unknown appendanim '%s\'\n", token );
  3816. }
  3817. pcmd->u.appendanim.ref = extanim;
  3818. }
  3819. else
  3820. {
  3821. return false;
  3822. }
  3823. numcmds++;
  3824. return true;
  3825. }
  3826. //-----------------------------------------------------------------------------
  3827. // Purpose: parse order independant s_animation_t token for $animations
  3828. //-----------------------------------------------------------------------------
  3829. bool ParseAnimationToken( s_animation_t *panim )
  3830. {
  3831. if ( !Q_stricmp( "if", token ) )
  3832. {
  3833. // fixme: add expression evaluation
  3834. GetToken( false );
  3835. if (atoi( token ) == 0 && stricmp( token, "true" ) != 0)
  3836. {
  3837. GetToken(true);
  3838. if (token[0] == '{')
  3839. {
  3840. int depth = 1;
  3841. while (TokenAvailable() && depth > 0)
  3842. {
  3843. GetToken( true );
  3844. if (stricmp("{", token ) == 0)
  3845. {
  3846. depth++;
  3847. }
  3848. else if (stricmp("}", token ) == 0)
  3849. {
  3850. depth--;
  3851. }
  3852. }
  3853. }
  3854. }
  3855. return true;
  3856. }
  3857. if ( !Q_stricmp( "fps", token ) )
  3858. {
  3859. GetToken( false );
  3860. panim->fps = verify_atof( token );
  3861. if ( panim->fps <= 0.0f )
  3862. {
  3863. TokenError( "ParseAnimationToken: fps (%f from '%s') <= 0.0\n", panim->fps, token );
  3864. }
  3865. return true;
  3866. }
  3867. if ( !Q_stricmp( "origin", token ) )
  3868. {
  3869. GetToken (false);
  3870. panim->adjust.x = verify_atof (token);
  3871. GetToken (false);
  3872. panim->adjust.y = verify_atof (token);
  3873. GetToken (false);
  3874. panim->adjust.z = verify_atof (token);
  3875. return true;
  3876. }
  3877. if ( !Q_stricmp( "rotate", token ) )
  3878. {
  3879. GetToken( false );
  3880. // FIXME: broken for Maya
  3881. panim->rotation.z = DEG2RAD( verify_atof( token ) + 90 );
  3882. return true;
  3883. }
  3884. if ( !Q_stricmp( "angles", token ) )
  3885. {
  3886. GetToken( false );
  3887. panim->rotation.x = DEG2RAD( verify_atof( token ) );
  3888. GetToken( false );
  3889. panim->rotation.y = DEG2RAD( verify_atof( token ) );
  3890. GetToken( false );
  3891. panim->rotation.z = DEG2RAD( verify_atof( token ) + 90.0f);
  3892. return true;
  3893. }
  3894. if ( !Q_stricmp( "scale", token ) )
  3895. {
  3896. GetToken( false );
  3897. panim->scale = verify_atof( token );
  3898. return true;
  3899. }
  3900. if ( !Q_strnicmp( "loop", token, 4 ) )
  3901. {
  3902. panim->flags |= STUDIO_LOOPING;
  3903. return true;
  3904. }
  3905. if ( !Q_stricmp( "noforceloop", token ) )
  3906. {
  3907. panim->flags |= STUDIO_NOFORCELOOP;
  3908. return true;
  3909. }
  3910. if ( !Q_strcmp( "startloop", token ) )
  3911. {
  3912. GetToken( false );
  3913. panim->looprestart = verify_atoi( token );
  3914. if ( panim->looprestartpercent != 0 )
  3915. {
  3916. MdlError( "Can't specify startloop for animation %s, percentstartloop already specified.", panim->name );
  3917. }
  3918. panim->flags |= STUDIO_LOOPING;
  3919. return true;
  3920. }
  3921. if ( !Q_strcmp( "percentstartloop", token ) )
  3922. {
  3923. GetToken( false );
  3924. panim->looprestartpercent = verify_atof( token );
  3925. if ( panim->looprestart != 0 )
  3926. {
  3927. MdlError( "Can't specify percentstartloop for animation %s, looprestart already specified.", panim->name );
  3928. }
  3929. panim->flags |= STUDIO_LOOPING;
  3930. return true;
  3931. }
  3932. if ( !Q_stricmp( "fudgeloop", token ) )
  3933. {
  3934. panim->fudgeloop = true;
  3935. panim->flags |= STUDIO_LOOPING;
  3936. return true;
  3937. }
  3938. if ( !Q_strnicmp( "snap", token, 4 ) )
  3939. {
  3940. panim->flags |= STUDIO_SNAP;
  3941. return true;
  3942. }
  3943. if ( !Q_strnicmp( "frame", token, 5 ) || !Q_strnicmp( "framestart", token, 10 ) )
  3944. {
  3945. // framestart assumes the animation's end frame is ok to use no matter what it is. This is better than finding 'frame 9 10000' in qc script
  3946. bool bUseDefaultEndFrame = ( !Q_strnicmp( "framestart", token, 10 ) );
  3947. GetToken( false );
  3948. panim->startframe = verify_atoi( token );
  3949. if ( !bUseDefaultEndFrame )
  3950. {
  3951. GetToken( false );
  3952. panim->endframe = verify_atoi( token );
  3953. }
  3954. // NOTE: This always affects the first source anim read in
  3955. s_sourceanim_t *pSourceAnim = FindSourceAnim( panim->source, panim->animationname );
  3956. if ( pSourceAnim )
  3957. {
  3958. if ( panim->startframe < pSourceAnim->startframe )
  3959. {
  3960. panim->startframe = pSourceAnim->startframe;
  3961. }
  3962. if ( panim->endframe > pSourceAnim->endframe || bUseDefaultEndFrame )
  3963. {
  3964. panim->endframe = pSourceAnim->endframe;
  3965. }
  3966. }
  3967. if ( !g_bCreateMakefile && panim->endframe < panim->startframe )
  3968. {
  3969. TokenError( "end frame before start frame in %s", panim->name );
  3970. }
  3971. panim->numframes = panim->endframe - panim->startframe + 1;
  3972. return true;
  3973. }
  3974. if ( !Q_stricmp( "blockname", token ) )
  3975. {
  3976. GetToken( false );
  3977. s_sourceanim_t *pSourceAnim = FindSourceAnim( panim->source, token );
  3978. // NOTE: This always affects the first source anim read in
  3979. if ( pSourceAnim )
  3980. {
  3981. panim->startframe = pSourceAnim->startframe;
  3982. panim->endframe = pSourceAnim->endframe;
  3983. if ( !g_bCreateMakefile && panim->endframe < panim->startframe )
  3984. {
  3985. TokenError( "end frame before start frame in %s", panim->name );
  3986. }
  3987. panim->numframes = panim->endframe - panim->startframe + 1;
  3988. Q_strncpy( panim->animationname, token, sizeof(panim->animationname) );
  3989. }
  3990. else
  3991. {
  3992. MdlError( "Requested unknown animation block name %s\n", token );
  3993. }
  3994. return true;
  3995. }
  3996. if ( !Q_stricmp( "post", token ) )
  3997. {
  3998. panim->flags |= STUDIO_POST;
  3999. return true;
  4000. }
  4001. if ( !Q_stricmp( "noautoik", token ) )
  4002. {
  4003. panim->noAutoIK = true;
  4004. return true;
  4005. }
  4006. if ( !Q_stricmp( "autoik", token ) )
  4007. {
  4008. panim->noAutoIK = false;
  4009. return true;
  4010. }
  4011. if ( ParseCmdlistToken( panim->numcmds, panim->cmds ) )
  4012. return true;
  4013. if ( !Q_stricmp( "cmdlist", token ) )
  4014. {
  4015. GetToken( false ); // A
  4016. int i;
  4017. for ( i = 0; i < g_numcmdlists; i++)
  4018. {
  4019. if (stricmp( g_cmdlist[i].name, token) == 0)
  4020. {
  4021. break;
  4022. }
  4023. }
  4024. if (i == g_numcmdlists)
  4025. TokenError( "unknown cmdlist %s\n", token );
  4026. for (int j = 0; j < g_cmdlist[i].numcmds; j++)
  4027. {
  4028. if (panim->numcmds >= MAXSTUDIOCMDS)
  4029. {
  4030. TokenError("Too many cmds in %s\n", panim->name );
  4031. }
  4032. panim->cmds[panim->numcmds++] = g_cmdlist[i].cmds[j];
  4033. }
  4034. return true;
  4035. }
  4036. if ( !Q_stricmp( "motionrollback", token ) )
  4037. {
  4038. GetToken( false );
  4039. panim->motionrollback = atof( token );
  4040. return true;
  4041. }
  4042. if ( !Q_stricmp( "noanimblock", token ) )
  4043. {
  4044. panim->disableAnimblocks = true;
  4045. return true;
  4046. }
  4047. if ( !Q_stricmp( "noanimblockstall", token ) )
  4048. {
  4049. panim->isFirstSectionLocal = true;
  4050. return true;
  4051. }
  4052. if ( !Q_stricmp( "nostallframes", token ) )
  4053. {
  4054. GetToken( false );
  4055. panim->numNostallFrames = atof( token );
  4056. return true;
  4057. }
  4058. if ( lookupControl( token ) != -1 )
  4059. {
  4060. panim->motiontype |= lookupControl( token );
  4061. return true;
  4062. }
  4063. return false;
  4064. }
  4065. //-----------------------------------------------------------------------------
  4066. // Purpose: create named order dependant s_animcmd_t blocks, used as replicated token list for $animations
  4067. //-----------------------------------------------------------------------------
  4068. void Cmd_Cmdlist( )
  4069. {
  4070. int depth = 0;
  4071. // name
  4072. GetToken(false);
  4073. strcpyn( g_cmdlist[g_numcmdlists].name, token );
  4074. while (1)
  4075. {
  4076. if (depth > 0)
  4077. {
  4078. if(!GetToken(true))
  4079. {
  4080. break;
  4081. }
  4082. }
  4083. else
  4084. {
  4085. if (!TokenAvailable())
  4086. {
  4087. break;
  4088. }
  4089. else
  4090. {
  4091. GetToken (false);
  4092. }
  4093. }
  4094. if (endofscript)
  4095. {
  4096. if (depth != 0)
  4097. {
  4098. TokenError("missing }\n" );
  4099. }
  4100. return;
  4101. }
  4102. if (stricmp("{", token ) == 0)
  4103. {
  4104. depth++;
  4105. }
  4106. else if (stricmp("}", token ) == 0)
  4107. {
  4108. depth--;
  4109. }
  4110. else if (ParseCmdlistToken( g_cmdlist[g_numcmdlists].numcmds, g_cmdlist[g_numcmdlists].cmds ))
  4111. {
  4112. }
  4113. else
  4114. {
  4115. TokenError( "unknown command: %s\n", token );
  4116. }
  4117. if (depth < 0)
  4118. {
  4119. TokenError("missing {\n");
  4120. }
  4121. };
  4122. g_numcmdlists++;
  4123. }
  4124. int ParseAnimation( s_animation_t *panim, bool isAppend );
  4125. int ParseEmpty( void );
  4126. //-----------------------------------------------------------------------------
  4127. // Purpose: allocate an entry for $animation
  4128. //-----------------------------------------------------------------------------
  4129. void Cmd_Animation( )
  4130. {
  4131. // name
  4132. GetToken(false);
  4133. s_animation_t *panim = LookupAnimation( token );
  4134. if (panim != NULL)
  4135. {
  4136. if (!panim->isOverride)
  4137. {
  4138. TokenError( "Duplicate animation name \"%s\"\n", token );
  4139. }
  4140. else
  4141. {
  4142. panim->doesOverride = true;
  4143. ParseEmpty();
  4144. return;
  4145. }
  4146. }
  4147. // allocate animation entry
  4148. g_panimation[g_numani] = (s_animation_t *)calloc( 1, sizeof( s_animation_t ) );
  4149. g_panimation[g_numani]->index = g_numani;
  4150. panim = g_panimation[g_numani];
  4151. strcpyn( panim->name, token );
  4152. g_numani++;
  4153. // filename
  4154. GetToken(false);
  4155. strcpyn( panim->filename, token );
  4156. panim->source = Load_Source( panim->filename, "" );
  4157. if ( panim->source->m_Animations.Count() )
  4158. {
  4159. s_sourceanim_t *pSourceAnim = &panim->source->m_Animations[0];
  4160. panim->startframe = pSourceAnim->startframe;
  4161. panim->endframe = pSourceAnim->endframe;
  4162. Q_strncpy( panim->animationname, pSourceAnim->animationname, sizeof(panim->animationname) );
  4163. }
  4164. else
  4165. {
  4166. panim->startframe = 0;
  4167. panim->endframe = 0;
  4168. Q_strncpy( panim->animationname, "", sizeof(panim->animationname) );
  4169. }
  4170. VectorCopy( g_defaultadjust, panim->adjust );
  4171. panim->rotation = g_defaultrotation;
  4172. panim->scale = 1.0f;
  4173. panim->fps = 30.0;
  4174. panim->motionrollback = g_flDefaultMotionRollback;
  4175. ParseAnimation( panim, false );
  4176. panim->numframes = panim->endframe - panim->startframe + 1;
  4177. //CheckAutoShareAnimationGroup( panim->name );
  4178. }
  4179. //-----------------------------------------------------------------------------
  4180. // Purpose: wrapper for parsing $animation tokens
  4181. //-----------------------------------------------------------------------------
  4182. int ParseAnimation( s_animation_t *panim, bool isAppend )
  4183. {
  4184. int depth = 0;
  4185. while (1)
  4186. {
  4187. if (depth > 0)
  4188. {
  4189. if(!GetToken(true))
  4190. {
  4191. break;
  4192. }
  4193. }
  4194. else
  4195. {
  4196. if (!TokenAvailable())
  4197. {
  4198. break;
  4199. }
  4200. else
  4201. {
  4202. GetToken (false);
  4203. }
  4204. }
  4205. if (endofscript)
  4206. {
  4207. if (depth != 0)
  4208. {
  4209. TokenError("missing }\n" );
  4210. }
  4211. return 1;
  4212. }
  4213. if (stricmp("{", token ) == 0)
  4214. {
  4215. depth++;
  4216. }
  4217. else if (stricmp("}", token ) == 0)
  4218. {
  4219. depth--;
  4220. }
  4221. else if (ParseAnimationToken( panim ))
  4222. {
  4223. }
  4224. else
  4225. {
  4226. TokenError( "Unknown animation option\'%s\'\n", token );
  4227. }
  4228. if (depth < 0)
  4229. {
  4230. TokenError("missing {\n");
  4231. }
  4232. };
  4233. return 0;
  4234. }
  4235. //-----------------------------------------------------------------------------
  4236. // Purpose: create a virtual $animation command from a $sequence reference
  4237. //-----------------------------------------------------------------------------
  4238. s_animation_t *ProcessImpliedAnimation( s_sequence_t *psequence, const char *filename )
  4239. {
  4240. // allocate animation entry
  4241. g_panimation[g_numani] = (s_animation_t *)calloc( 1, sizeof( s_animation_t ) );
  4242. g_panimation[g_numani]->index = g_numani;
  4243. s_animation_t *panim = g_panimation[g_numani];
  4244. g_numani++;
  4245. panim->isImplied = true;
  4246. panim->startframe = 0;
  4247. panim->endframe = MAXSTUDIOANIMFRAMES - 1;
  4248. strcpy( panim->name, "@" );
  4249. strcat( panim->name, psequence->name );
  4250. strcpyn( panim->filename, filename );
  4251. VectorCopy( g_defaultadjust, panim->adjust );
  4252. panim->scale = 1.0f;
  4253. panim->rotation = g_defaultrotation;
  4254. panim->fps = 30;
  4255. panim->motionrollback = g_flDefaultMotionRollback;
  4256. //panim->source = Load_Source( panim->filename, "smd" );
  4257. panim->source = Load_Source( panim->filename, "" );
  4258. if ( panim->source->m_Animations.Count() )
  4259. {
  4260. s_sourceanim_t *pSourceAnim = &panim->source->m_Animations[0];
  4261. Q_strncpy( panim->animationname, panim->source->m_Animations[0].animationname, sizeof(panim->animationname) );
  4262. if ( panim->startframe < pSourceAnim->startframe )
  4263. {
  4264. panim->startframe = pSourceAnim->startframe;
  4265. }
  4266. if ( panim->endframe > pSourceAnim->endframe )
  4267. {
  4268. panim->endframe = pSourceAnim->endframe;
  4269. }
  4270. }
  4271. else
  4272. {
  4273. Q_strncpy( panim->animationname, "", sizeof( panim->animationname ) );
  4274. }
  4275. if ( !g_bCreateMakefile && panim->endframe < panim->startframe )
  4276. {
  4277. TokenError( "end frame before start frame in %s", panim->name );
  4278. }
  4279. panim->numframes = panim->endframe - panim->startframe + 1;
  4280. //CheckAutoShareAnimationGroup( panim->name );
  4281. return panim;
  4282. }
  4283. //-----------------------------------------------------------------------------
  4284. // Purpose: copy globally reavent $animation options from one $animation to another
  4285. //-----------------------------------------------------------------------------
  4286. void CopyAnimationSettings( s_animation_t *pdest, s_animation_t *psrc )
  4287. {
  4288. pdest->fps = psrc->fps;
  4289. VectorCopy( psrc->adjust, pdest->adjust );
  4290. pdest->scale = psrc->scale;
  4291. pdest->rotation = psrc->rotation;
  4292. pdest->motiontype = psrc->motiontype;
  4293. //Adrian - Hey! Revisit me later.
  4294. /*if (pdest->startframe < psrc->startframe)
  4295. pdest->startframe = psrc->startframe;
  4296. if (pdest->endframe > psrc->endframe)
  4297. pdest->endframe = psrc->endframe;
  4298. if (pdest->endframe < pdest->startframe)
  4299. TokenError( "fixedup end frame before start frame in %s", pdest->name );
  4300. pdest->numframes = pdest->endframe - pdest->startframe + 1;*/
  4301. for (int i = 0; i < psrc->numcmds; i++)
  4302. {
  4303. if (pdest->numcmds >= MAXSTUDIOCMDS)
  4304. {
  4305. TokenError("Too many cmds in %s\n", pdest->name );
  4306. }
  4307. pdest->cmds[pdest->numcmds++] = psrc->cmds[i];
  4308. }
  4309. }
  4310. int ParseSequence( s_sequence_t *pseq, bool isAppend );
  4311. //-----------------------------------------------------------------------------
  4312. // Purpose: allocate an entry for $sequence
  4313. //-----------------------------------------------------------------------------
  4314. s_sequence_t *ProcessCmdSequence( const char *pSequenceName )
  4315. {
  4316. s_animation_t *panim = LookupAnimation( pSequenceName );
  4317. // allocate sequence
  4318. if ( panim != NULL )
  4319. {
  4320. if ( !panim->isOverride )
  4321. {
  4322. TokenError( "Duplicate sequence name \"%s\"\n", pSequenceName );
  4323. }
  4324. else
  4325. {
  4326. panim->doesOverride = true;
  4327. return NULL;
  4328. }
  4329. }
  4330. if ( g_sequence.Count() >= MAXSTUDIOSEQUENCES )
  4331. {
  4332. TokenError("Too many sequences (%d max)\n", MAXSTUDIOSEQUENCES );
  4333. }
  4334. s_sequence_t *pseq = &g_sequence[ g_sequence.AddToTail() ];
  4335. memset( pseq, 0, sizeof( s_sequence_t ) );
  4336. // initialize sequence
  4337. Q_strncpy( pseq->name, pSequenceName, sizeof(pseq->name) );
  4338. pseq->actweight = 0;
  4339. pseq->activityname[0] = '\0';
  4340. pseq->activity = -1; // -1 is the default for 'no activity'
  4341. pseq->paramindex[0] = -1;
  4342. pseq->paramindex[1] = -1;
  4343. pseq->groupsize[0] = 0;
  4344. pseq->groupsize[1] = 0;
  4345. pseq->fadeintime = g_flDefaultFadeInTime;
  4346. pseq->fadeouttime = g_flDefaultFadeOutTime;
  4347. return pseq;
  4348. }
  4349. //-----------------------------------------------------------------------------
  4350. // Process the sequence command
  4351. //-----------------------------------------------------------------------------
  4352. void Cmd_Sequence( )
  4353. {
  4354. if ( !GetToken(false) )
  4355. return;
  4356. if ( g_bLCaseAllSequences )
  4357. strlwr(token);
  4358. // Find existing sequences
  4359. const char *pSequenceName = token;
  4360. s_animation_t *panim = LookupAnimation( pSequenceName );
  4361. if ( panim != NULL && panim->isOverride )
  4362. {
  4363. ParseEmpty( );
  4364. }
  4365. s_sequence_t *pseq = ProcessCmdSequence( pSequenceName );
  4366. if ( pseq )
  4367. {
  4368. ParseSequence( pseq, false );
  4369. }
  4370. }
  4371. //-----------------------------------------------------------------------------
  4372. // Performs processing on a sequence
  4373. //-----------------------------------------------------------------------------
  4374. void ProcessSequence( s_sequence_t *pseq, int numblends, s_animation_t **animations, bool isAppend )
  4375. {
  4376. if (isAppend)
  4377. return;
  4378. if ( numblends == 0 )
  4379. {
  4380. TokenError("no animations found\n");
  4381. }
  4382. if ( pseq->groupsize[0] == 0 )
  4383. {
  4384. if (numblends < 4)
  4385. {
  4386. pseq->groupsize[0] = numblends;
  4387. pseq->groupsize[1] = 1;
  4388. }
  4389. else
  4390. {
  4391. int i = sqrt( (float) numblends );
  4392. if (i * i == numblends)
  4393. {
  4394. pseq->groupsize[0] = i;
  4395. pseq->groupsize[1] = i;
  4396. }
  4397. else
  4398. {
  4399. TokenError( "non-square (%d) number of blends without \"blendwidth\" set\n", numblends );
  4400. }
  4401. }
  4402. }
  4403. else
  4404. {
  4405. pseq->groupsize[1] = numblends / pseq->groupsize[0];
  4406. if (pseq->groupsize[0] * pseq->groupsize[1] != numblends)
  4407. {
  4408. TokenError( "missing animation blends. Expected %d, found %d\n",
  4409. pseq->groupsize[0] * pseq->groupsize[1], numblends );
  4410. }
  4411. }
  4412. for (int i = 0; i < numblends; i++)
  4413. {
  4414. int j = i % pseq->groupsize[0];
  4415. int k = i / pseq->groupsize[0];
  4416. pseq->panim[j][k] = animations[i];
  4417. if (i > 0 && animations[i]->isImplied)
  4418. {
  4419. CopyAnimationSettings( animations[i], animations[0] );
  4420. }
  4421. animations[i]->isImplied = false; // don't copy any more commands
  4422. pseq->flags |= animations[i]->flags;
  4423. }
  4424. pseq->numblends = numblends;
  4425. }
  4426. //-----------------------------------------------------------------------------
  4427. // Purpose: parse options unique to $sequence
  4428. //-----------------------------------------------------------------------------
  4429. int ParseSequence( s_sequence_t *pseq, bool isAppend )
  4430. {
  4431. g_szInCurrentSeqName = pseq->name;
  4432. int depth = 0;
  4433. s_animation_t *animations[64];
  4434. int i, j, n;
  4435. int numblends = 0;
  4436. if (isAppend)
  4437. {
  4438. animations[0] = pseq->panim[0][0];
  4439. }
  4440. while (1)
  4441. {
  4442. if (depth > 0)
  4443. {
  4444. if(!GetToken(true))
  4445. {
  4446. break;
  4447. }
  4448. }
  4449. else
  4450. {
  4451. if (!TokenAvailable())
  4452. {
  4453. break;
  4454. }
  4455. else
  4456. {
  4457. GetToken (false);
  4458. }
  4459. }
  4460. if (endofscript)
  4461. {
  4462. if (depth != 0)
  4463. {
  4464. TokenError("missing }\n" );
  4465. }
  4466. return 1;
  4467. }
  4468. if (stricmp("{", token ) == 0)
  4469. {
  4470. depth++;
  4471. }
  4472. else if (stricmp("}", token ) == 0)
  4473. {
  4474. depth--;
  4475. }
  4476. /*
  4477. else if (stricmp("deform", token ) == 0)
  4478. {
  4479. Option_Deform( pseq );
  4480. }
  4481. */
  4482. else if (stricmp("animtag", token ) == 0)
  4483. {
  4484. depth -= Option_AnimTag( pseq );
  4485. }
  4486. else if (stricmp("event", token ) == 0)
  4487. {
  4488. depth -= Option_Event( pseq );
  4489. }
  4490. else if (stricmp("activity", token ) == 0)
  4491. {
  4492. Option_Activity( pseq );
  4493. }
  4494. else if ( (stricmp("activitymodifier", token ) == 0) || (stricmp("actmod", token ) == 0) )
  4495. {
  4496. Option_ActivityModifier( pseq );
  4497. }
  4498. else if (strnicmp( token, "ACT_", 4 ) == 0)
  4499. {
  4500. UnGetToken( );
  4501. Option_Activity( pseq );
  4502. }
  4503. else if (stricmp("snap", token ) == 0)
  4504. {
  4505. pseq->flags |= STUDIO_SNAP;
  4506. }
  4507. else if (stricmp("blendwidth", token ) == 0)
  4508. {
  4509. GetToken( false );
  4510. pseq->groupsize[0] = verify_atoi( token );
  4511. }
  4512. else if (stricmp("blend", token ) == 0)
  4513. {
  4514. i = 0;
  4515. if (pseq->paramindex[0] != -1)
  4516. {
  4517. i = 1;
  4518. }
  4519. GetToken( false );
  4520. j = LookupPoseParameter( token );
  4521. pseq->paramindex[i] = j;
  4522. pseq->paramattachment[i] = -1;
  4523. GetToken( false );
  4524. pseq->paramstart[i] = verify_atof( token );
  4525. GetToken( false );
  4526. pseq->paramend[i] = verify_atof( token );
  4527. g_pose[j].min = min( g_pose[j].min, pseq->paramstart[i] );
  4528. g_pose[j].min = min( g_pose[j].min, pseq->paramend[i] );
  4529. g_pose[j].max = max( g_pose[j].max, pseq->paramstart[i] );
  4530. g_pose[j].max = max( g_pose[j].max, pseq->paramend[i] );
  4531. }
  4532. else if (stricmp("calcblend", token ) == 0)
  4533. {
  4534. i = 0;
  4535. if (pseq->paramindex[0] != -1)
  4536. {
  4537. i = 1;
  4538. }
  4539. GetToken( false );
  4540. j = LookupPoseParameter( token );
  4541. pseq->paramindex[i] = j;
  4542. GetToken( false );
  4543. pseq->paramattachment[i] = LookupAttachment( token );
  4544. if (pseq->paramattachment[i] == -1)
  4545. {
  4546. TokenError( "Unknown calcblend attachment \"%s\"\n", token );
  4547. }
  4548. GetToken( false );
  4549. pseq->paramcontrol[i] = lookupControl( token );
  4550. }
  4551. else if (stricmp("blendref", token ) == 0)
  4552. {
  4553. GetToken( false );
  4554. pseq->paramanim = LookupAnimation( token );
  4555. if (pseq->paramanim == NULL)
  4556. {
  4557. TokenError( "Unknown blendref animation \"%s\"\n", token );
  4558. }
  4559. }
  4560. else if (stricmp("blendcomp", token ) == 0)
  4561. {
  4562. GetToken( false );
  4563. pseq->paramcompanim = LookupAnimation( token );
  4564. if (pseq->paramcompanim == NULL)
  4565. {
  4566. TokenError( "Unknown blendcomp animation \"%s\"\n", token );
  4567. }
  4568. }
  4569. else if (stricmp("blendcenter", token ) == 0)
  4570. {
  4571. GetToken( false );
  4572. pseq->paramcenter = LookupAnimation( token );
  4573. if (pseq->paramcenter == NULL)
  4574. {
  4575. TokenError( "Unknown blendcenter animation \"%s\"\n", token );
  4576. }
  4577. }
  4578. else if (stricmp("node", token ) == 0)
  4579. {
  4580. GetToken( false );
  4581. pseq->entrynode = pseq->exitnode = LookupXNode( token );
  4582. }
  4583. else if (stricmp("transition", token ) == 0)
  4584. {
  4585. GetToken( false );
  4586. pseq->entrynode = LookupXNode( token );
  4587. GetToken( false );
  4588. pseq->exitnode = LookupXNode( token );
  4589. }
  4590. else if (stricmp("rtransition", token ) == 0)
  4591. {
  4592. GetToken( false );
  4593. pseq->entrynode = LookupXNode( token );
  4594. GetToken( false );
  4595. pseq->exitnode = LookupXNode( token );
  4596. pseq->nodeflags |= 1;
  4597. }
  4598. else if (stricmp("exitphase", token ) == 0)
  4599. {
  4600. GetToken( false );
  4601. pseq->exitphase = verify_atof( token );
  4602. }
  4603. else if (stricmp("delta", token) == 0)
  4604. {
  4605. pseq->flags |= STUDIO_DELTA;
  4606. pseq->flags |= STUDIO_POST;
  4607. }
  4608. else if (stricmp("worldspace", token) == 0)
  4609. {
  4610. pseq->flags |= STUDIO_WORLD;
  4611. pseq->flags |= STUDIO_POST;
  4612. }
  4613. else if (stricmp("worldrelative", token) == 0)
  4614. {
  4615. pseq->flags |= STUDIO_WORLD_AND_RELATIVE;
  4616. pseq->flags |= STUDIO_POST;
  4617. }
  4618. else if (stricmp("rootdriver", token) == 0)
  4619. {
  4620. pseq->flags |= STUDIO_ROOTXFORM;
  4621. // get bone name
  4622. GetToken( false );
  4623. strcpyn( pseq->rootDriverBoneName, token );
  4624. }
  4625. else if (stricmp("post", token) == 0) // remove
  4626. {
  4627. pseq->flags |= STUDIO_POST;
  4628. }
  4629. else if (stricmp("predelta", token) == 0)
  4630. {
  4631. pseq->flags |= STUDIO_DELTA;
  4632. }
  4633. else if (stricmp("autoplay", token) == 0)
  4634. {
  4635. pseq->flags |= STUDIO_AUTOPLAY;
  4636. }
  4637. else if (stricmp( "fadein", token ) == 0)
  4638. {
  4639. GetToken( false );
  4640. pseq->fadeintime = verify_atof( token );
  4641. }
  4642. else if (stricmp( "fadeout", token ) == 0)
  4643. {
  4644. GetToken( false );
  4645. pseq->fadeouttime = verify_atof( token );
  4646. }
  4647. else if (stricmp( "realtime", token ) == 0)
  4648. {
  4649. pseq->flags |= STUDIO_REALTIME;
  4650. }
  4651. else if (stricmp( "posecycle", token ) == 0)
  4652. {
  4653. pseq->flags |= STUDIO_CYCLEPOSE;
  4654. GetToken( false );
  4655. pseq->cycleposeindex = LookupPoseParameter( token );
  4656. }
  4657. else if (stricmp( "hidden", token ) == 0)
  4658. {
  4659. pseq->flags |= STUDIO_HIDDEN;
  4660. }
  4661. else if (stricmp( "addlayer", token ) == 0)
  4662. {
  4663. GetToken( false );
  4664. strcpyn( pseq->autolayer[pseq->numautolayers].name, token );
  4665. while (TokenAvailable( ))
  4666. {
  4667. GetToken( false );
  4668. if (stricmp( "local", token ) == 0)
  4669. {
  4670. pseq->autolayer[pseq->numautolayers].flags |= STUDIO_AL_LOCAL;
  4671. pseq->flags |= STUDIO_LOCAL;
  4672. }
  4673. else
  4674. {
  4675. UnGetToken();
  4676. break;
  4677. }
  4678. }
  4679. pseq->numautolayers++;
  4680. }
  4681. else if (stricmp( "iklock", token ) == 0)
  4682. {
  4683. GetToken(false);
  4684. strcpyn( pseq->iklock[pseq->numiklocks].name, token );
  4685. GetToken(false);
  4686. pseq->iklock[pseq->numiklocks].flPosWeight = verify_atof( token );
  4687. GetToken(false);
  4688. pseq->iklock[pseq->numiklocks].flLocalQWeight = verify_atof( token );
  4689. pseq->numiklocks++;
  4690. }
  4691. else if (stricmp( "keyvalues", token ) == 0)
  4692. {
  4693. Option_KeyValues( &pseq->KeyValue );
  4694. }
  4695. else if (stricmp( "blendlayer", token ) == 0)
  4696. {
  4697. pseq->autolayer[pseq->numautolayers].flags = 0;
  4698. GetToken( false );
  4699. strcpyn( pseq->autolayer[pseq->numautolayers].name, token );
  4700. GetToken( false );
  4701. pseq->autolayer[pseq->numautolayers].start = verify_atof( token );
  4702. GetToken( false );
  4703. pseq->autolayer[pseq->numautolayers].peak = verify_atof( token );
  4704. GetToken( false );
  4705. pseq->autolayer[pseq->numautolayers].tail = verify_atof( token );
  4706. GetToken( false );
  4707. pseq->autolayer[pseq->numautolayers].end = verify_atof( token );
  4708. while (TokenAvailable( ))
  4709. {
  4710. GetToken( false );
  4711. if (stricmp( "xfade", token ) == 0)
  4712. {
  4713. pseq->autolayer[pseq->numautolayers].flags |= STUDIO_AL_XFADE;
  4714. }
  4715. else if (stricmp( "spline", token ) == 0)
  4716. {
  4717. pseq->autolayer[pseq->numautolayers].flags |= STUDIO_AL_SPLINE;
  4718. }
  4719. else if (stricmp( "noblend", token ) == 0)
  4720. {
  4721. pseq->autolayer[pseq->numautolayers].flags |= STUDIO_AL_NOBLEND;
  4722. }
  4723. else if (stricmp( "poseparameter", token ) == 0)
  4724. {
  4725. pseq->autolayer[pseq->numautolayers].flags |= STUDIO_AL_POSE;
  4726. GetToken( false );
  4727. pseq->autolayer[pseq->numautolayers].pose = LookupPoseParameter( token );
  4728. }
  4729. else if (stricmp( "local", token ) == 0)
  4730. {
  4731. pseq->autolayer[pseq->numautolayers].flags |= STUDIO_AL_LOCAL;
  4732. pseq->flags |= STUDIO_LOCAL;
  4733. }
  4734. else
  4735. {
  4736. UnGetToken();
  4737. break;
  4738. }
  4739. }
  4740. pseq->numautolayers++;
  4741. }
  4742. else if ((numblends || isAppend) && ParseAnimationToken( animations[0] ))
  4743. {
  4744. }
  4745. else if (!isAppend)
  4746. {
  4747. // assume it's an animation reference
  4748. // first look up an existing animation
  4749. for (n = 0; n < g_numani; n++)
  4750. {
  4751. if (stricmp( token, g_panimation[n]->name ) == 0)
  4752. {
  4753. animations[numblends++] = g_panimation[n];
  4754. break;
  4755. }
  4756. }
  4757. if (n >= g_numani)
  4758. {
  4759. // assume it's an implied animation
  4760. animations[numblends++] = ProcessImpliedAnimation( pseq, token );
  4761. }
  4762. // hack to allow animation commands to refer to same sequence
  4763. if (numblends == 1)
  4764. {
  4765. pseq->panim[0][0] = animations[0];
  4766. }
  4767. }
  4768. else
  4769. {
  4770. TokenError( "unknown command \"%s\"\n", token );
  4771. }
  4772. if (depth < 0)
  4773. {
  4774. TokenError("missing {\n");
  4775. }
  4776. }
  4777. ProcessSequence( pseq, numblends, animations, isAppend );
  4778. return 0;
  4779. }
  4780. //-----------------------------------------------------------------------------
  4781. // Purpose: throw away all the options for a specific sequence or animation
  4782. //-----------------------------------------------------------------------------
  4783. int ParseEmpty( )
  4784. {
  4785. int depth = 0;
  4786. while (1)
  4787. {
  4788. if (depth > 0)
  4789. {
  4790. if(!GetToken(true))
  4791. {
  4792. break;
  4793. }
  4794. }
  4795. else
  4796. {
  4797. if (!TokenAvailable())
  4798. {
  4799. break;
  4800. }
  4801. else
  4802. {
  4803. GetToken (false);
  4804. }
  4805. }
  4806. if (endofscript)
  4807. {
  4808. if (depth != 0)
  4809. {
  4810. TokenError("missing }\n" );
  4811. }
  4812. return 1;
  4813. }
  4814. if (stricmp("{", token ) == 0)
  4815. {
  4816. depth++;
  4817. }
  4818. else if (stricmp("}", token ) == 0)
  4819. {
  4820. depth--;
  4821. }
  4822. if (depth < 0)
  4823. {
  4824. TokenError("missing {\n");
  4825. }
  4826. }
  4827. return 0;
  4828. }
  4829. //-----------------------------------------------------------------------------
  4830. // Purpose: append commands to either a sequence or an animation
  4831. //-----------------------------------------------------------------------------
  4832. void Cmd_Append( )
  4833. {
  4834. GetToken(false);
  4835. s_sequence_t *pseq = LookupSequence( token );
  4836. if (pseq)
  4837. {
  4838. ParseSequence( pseq, true );
  4839. return;
  4840. }
  4841. else
  4842. {
  4843. s_animation_t *panim = LookupAnimation( token );
  4844. if (panim)
  4845. {
  4846. ParseAnimation( panim, true );
  4847. return;
  4848. }
  4849. }
  4850. TokenError( "unknown append animation %s\n", token );
  4851. }
  4852. void Cmd_Prepend( )
  4853. {
  4854. GetToken(false);
  4855. s_sequence_t *pseq = LookupSequence( token );
  4856. int count = 0;
  4857. s_animation_t *panim = NULL;
  4858. int iRet = false;
  4859. if (pseq)
  4860. {
  4861. panim = pseq->panim[0][0];
  4862. count = panim->numcmds;
  4863. iRet = ParseSequence( pseq, true );
  4864. }
  4865. else
  4866. {
  4867. panim = LookupAnimation( token );
  4868. if (panim)
  4869. {
  4870. count = panim->numcmds;
  4871. iRet = ParseAnimation( panim, true );
  4872. }
  4873. }
  4874. if (panim && count != panim->numcmds)
  4875. {
  4876. s_animcmd_t tmp;
  4877. tmp = panim->cmds[panim->numcmds - 1];
  4878. int i;
  4879. for (i = panim->numcmds - 1; i > 0; i--)
  4880. {
  4881. panim->cmds[i] = panim->cmds[i-1];
  4882. }
  4883. panim->cmds[0] = tmp;
  4884. return;
  4885. }
  4886. TokenError( "unknown prepend animation \"%s\"\n", token );
  4887. }
  4888. void Cmd_Continue( )
  4889. {
  4890. GetToken(false);
  4891. s_sequence_t *pseq = LookupSequence( token );
  4892. if (pseq)
  4893. {
  4894. GetToken(true);
  4895. UnGetToken();
  4896. if (token[0] != '$')
  4897. ParseSequence( pseq, true );
  4898. return;
  4899. }
  4900. else
  4901. {
  4902. s_animation_t *panim = LookupAnimation( token );
  4903. if (panim)
  4904. {
  4905. GetToken(true);
  4906. UnGetToken();
  4907. if (token[0] != '$')
  4908. ParseAnimation( panim, true );
  4909. return;
  4910. }
  4911. }
  4912. TokenError( "unknown continue animation %s\n", token );
  4913. }
  4914. //-----------------------------------------------------------------------------
  4915. // Purpose: foward declare an empty sequence
  4916. //-----------------------------------------------------------------------------
  4917. void Cmd_DeclareSequence( void )
  4918. {
  4919. if (g_sequence.Count() >= MAXSTUDIOSEQUENCES)
  4920. {
  4921. TokenError("Too many sequences (%d max)\n", MAXSTUDIOSEQUENCES );
  4922. }
  4923. s_sequence_t *pseq = &g_sequence[ g_sequence.AddToTail() ];
  4924. memset( pseq, 0, sizeof( s_sequence_t ) );
  4925. pseq->flags = STUDIO_OVERRIDE;
  4926. // initialize sequence
  4927. GetToken( false );
  4928. strcpyn( pseq->name, token );
  4929. }
  4930. //-----------------------------------------------------------------------------
  4931. // Purpose: foward declare an empty sequence
  4932. //-----------------------------------------------------------------------------
  4933. void Cmd_DeclareAnimation( void )
  4934. {
  4935. if (g_numani >= MAXSTUDIOANIMS)
  4936. {
  4937. TokenError("Too many animations (%d max)\n", MAXSTUDIOANIMS );
  4938. }
  4939. // allocate animation entry
  4940. s_animation_t *panim = (s_animation_t *)calloc( 1, sizeof( s_animation_t ) );
  4941. g_panimation[g_numani] = panim;
  4942. panim->index = g_numani;
  4943. panim->flags = STUDIO_OVERRIDE;
  4944. g_numani++;
  4945. // initialize animation
  4946. GetToken( false );
  4947. strcpyn( panim->name, token );
  4948. }
  4949. //-----------------------------------------------------------------------------
  4950. // Purpose: create named list of boneweights
  4951. //-----------------------------------------------------------------------------
  4952. void Option_Weightlist( s_weightlist_t *pweightlist )
  4953. {
  4954. int depth = 0;
  4955. int i;
  4956. pweightlist->numbones = 0;
  4957. while (1)
  4958. {
  4959. if (depth > 0)
  4960. {
  4961. if(!GetToken(true))
  4962. {
  4963. break;
  4964. }
  4965. }
  4966. else
  4967. {
  4968. if (!TokenAvailable())
  4969. {
  4970. break;
  4971. }
  4972. else
  4973. {
  4974. GetToken (false);
  4975. }
  4976. }
  4977. if (endofscript)
  4978. {
  4979. if (depth != 0)
  4980. {
  4981. TokenError("missing }\n" );
  4982. }
  4983. return;
  4984. }
  4985. if (stricmp("{", token ) == 0)
  4986. {
  4987. depth++;
  4988. }
  4989. else if (stricmp("}", token ) == 0)
  4990. {
  4991. depth--;
  4992. }
  4993. else if (stricmp("posweight", token ) == 0)
  4994. {
  4995. i = pweightlist->numbones - 1;
  4996. if (i < 0)
  4997. {
  4998. MdlError( "Error with specifing bone Position weight \'%s:%s\'\n", pweightlist->name, pweightlist->bonename[i] );
  4999. }
  5000. GetToken( false );
  5001. pweightlist->boneposweight[i] = verify_atof( token );
  5002. if (pweightlist->boneweight[i] == 0 && pweightlist->boneposweight[i] > 0)
  5003. {
  5004. MdlError( "Non-zero Position weight with zero Rotation weight not allowed \'%s:%s %f %f\'\n",
  5005. pweightlist->name, pweightlist->bonename[i], pweightlist->boneweight[i], pweightlist->boneposweight[i] );
  5006. }
  5007. }
  5008. else
  5009. {
  5010. i = pweightlist->numbones++;
  5011. if (i >= MAXWEIGHTSPERLIST)
  5012. {
  5013. TokenError("Too many bones (%d) in weightlist '%s'\n", i, pweightlist->name );
  5014. }
  5015. pweightlist->bonename[i] = strdup( token );
  5016. GetToken( false );
  5017. pweightlist->boneweight[i] = verify_atof( token );
  5018. pweightlist->boneposweight[i] = pweightlist->boneweight[i];
  5019. }
  5020. if (depth < 0)
  5021. {
  5022. TokenError("missing {\n");
  5023. }
  5024. };
  5025. }
  5026. void Cmd_Weightlist( )
  5027. {
  5028. int i;
  5029. if (!GetToken(false))
  5030. return;
  5031. if (g_numweightlist >= MAXWEIGHTLISTS)
  5032. {
  5033. TokenError( "Too many weightlist commands (%d)\n", MAXWEIGHTLISTS );
  5034. }
  5035. for (i = 1; i < g_numweightlist; i++)
  5036. {
  5037. if (stricmp( g_weightlist[i].name, token ) == 0)
  5038. {
  5039. TokenError( "Duplicate weightlist '%s'\n", token );
  5040. }
  5041. }
  5042. strcpyn( g_weightlist[i].name, token );
  5043. Option_Weightlist( &g_weightlist[g_numweightlist] );
  5044. g_numweightlist++;
  5045. }
  5046. void Cmd_DefaultWeightlist( )
  5047. {
  5048. Option_Weightlist( &g_weightlist[0] );
  5049. }
  5050. //-----------------------------------------------------------------------------
  5051. // Purpose:
  5052. //-----------------------------------------------------------------------------
  5053. void Option_Eyeball( s_model_t *pmodel )
  5054. {
  5055. Vector tmp;
  5056. int i, j;
  5057. int mesh_material;
  5058. char szMeshMaterial[256];
  5059. s_eyeball_t *eyeball = &(pmodel->eyeball[pmodel->numeyeballs++]);
  5060. // name
  5061. GetToken (false);
  5062. strcpyn( eyeball->name, token );
  5063. // bone name
  5064. GetToken (false);
  5065. for (i = 0; i < pmodel->source->numbones; i++)
  5066. {
  5067. if ( !Q_stricmp( pmodel->source->localBone[i].name, token ) )
  5068. {
  5069. eyeball->bone = i;
  5070. break;
  5071. }
  5072. }
  5073. if (!g_bCreateMakefile && i >= pmodel->source->numbones)
  5074. {
  5075. TokenError( "unknown eyeball bone \"%s\"\n", token );
  5076. }
  5077. // X
  5078. GetToken (false);
  5079. tmp[0] = verify_atof (token);
  5080. // Y
  5081. GetToken (false);
  5082. tmp[1] = verify_atof (token);
  5083. // Z
  5084. GetToken (false);
  5085. tmp[2] = verify_atof (token);
  5086. // mesh material
  5087. GetToken (false);
  5088. Q_strncpy( szMeshMaterial, token, sizeof(szMeshMaterial) );
  5089. mesh_material = UseTextureAsMaterial( LookupTexture( token ) );
  5090. // diameter
  5091. GetToken (false);
  5092. eyeball->radius = verify_atof (token) / 2.0;
  5093. // Z angle offset
  5094. GetToken (false);
  5095. eyeball->zoffset = tan( DEG2RAD( verify_atof (token) ) );
  5096. // iris material (no longer used, but we need to remove the token)
  5097. GetToken (false);
  5098. // pupil scale
  5099. GetToken (false);
  5100. eyeball->iris_scale = 1.0 / verify_atof( token );
  5101. VectorCopy( tmp, eyeball->org );
  5102. for (i = 0; i < pmodel->source->nummeshes; i++)
  5103. {
  5104. j = pmodel->source->meshindex[i]; // meshes are internally stored by material index
  5105. if (j == mesh_material)
  5106. {
  5107. eyeball->mesh = i; // FIXME: should this be pre-adjusted?
  5108. break;
  5109. }
  5110. }
  5111. if (!g_bCreateMakefile && i >= pmodel->source->nummeshes)
  5112. {
  5113. TokenError("can't find eyeball texture \"%s\" on model\n", szMeshMaterial );
  5114. }
  5115. // translate eyeball into bone space
  5116. VectorITransform( tmp, pmodel->source->boneToPose[eyeball->bone], eyeball->org );
  5117. matrix3x4_t vtmp;
  5118. AngleMatrix( g_defaultrotation, vtmp );
  5119. VectorIRotate( Vector( 0, 0, 1 ), vtmp, tmp );
  5120. VectorIRotate( tmp, pmodel->source->boneToPose[eyeball->bone], eyeball->up );
  5121. VectorIRotate( Vector( 1, 0, 0 ), vtmp, tmp );
  5122. VectorIRotate( tmp, pmodel->source->boneToPose[eyeball->bone], eyeball->forward );
  5123. // these get overwritten by "eyelid" data
  5124. eyeball->upperlidflexdesc = -1;
  5125. eyeball->lowerlidflexdesc = -1;
  5126. }
  5127. //-----------------------------------------------------------------------------
  5128. // Purpose:
  5129. //-----------------------------------------------------------------------------
  5130. void Option_Spherenormals( s_source_t *psource )
  5131. {
  5132. Vector pos;
  5133. int i, j;
  5134. int mesh_material;
  5135. char szMeshMaterial[256];
  5136. // mesh material
  5137. GetToken (false);
  5138. strcpyn( szMeshMaterial, token );
  5139. mesh_material = UseTextureAsMaterial( LookupTexture( token ) );
  5140. // X
  5141. GetToken (false);
  5142. pos[0] = verify_atof (token);
  5143. // Y
  5144. GetToken (false);
  5145. pos[1] = verify_atof (token);
  5146. // Z
  5147. GetToken (false);
  5148. pos[2] = verify_atof (token);
  5149. for (i = 0; i < psource->nummeshes; i++)
  5150. {
  5151. j = psource->meshindex[i]; // meshes are internally stored by material index
  5152. if (j == mesh_material)
  5153. {
  5154. s_vertexinfo_t *vertex = &psource->vertex[psource->mesh[i].vertexoffset];
  5155. for (int k = 0; k < psource->mesh[i].numvertices; k++)
  5156. {
  5157. Vector n = vertex[k].position - pos;
  5158. VectorNormalize( n );
  5159. if (DotProduct( n, vertex[k].normal ) < 0.0)
  5160. {
  5161. vertex[k].normal = -1 * n;
  5162. }
  5163. else
  5164. {
  5165. vertex[k].normal = n;
  5166. }
  5167. #if 0
  5168. vertex[k].normal[0] += 0.5f * ( 2.0f * ( ( float )rand() ) / ( float )VALVE_RAND_MAX ) - 1.0f;
  5169. vertex[k].normal[1] += 0.5f * ( 2.0f * ( ( float )rand() ) / ( float )VALVE_RAND_MAX ) - 1.0f;
  5170. vertex[k].normal[2] += 0.5f * ( 2.0f * ( ( float )rand() ) / ( float )VALVE_RAND_MAX ) - 1.0f;
  5171. VectorNormalize( vertex[k].normal );
  5172. #endif
  5173. }
  5174. break;
  5175. }
  5176. }
  5177. if (i >= psource->nummeshes)
  5178. {
  5179. TokenError("can't find spherenormal texture \"%s\" on model\n", szMeshMaterial );
  5180. }
  5181. }
  5182. //-----------------------------------------------------------------------------
  5183. // Purpose:
  5184. //-----------------------------------------------------------------------------
  5185. int Add_Flexdesc( const char *name )
  5186. {
  5187. int flexdesc;
  5188. for ( flexdesc = 0; flexdesc < g_numflexdesc; flexdesc++)
  5189. {
  5190. if (stricmp( name, g_flexdesc[flexdesc].FACS ) == 0)
  5191. {
  5192. break;
  5193. }
  5194. }
  5195. if (flexdesc >= MAXSTUDIOFLEXDESC)
  5196. {
  5197. TokenError( "Too many flex types, max %d\n", MAXSTUDIOFLEXDESC );
  5198. }
  5199. if (flexdesc == g_numflexdesc)
  5200. {
  5201. strcpyn( g_flexdesc[flexdesc].FACS, name );
  5202. g_numflexdesc++;
  5203. }
  5204. return flexdesc;
  5205. }
  5206. //-----------------------------------------------------------------------------
  5207. //
  5208. // A vertex cache animation file is a special case of a VTA file
  5209. // Same format
  5210. // Frame 0 is the defaultflex frame
  5211. // All other frames will get a flexdesc of "f#" where # [0,frameCount-1]
  5212. // Then an NWAY controller will be defined to play back the flex data
  5213. // as an animation as the controller goes from [0,1]
  5214. //-----------------------------------------------------------------------------
  5215. void Option_VertexCacheAnimationFile( char *pszVtaFile, int nModelIndex )
  5216. {
  5217. if ( g_numflexkeys > 0 )
  5218. {
  5219. MdlError( __FUNCTION__": Flexes already defined. vcafile can be only flex option in $model block\n" );
  5220. return;
  5221. }
  5222. s_source_t *pSource = g_model[ nModelIndex ]->source;
  5223. s_source_t *pVtaSource = Load_Source( pszVtaFile, "vta" );
  5224. if ( pVtaSource->m_Animations.Count() <= 0 )
  5225. {
  5226. MdlError( __FUNCTION__": No animations in VertexCacheAnimationFile \"%s\"\n", pszVtaFile );
  5227. return;
  5228. }
  5229. {
  5230. s_flexkey_t &flexKey = g_flexkey[g_numflexkeys++];
  5231. flexKey.flexdesc = Add_Flexdesc( "default" );
  5232. flexKey.flexpair = 0;
  5233. flexKey.source = pVtaSource;
  5234. flexKey.imodel = nModelIndex;
  5235. flexKey.frame = 0;
  5236. flexKey.target0 = 0.0;
  5237. flexKey.target1 = 1.0;
  5238. flexKey.target2 = 10;
  5239. flexKey.target3 = 11;
  5240. flexKey.split = 0;
  5241. flexKey.decay = 0.0;
  5242. V_strncpy( flexKey.animationname, pVtaSource->m_Animations[0].animationname, ARRAYSIZE( flexKey.animationname ) );
  5243. }
  5244. CFmtStr sTmp;
  5245. const int nActualFrameCount = pVtaSource->m_Animations.Head().numframes - 1;
  5246. for ( int i = 0; i < nActualFrameCount; ++i )
  5247. {
  5248. sTmp.sprintf( "f%d", i );
  5249. {
  5250. s_flexkey_t &flexKey = g_flexkey[g_numflexkeys++];
  5251. flexKey.flexdesc = Add_Flexdesc( sTmp.Access() );
  5252. flexKey.flexpair = 0;
  5253. flexKey.source = pVtaSource;
  5254. flexKey.imodel = nModelIndex;
  5255. flexKey.frame = ( i + 1 );
  5256. flexKey.target0 = 0.0;
  5257. flexKey.target1 = 1.0;
  5258. flexKey.target2 = 10;
  5259. flexKey.target3 = 11;
  5260. flexKey.split = 0;
  5261. flexKey.decay = 0.0;
  5262. V_strncpy( flexKey.animationname, pVtaSource->m_Animations[0].animationname, ARRAYSIZE( flexKey.animationname ) );
  5263. }
  5264. }
  5265. s_flexcontrollerremap_t &flexRemap = pSource->m_FlexControllerRemaps[ pSource->m_FlexControllerRemaps.AddToTail() ];
  5266. flexRemap.m_RemapType = FLEXCONTROLLER_REMAP_NWAY;
  5267. flexRemap.m_bIsStereo = false;
  5268. flexRemap.m_Index = -1; // Don't know this right now
  5269. flexRemap.m_LeftIndex = -1; // Don't know this right now
  5270. flexRemap.m_RightIndex = -1; // Don't know this right now
  5271. flexRemap.m_MultiIndex = -1; // Don't know this right now
  5272. flexRemap.m_EyesUpDownFlexController = -1;
  5273. flexRemap.m_BlinkController = -1;
  5274. char szBuf[ MAX_PATH ];
  5275. V_FileBase( pVtaSource->filename, szBuf, ARRAYSIZE( szBuf ) );
  5276. flexRemap.m_Name = szBuf;
  5277. for ( int i = 0; i < nActualFrameCount; ++i )
  5278. {
  5279. sTmp.sprintf( "f%d", i );
  5280. flexRemap.m_RawControls.AddToTail( sTmp.Access() );
  5281. }
  5282. for ( int i = 0; i < flexRemap.m_RawControls.Count(); ++i )
  5283. {
  5284. int nFlexKey = -1;
  5285. for ( int j = 0; j < g_numflexkeys; ++j )
  5286. {
  5287. if ( !V_stricmp( g_flexdesc[ g_flexkey[j].flexdesc ].FACS, flexRemap.m_RawControls[i].Get() ) )
  5288. {
  5289. nFlexKey = j;
  5290. break;
  5291. }
  5292. }
  5293. if ( nFlexKey < 0 )
  5294. {
  5295. MdlError( __FUNCTION__"Cannot find flex to group \"%s\"\n", flexRemap.m_RawControls[i].Get() );
  5296. pSource->m_FlexControllerRemaps.RemoveMultipleFromTail( 1 );
  5297. return;
  5298. }
  5299. s_combinationcontrol_t &combinationControl = pSource->m_CombinationControls[ pSource->m_CombinationControls.AddToTail() ];
  5300. V_strncpy( combinationControl.name, flexRemap.m_RawControls[i].Get(), ARRAYSIZE( combinationControl.name ) );
  5301. s_combinationrule_t &combinationRule = pSource->m_CombinationRules[ pSource->m_CombinationRules.AddToTail() ];
  5302. combinationRule.m_nFlex = nFlexKey;
  5303. combinationRule.m_Combination.AddToTail( nFlexKey - 1 );
  5304. }
  5305. AddFlexControllers( pSource );
  5306. AddBodyFlexRemaps( pSource );
  5307. }
  5308. //-----------------------------------------------------------------------------
  5309. // Purpose:
  5310. //-----------------------------------------------------------------------------
  5311. void Option_Flex( char *name, char *vtafile, int imodel, float pairsplit )
  5312. {
  5313. if (g_numflexkeys >= MAXSTUDIOFLEXKEYS)
  5314. {
  5315. TokenError( "Too many flexes, max %d\n", MAXSTUDIOFLEXKEYS );
  5316. }
  5317. int flexdesc, flexpair;
  5318. if (pairsplit != 0)
  5319. {
  5320. char mod[256];
  5321. sprintf( mod, "%sR", name );
  5322. flexdesc = Add_Flexdesc( mod );
  5323. sprintf( mod, "%sL", name );
  5324. flexpair = Add_Flexdesc( mod );
  5325. }
  5326. else
  5327. {
  5328. flexdesc = Add_Flexdesc( name );
  5329. flexpair = 0;
  5330. }
  5331. // initialize
  5332. g_flexkey[g_numflexkeys].imodel = imodel;
  5333. g_flexkey[g_numflexkeys].flexdesc = flexdesc;
  5334. g_flexkey[g_numflexkeys].target0 = 0.0;
  5335. g_flexkey[g_numflexkeys].target1 = 1.0;
  5336. g_flexkey[g_numflexkeys].target2 = 10;
  5337. g_flexkey[g_numflexkeys].target3 = 11;
  5338. g_flexkey[g_numflexkeys].split = pairsplit;
  5339. g_flexkey[g_numflexkeys].flexpair = flexpair;
  5340. g_flexkey[g_numflexkeys].decay = 1.0;
  5341. while (TokenAvailable())
  5342. {
  5343. GetToken(false);
  5344. if (stricmp( token, "frame") == 0)
  5345. {
  5346. GetToken (false);
  5347. g_flexkey[g_numflexkeys].frame = verify_atoi( token );
  5348. }
  5349. else if (stricmp( token, "position") == 0)
  5350. {
  5351. GetToken (false);
  5352. g_flexkey[g_numflexkeys].target1 = verify_atof( token );
  5353. }
  5354. else if (stricmp( token, "split") == 0)
  5355. {
  5356. GetToken (false);
  5357. g_flexkey[g_numflexkeys].split = verify_atof( token );
  5358. }
  5359. else if (stricmp( token, "decay") == 0)
  5360. {
  5361. GetToken (false);
  5362. g_flexkey[g_numflexkeys].decay = verify_atof( token );
  5363. }
  5364. else
  5365. {
  5366. TokenError( "unknown option: %s", token );
  5367. }
  5368. }
  5369. if (g_numflexkeys > 1)
  5370. {
  5371. if (g_flexkey[g_numflexkeys-1].flexdesc == g_flexkey[g_numflexkeys].flexdesc)
  5372. {
  5373. g_flexkey[g_numflexkeys-1].target2 = g_flexkey[g_numflexkeys-1].target1;
  5374. g_flexkey[g_numflexkeys-1].target3 = g_flexkey[g_numflexkeys].target1;
  5375. g_flexkey[g_numflexkeys].target0 = g_flexkey[g_numflexkeys-1].target1;
  5376. }
  5377. }
  5378. // link to source
  5379. s_source_t *pSource = Load_Source( vtafile, "vta" );
  5380. g_flexkey[g_numflexkeys].source = pSource;
  5381. if ( pSource->m_Animations.Count() )
  5382. {
  5383. Q_strncpy( g_flexkey[g_numflexkeys].animationname, pSource->m_Animations[0].animationname, sizeof( g_flexkey[g_numflexkeys].animationname ) );
  5384. }
  5385. else
  5386. {
  5387. g_flexkey[g_numflexkeys].animationname[0] = 0;
  5388. }
  5389. g_numflexkeys++;
  5390. // this needs to be per model.
  5391. }
  5392. //-----------------------------------------------------------------------------
  5393. // Adds combination data to the source
  5394. //-----------------------------------------------------------------------------
  5395. int FindSourceFlexKey( s_source_t *pSource, const char *pName )
  5396. {
  5397. int nCount = pSource->m_FlexKeys.Count();
  5398. for ( int i = 0; i < nCount; ++i )
  5399. {
  5400. if ( !Q_stricmp( pSource->m_FlexKeys[i].animationname, pName ) )
  5401. return i;
  5402. }
  5403. return -1;
  5404. }
  5405. //-----------------------------------------------------------------------------
  5406. // Adds flexkey data to a particular source
  5407. //-----------------------------------------------------------------------------
  5408. void AddFlexKey( s_source_t *pSource, CDmeCombinationOperator *pComboOp, const char *pFlexKeyName )
  5409. {
  5410. // See if the delta state is already accounted for
  5411. if ( FindSourceFlexKey( pSource, pFlexKeyName ) >= 0 )
  5412. return;
  5413. int i = pSource->m_FlexKeys.AddToTail();
  5414. s_flexkey_t &key = pSource->m_FlexKeys[i];
  5415. memset( &key, 0, sizeof(key) );
  5416. key.target0 = 0.0f;
  5417. key.target1 = 1.0f;
  5418. key.target2 = 10.0f;
  5419. key.target3 = 11.0f;
  5420. key.decay = 1.0f;
  5421. key.source = pSource;
  5422. Q_strncpy( key.animationname, pFlexKeyName, sizeof(key.animationname) );
  5423. key.flexpair = pComboOp->IsDeltaStateStereo( pFlexKeyName ); // Signal used by AddBodyFlexData
  5424. }
  5425. //-----------------------------------------------------------------------------
  5426. //
  5427. //-----------------------------------------------------------------------------
  5428. void FindOrAddFlexController(
  5429. const char *pszFlexControllerName,
  5430. const char *pszFlexControllerType = "default",
  5431. float flMin = 0.0f,
  5432. float flMax = 1.0f )
  5433. {
  5434. for ( int i = 0; i < g_numflexcontrollers; ++i )
  5435. {
  5436. if ( !V_strcmp( g_flexcontroller[i].name, pszFlexControllerName ) )
  5437. {
  5438. if ( V_strcmp( g_flexcontroller[i].type, pszFlexControllerType ) ||
  5439. g_flexcontroller[i].min != flMin ||
  5440. g_flexcontroller[i].max != flMax )
  5441. {
  5442. MdlWarning( "Flex Controller %s Defined Twice With Different Params: %s, %f %f vs %s, %f %f\n",
  5443. pszFlexControllerName,
  5444. pszFlexControllerType, flMin, flMax,
  5445. g_flexcontroller[i].type, g_flexcontroller[i].min, g_flexcontroller[i].max );
  5446. }
  5447. return;
  5448. }
  5449. }
  5450. strcpyn( g_flexcontroller[g_numflexcontrollers].name, pszFlexControllerName );
  5451. strcpyn( g_flexcontroller[g_numflexcontrollers].type, pszFlexControllerType );
  5452. g_flexcontroller[g_numflexcontrollers].min = flMin;
  5453. g_flexcontroller[g_numflexcontrollers].max = flMax;
  5454. g_numflexcontrollers++;
  5455. }
  5456. //-----------------------------------------------------------------------------
  5457. // In scriplib.cpp
  5458. // Called to parse from a memory buffer on the script stack
  5459. //-----------------------------------------------------------------------------
  5460. void PushMemoryScript( char *pszBuffer, const int nSize );
  5461. bool PopMemoryScript();
  5462. //-----------------------------------------------------------------------------
  5463. // Adds combination data to the source
  5464. //-----------------------------------------------------------------------------
  5465. void AddCombination( s_source_t *pSource, CDmeCombinationOperator *pCombination )
  5466. {
  5467. CDmrElementArray< CDmElement > targets = pCombination->GetAttribute( "targets" );
  5468. // See if all targets of the DmeCombinationOperator are DmeFlexRules
  5469. // If so implement controllers & flexes from flex rules, if not do old
  5470. // behavior
  5471. bool bFlexRules = true;
  5472. for ( int i = 0; i < targets.Count(); ++i )
  5473. {
  5474. if ( !CastElement< CDmeFlexRules >( targets[i] ) )
  5475. {
  5476. bFlexRules = false;
  5477. break;
  5478. }
  5479. }
  5480. if ( bFlexRules )
  5481. {
  5482. // Add a controller for each control in the combintion operator
  5483. CDmAttribute *pControlsAttr = pCombination->GetAttribute( "controls" );
  5484. if ( pControlsAttr )
  5485. {
  5486. CDmrElementArrayConst< CDmElement > controlsAttr( pControlsAttr );
  5487. for ( int i = 0; i < controlsAttr.Count(); ++i )
  5488. {
  5489. CDmElement *pControlElement = controlsAttr[i];
  5490. if ( !pControlElement )
  5491. continue;
  5492. float flMin = 0.0f;
  5493. float flMax = 1.0f;
  5494. flMin = pControlElement->GetValue( "flexMin", flMin );
  5495. flMax = pControlElement->GetValue( "flexMax", flMax );
  5496. FindOrAddFlexController( pControlElement->GetName(), "default", flMin, flMax );
  5497. }
  5498. }
  5499. CUtlString sOldToken = token;
  5500. CUtlString sTmpBuf;
  5501. for ( int i = 0; i < targets.Count(); ++i )
  5502. {
  5503. CDmeFlexRules *pDmeFlexRules = CastElement< CDmeFlexRules >( targets[i] );
  5504. if ( !pDmeFlexRules )
  5505. continue;
  5506. for ( int i = 0; i < pDmeFlexRules->GetRuleCount(); ++i )
  5507. {
  5508. CDmeFlexRuleBase *pDmeFlexRule = pDmeFlexRules->GetRule( i );
  5509. if ( !pDmeFlexRule )
  5510. continue;
  5511. sTmpBuf = "= ";
  5512. bool bFlexRule = true;
  5513. if ( CastElement< CDmeFlexRulePassThrough >( pDmeFlexRule ) )
  5514. {
  5515. sTmpBuf += pDmeFlexRule->GetName();
  5516. }
  5517. else if ( CastElement< CDmeFlexRuleExpression >( pDmeFlexRule ) )
  5518. {
  5519. CDmeFlexRuleExpression *pDmeFlexRuleExpression = CastElement< CDmeFlexRuleExpression >( pDmeFlexRule );
  5520. sTmpBuf += pDmeFlexRuleExpression->GetExpression();
  5521. }
  5522. else if ( CastElement< CDmeFlexRuleLocalVar >( pDmeFlexRule ) )
  5523. {
  5524. bFlexRule = false;
  5525. }
  5526. else
  5527. {
  5528. MdlWarning( "Unknown DmeDeltaRule: %s Of Type: %s\n", pDmeFlexRule->GetName(), pDmeFlexRule->GetTypeString() );
  5529. continue;
  5530. }
  5531. PushMemoryScript( sTmpBuf.Get(), sTmpBuf.Length() );
  5532. Add_Flexdesc( pDmeFlexRule->GetName() );
  5533. if ( bFlexRule )
  5534. {
  5535. Option_Flexrule( NULL, pDmeFlexRule->GetName() );
  5536. }
  5537. PopMemoryScript();
  5538. }
  5539. }
  5540. V_strncpy( token, sOldToken.Get(), ARRAYSIZE( token ) );
  5541. UnGetToken();
  5542. return;
  5543. }
  5544. // Define the remapped controls
  5545. int nControlCount = pCombination->GetRawControlCount();
  5546. for ( int i = 0; i < nControlCount; ++i )
  5547. {
  5548. int m = pSource->m_CombinationControls.AddToTail();
  5549. s_combinationcontrol_t &control = pSource->m_CombinationControls[m];
  5550. Q_strncpy( control.name, pCombination->GetRawControlName( i ), sizeof(control.name) );
  5551. }
  5552. // Define the combination + domination rules
  5553. int nTargetCount = pCombination->GetOperationTargetCount();
  5554. for ( int i = 0; i < nTargetCount; ++i )
  5555. {
  5556. int nOpCount = pCombination->GetOperationCount( i );
  5557. for ( int j = 0; j < nOpCount; ++j )
  5558. {
  5559. CDmElement *pDeltaState = pCombination->GetOperationDeltaState( i, j );
  5560. if ( !pDeltaState )
  5561. continue;
  5562. int nFlex = FindSourceFlexKey( pSource, pDeltaState->GetName() );
  5563. if ( nFlex < 0 )
  5564. continue;
  5565. int k = pSource->m_CombinationRules.AddToTail();
  5566. s_combinationrule_t &rule = pSource->m_CombinationRules[k];
  5567. rule.m_nFlex = nFlex;
  5568. rule.m_Combination = pCombination->GetOperationControls( i, j );
  5569. int nDominatorCount = pCombination->GetOperationDominatorCount( i, j );
  5570. for ( int l = 0; l < nDominatorCount; ++l )
  5571. {
  5572. int m = rule.m_Dominators.AddToTail();
  5573. rule.m_Dominators[m] = pCombination->GetOperationDominator( i, j, l );
  5574. }
  5575. }
  5576. }
  5577. // Define the remapping controls
  5578. nControlCount = pCombination->GetControlCount();
  5579. for ( int i = 0; i < nControlCount; ++i )
  5580. {
  5581. int k = pSource->m_FlexControllerRemaps.AddToTail();
  5582. s_flexcontrollerremap_t &remap = pSource->m_FlexControllerRemaps[k];
  5583. remap.m_Name = pCombination->GetControlName( i );
  5584. remap.m_bIsStereo = pCombination->IsStereoControl( i );
  5585. remap.m_Index = -1; // Don't know this right now
  5586. remap.m_LeftIndex = -1; // Don't know this right now
  5587. remap.m_RightIndex = -1; // Don't know this right now
  5588. remap.m_MultiIndex = -1; // Don't know this right now
  5589. remap.m_EyesUpDownFlexController = -1;
  5590. remap.m_BlinkController = -1;
  5591. int nRemapCount = pCombination->GetRawControlCount( i );
  5592. if ( pCombination->IsEyelidControl( i ) )
  5593. {
  5594. remap.m_RemapType = FLEXCONTROLLER_REMAP_EYELID;
  5595. // Save the eyes_updown flex for later
  5596. const char *pEyesUpDownFlexName = pCombination->GetEyesUpDownFlexName( i );
  5597. remap.m_EyesUpDownFlexName = pEyesUpDownFlexName ? pEyesUpDownFlexName : "eyes_updown";
  5598. }
  5599. else
  5600. {
  5601. switch( nRemapCount )
  5602. {
  5603. case 0:
  5604. case 1:
  5605. remap.m_RemapType = FLEXCONTROLLER_REMAP_PASSTHRU;
  5606. break;
  5607. case 2:
  5608. remap.m_RemapType = FLEXCONTROLLER_REMAP_2WAY;
  5609. break;
  5610. default:
  5611. remap.m_RemapType = FLEXCONTROLLER_REMAP_NWAY;
  5612. break;
  5613. }
  5614. }
  5615. Assert( nRemapCount != 0 );
  5616. for ( int j = 0; j < nRemapCount; ++j )
  5617. {
  5618. const char *pRemapName = pCombination->GetRawControlName( i, j );
  5619. remap.m_RawControls.AddToTail( pRemapName );
  5620. }
  5621. }
  5622. }
  5623. //-----------------------------------------------------------------------------
  5624. // Purpose:
  5625. //-----------------------------------------------------------------------------
  5626. void Option_Eyelid( int imodel )
  5627. {
  5628. char type[256];
  5629. char vtafile[256];
  5630. // type
  5631. GetToken (false);
  5632. strcpyn( type, token );
  5633. // source
  5634. GetToken (false);
  5635. strcpyn( vtafile, token );
  5636. int lowererframe = 0;
  5637. int neutralframe = 0;
  5638. int raiserframe = 0;
  5639. float lowerertarget = 0.0f;
  5640. float neutraltarget = 0.0f;
  5641. float raisertarget = 0.0f;
  5642. int lowererdesc = 0;
  5643. int neutraldesc = 0;
  5644. int raiserdesc = 0;
  5645. int basedesc;
  5646. float split = 0;
  5647. char szEyeball[64] = {""};
  5648. basedesc = g_numflexdesc;
  5649. strcpyn( g_flexdesc[g_numflexdesc++].FACS, type );
  5650. while (TokenAvailable())
  5651. {
  5652. GetToken(false);
  5653. char localdesc[256];
  5654. strcpy( localdesc, type );
  5655. strcat( localdesc, "_" );
  5656. strcat( localdesc, token );
  5657. if (stricmp( token, "lowerer") == 0)
  5658. {
  5659. GetToken (false);
  5660. lowererframe = verify_atoi( token );
  5661. GetToken (false);
  5662. lowerertarget = verify_atof( token );
  5663. lowererdesc = g_numflexdesc;
  5664. strcpyn( g_flexdesc[g_numflexdesc++].FACS, localdesc );
  5665. }
  5666. else if (stricmp( token, "neutral") == 0)
  5667. {
  5668. GetToken (false);
  5669. neutralframe = verify_atoi( token );
  5670. GetToken (false);
  5671. neutraltarget = verify_atof( token );
  5672. neutraldesc = g_numflexdesc;
  5673. strcpyn( g_flexdesc[g_numflexdesc++].FACS, localdesc );
  5674. }
  5675. else if (stricmp( token, "raiser") == 0)
  5676. {
  5677. GetToken (false);
  5678. raiserframe = verify_atoi( token );
  5679. GetToken (false);
  5680. raisertarget = verify_atof( token );
  5681. raiserdesc = g_numflexdesc;
  5682. strcpyn( g_flexdesc[g_numflexdesc++].FACS, localdesc );
  5683. }
  5684. else if (stricmp( token, "split") == 0)
  5685. {
  5686. GetToken (false);
  5687. split = verify_atof( token );
  5688. }
  5689. else if (stricmp( token, "eyeball") == 0)
  5690. {
  5691. GetToken (false);
  5692. strcpy( szEyeball, token );
  5693. }
  5694. else
  5695. {
  5696. TokenError( "unknown option: %s", token );
  5697. }
  5698. }
  5699. s_source_t *pSource = Load_Source( vtafile, "vta" );
  5700. g_flexkey[g_numflexkeys+0].source = pSource;
  5701. g_flexkey[g_numflexkeys+0].frame = lowererframe;
  5702. g_flexkey[g_numflexkeys+0].flexdesc = basedesc;
  5703. g_flexkey[g_numflexkeys+0].imodel = imodel;
  5704. g_flexkey[g_numflexkeys+0].split = split;
  5705. g_flexkey[g_numflexkeys+0].target0 = -11;
  5706. g_flexkey[g_numflexkeys+0].target1 = -10;
  5707. g_flexkey[g_numflexkeys+0].target2 = lowerertarget;
  5708. g_flexkey[g_numflexkeys+0].target3 = neutraltarget;
  5709. g_flexkey[g_numflexkeys+0].decay = 0.0;
  5710. if ( pSource->m_Animations.Count() > 0 )
  5711. {
  5712. Q_strncpy( g_flexkey[g_numflexkeys+0].animationname, pSource->m_Animations[0].animationname, sizeof(g_flexkey[g_numflexkeys+0].animationname) );
  5713. }
  5714. else
  5715. {
  5716. g_flexkey[g_numflexkeys+0].animationname[0] = 0;
  5717. }
  5718. g_flexkey[g_numflexkeys+1].source = g_flexkey[g_numflexkeys+0].source;
  5719. Q_strncpy( g_flexkey[g_numflexkeys+1].animationname, g_flexkey[g_numflexkeys+0].animationname, sizeof(g_flexkey[g_numflexkeys+1].animationname) );
  5720. g_flexkey[g_numflexkeys+1].frame = neutralframe;
  5721. g_flexkey[g_numflexkeys+1].flexdesc = basedesc;
  5722. g_flexkey[g_numflexkeys+1].imodel = imodel;
  5723. g_flexkey[g_numflexkeys+1].split = split;
  5724. g_flexkey[g_numflexkeys+1].target0 = lowerertarget;
  5725. g_flexkey[g_numflexkeys+1].target1 = neutraltarget;
  5726. g_flexkey[g_numflexkeys+1].target2 = neutraltarget;
  5727. g_flexkey[g_numflexkeys+1].target3 = raisertarget;
  5728. g_flexkey[g_numflexkeys+1].decay = 0.0;
  5729. g_flexkey[g_numflexkeys+2].source = g_flexkey[g_numflexkeys+0].source;
  5730. Q_strncpy( g_flexkey[g_numflexkeys+2].animationname, g_flexkey[g_numflexkeys+0].animationname, sizeof(g_flexkey[g_numflexkeys+2].animationname) );
  5731. g_flexkey[g_numflexkeys+2].frame = raiserframe;
  5732. g_flexkey[g_numflexkeys+2].flexdesc = basedesc;
  5733. g_flexkey[g_numflexkeys+2].imodel = imodel;
  5734. g_flexkey[g_numflexkeys+2].split = split;
  5735. g_flexkey[g_numflexkeys+2].target0 = neutraltarget;
  5736. g_flexkey[g_numflexkeys+2].target1 = raisertarget;
  5737. g_flexkey[g_numflexkeys+2].target2 = 10;
  5738. g_flexkey[g_numflexkeys+2].target3 = 11;
  5739. g_flexkey[g_numflexkeys+2].decay = 0.0;
  5740. g_numflexkeys += 3;
  5741. s_model_t *pmodel = g_model[imodel];
  5742. for (int i = 0; i < pmodel->numeyeballs; i++)
  5743. {
  5744. s_eyeball_t *peyeball = &(pmodel->eyeball[i]);
  5745. if (szEyeball[0] != '\0')
  5746. {
  5747. if (stricmp( peyeball->name, szEyeball ) != 0)
  5748. continue;
  5749. }
  5750. if (fabs( lowerertarget ) > peyeball->radius)
  5751. {
  5752. TokenError( "Eyelid \"%s\" lowerer out of range (+-%.1f)\n", type, peyeball->radius );
  5753. }
  5754. if (fabs( neutraltarget ) > peyeball->radius)
  5755. {
  5756. TokenError( "Eyelid \"%s\" neutral out of range (+-%.1f)\n", type, peyeball->radius );
  5757. }
  5758. if (fabs( raisertarget ) > peyeball->radius)
  5759. {
  5760. TokenError( "Eyelid \"%s\" raiser out of range (+-%.1f)\n", type, peyeball->radius );
  5761. }
  5762. switch( type[0] )
  5763. {
  5764. case 'u':
  5765. peyeball->upperlidflexdesc = basedesc;
  5766. peyeball->upperflexdesc[0] = lowererdesc;
  5767. peyeball->uppertarget[0] = lowerertarget;
  5768. peyeball->upperflexdesc[1] = neutraldesc;
  5769. peyeball->uppertarget[1] = neutraltarget;
  5770. peyeball->upperflexdesc[2] = raiserdesc;
  5771. peyeball->uppertarget[2] = raisertarget;
  5772. break;
  5773. case 'l':
  5774. peyeball->lowerlidflexdesc = basedesc;
  5775. peyeball->lowerflexdesc[0] = lowererdesc;
  5776. peyeball->lowertarget[0] = lowerertarget;
  5777. peyeball->lowerflexdesc[1] = neutraldesc;
  5778. peyeball->lowertarget[1] = neutraltarget;
  5779. peyeball->lowerflexdesc[2] = raiserdesc;
  5780. peyeball->lowertarget[2] = raisertarget;
  5781. break;
  5782. }
  5783. }
  5784. }
  5785. //-----------------------------------------------------------------------------
  5786. // Purpose: Returns an s_sourceanim_t * from the specified s_source_t *
  5787. // that matches the specified animation name (case insensitive)
  5788. // and is also a new style (i.e. DMX) vertex animation
  5789. //-----------------------------------------------------------------------------
  5790. const s_sourceanim_t *GetNewStyleSourceVertexAnim( s_source_t *pSource, const char *pszVertexAnimName )
  5791. {
  5792. for ( int i = 0; i < pSource->m_Animations.Count(); ++i )
  5793. {
  5794. const s_sourceanim_t *pSourceAnim = &( pSource->m_Animations[i] );
  5795. if ( !pSourceAnim || !pSourceAnim->newStyleVertexAnimations )
  5796. continue;
  5797. if ( !Q_stricmp( pszVertexAnimName, pSourceAnim->animationname ) )
  5798. return pSourceAnim;
  5799. }
  5800. return NULL;
  5801. }
  5802. //-----------------------------------------------------------------------------
  5803. // Purpose: Handle the eyelid option using a DMX instead of a VTA source
  5804. // QC Syntax: dmxeyelid <upper|lower> <source> lowerer <delta> <-0.20 neutral F00 0.19 raiser F02 0.28 righteyeball righteye lefteyeball lefteye
  5805. // e.g: dmxeyelid upper "coach_model_merged.dmx" lowerer F01 -0.20 neutral F00 0.19 raiser F02 0.28 righteyeball righteye lefteyeball lefteye
  5806. //-----------------------------------------------------------------------------
  5807. void Option_DmxEyelid( int imodel )
  5808. {
  5809. // upper | lower
  5810. const char *pszType = NULL;
  5811. GetToken( false );
  5812. if ( !Q_stricmp( "upper", token ) )
  5813. {
  5814. pszType = "upper";
  5815. }
  5816. else if ( !Q_stricmp( "lower", token ) )
  5817. {
  5818. pszType = "lower";
  5819. }
  5820. else
  5821. {
  5822. TokenError( "$model dmxeyelid, expected one of \"upper\", \"lower\"" );
  5823. return;
  5824. }
  5825. // (exr)
  5826. CUtlString sSourceFile;
  5827. GetToken( false );
  5828. sSourceFile = token;
  5829. s_source_t *pSource = Load_Source( sSourceFile.Get(), "dmx" );
  5830. if ( !pSource )
  5831. {
  5832. MdlError( "(%d) : %s: Cannot load source file \"%s\"\n", g_iLinecount, g_szLine, sSourceFile.Get() );
  5833. return;
  5834. }
  5835. enum RightLeftType_t
  5836. {
  5837. kLeft = 0,
  5838. kRight = 1,
  5839. kRightLeftTypeCount = 2
  5840. };
  5841. struct EyelidData_t
  5842. {
  5843. int m_nFlexDesc[ kRightLeftTypeCount ];
  5844. const s_sourceanim_t *m_pSourceAnim;
  5845. float m_flTarget;
  5846. const char *m_pszSuffix;
  5847. };
  5848. EyelidData_t eyelidData[3] =
  5849. {
  5850. { { -1, -1 }, NULL, 0.0f, "lowerer" },
  5851. { { -1, -1 }, NULL, 0.0f, "neutral" },
  5852. { { -1, -1 }, NULL, 0.0f, "raiser" }
  5853. };
  5854. CUtlString sRightEyeball;
  5855. CUtlString sLeftEyeball;
  5856. while ( TokenAvailable() )
  5857. {
  5858. GetToken( false );
  5859. bool bTokenHandled = false;
  5860. for ( int i = 0; i < kEyelidTypeCount; ++i )
  5861. {
  5862. if ( !Q_stricmp( token, eyelidData[i].m_pszSuffix ) )
  5863. {
  5864. bTokenHandled = true;
  5865. GetToken( false );
  5866. eyelidData[i].m_pSourceAnim = GetNewStyleSourceVertexAnim( pSource, token );
  5867. if ( eyelidData[i].m_pSourceAnim == NULL )
  5868. {
  5869. MdlError( "(%d) : %s: No DMX vertex animation named \"%s\" in source \"%s\"\n", g_iLinecount, g_szLine, token, sSourceFile.Get() );
  5870. return;
  5871. }
  5872. // target
  5873. GetToken( false );
  5874. eyelidData[i].m_flTarget = verify_atof( token );
  5875. break;
  5876. }
  5877. }
  5878. if ( bTokenHandled )
  5879. continue;
  5880. else if ( !Q_stricmp( token, "righteyeball" ) )
  5881. {
  5882. GetToken( false );
  5883. sRightEyeball = token;
  5884. }
  5885. else if ( !Q_stricmp( token, "lefteyeball" ) )
  5886. {
  5887. GetToken( false );
  5888. sLeftEyeball = token;
  5889. }
  5890. }
  5891. // Add a flexdesc for <type>_right & <type>_left
  5892. // Where <type> is "upper" or "lower"
  5893. int nRightLeftBaseDesc[kRightLeftTypeCount] = { -1, -1 };
  5894. CUtlString sRightBaseDesc = pszType;
  5895. sRightBaseDesc += "_right";
  5896. nRightLeftBaseDesc[kRight] = Add_Flexdesc( sRightBaseDesc.Get() );
  5897. for ( int i = 0; i < kEyelidTypeCount; ++i )
  5898. {
  5899. CUtlString sRightLocalDesc = sRightBaseDesc;
  5900. sRightLocalDesc += "_";
  5901. sRightLocalDesc += eyelidData[i].m_pszSuffix;
  5902. eyelidData[i].m_nFlexDesc[kRight] = Add_Flexdesc( sRightLocalDesc.Get() );
  5903. }
  5904. CUtlString sLeftBaseDesc = pszType;
  5905. sLeftBaseDesc += "_left";
  5906. nRightLeftBaseDesc[kLeft] = Add_Flexdesc( sLeftBaseDesc.Get() );
  5907. for ( int i = 0; i < kEyelidTypeCount; ++i )
  5908. {
  5909. CUtlString sLeftLocalDesc = sLeftBaseDesc;
  5910. sLeftLocalDesc += "_";
  5911. sLeftLocalDesc += eyelidData[i].m_pszSuffix;
  5912. eyelidData[i].m_nFlexDesc[kLeft] = Add_Flexdesc( sLeftLocalDesc.Get() );
  5913. }
  5914. for ( int i = 0; i < kEyelidTypeCount; ++i )
  5915. {
  5916. s_flexkey_t *pFlexKey = &g_flexkey[ g_numflexkeys ];
  5917. pFlexKey->source = pSource;
  5918. Q_strncpy( pFlexKey->animationname, eyelidData[i].m_pSourceAnim->animationname, sizeof( pFlexKey->animationname ) );
  5919. pFlexKey->frame = 0; // Currently always 0 for DMX
  5920. pFlexKey->imodel = imodel;
  5921. pFlexKey->flexdesc = nRightLeftBaseDesc[kLeft];
  5922. pFlexKey->flexpair = nRightLeftBaseDesc[kRight];
  5923. pFlexKey->split = 0.0f;
  5924. pFlexKey->decay = 1.0;
  5925. switch ( i )
  5926. {
  5927. case kLowerer:
  5928. pFlexKey->target0 = -11;
  5929. pFlexKey->target1 = -10;
  5930. pFlexKey->target2 = eyelidData[kLowerer].m_flTarget;
  5931. pFlexKey->target3 = eyelidData[kNeutral].m_flTarget;
  5932. break;
  5933. case kNeutral:
  5934. pFlexKey->target0 = eyelidData[kLowerer].m_flTarget;
  5935. pFlexKey->target1 = eyelidData[kNeutral].m_flTarget;
  5936. pFlexKey->target2 = eyelidData[kNeutral].m_flTarget;
  5937. pFlexKey->target3 = eyelidData[kRaiser].m_flTarget;
  5938. break;
  5939. case kRaiser:
  5940. pFlexKey->target0 = eyelidData[kNeutral].m_flTarget;
  5941. pFlexKey->target1 = eyelidData[kRaiser].m_flTarget;
  5942. pFlexKey->target2 = 10;
  5943. pFlexKey->target3 = 11;
  5944. break;
  5945. }
  5946. ++g_numflexkeys;
  5947. }
  5948. bool bRightOk = false;
  5949. bool bLeftOk = false;
  5950. s_model_t *pModel = g_model[imodel];
  5951. for ( int i = 0; i < pModel->numeyeballs; ++i )
  5952. {
  5953. s_eyeball_t *pEyeball = &( pModel->eyeball[i] );
  5954. if ( !pEyeball )
  5955. continue;
  5956. RightLeftType_t nRightLeftIndex = kRight;
  5957. if ( !Q_stricmp( sRightEyeball, pEyeball->name ) )
  5958. {
  5959. nRightLeftIndex = kRight;
  5960. bRightOk = true;
  5961. }
  5962. else if ( !Q_stricmp( sLeftEyeball, pEyeball->name ) )
  5963. {
  5964. nRightLeftIndex = kLeft;
  5965. bLeftOk = true;
  5966. }
  5967. else
  5968. {
  5969. MdlWarning( "Unknown Eyeball: %s\n", pEyeball->name );
  5970. continue;
  5971. }
  5972. for ( int j = 0; j < kEyelidTypeCount; ++j )
  5973. {
  5974. if ( fabs( eyelidData[j].m_flTarget ) > pEyeball->radius )
  5975. {
  5976. TokenError( "Eyelid \"%s\" %s %.1f out of range (+-%.1f)\n", pszType, eyelidData[j].m_pszSuffix, eyelidData[j].m_flTarget, pEyeball->radius );
  5977. }
  5978. }
  5979. switch( *pszType )
  5980. {
  5981. case 'u': // upper
  5982. pEyeball->upperlidflexdesc = nRightLeftBaseDesc[nRightLeftIndex];
  5983. for ( int j = 0; j < kEyelidTypeCount; ++j )
  5984. {
  5985. pEyeball->upperflexdesc[j] = eyelidData[j].m_nFlexDesc[nRightLeftIndex];
  5986. pEyeball->uppertarget[j] = eyelidData[j].m_flTarget;
  5987. }
  5988. break;
  5989. case 'l': // lower
  5990. pEyeball->lowerlidflexdesc = nRightLeftBaseDesc[nRightLeftIndex];
  5991. for ( int j = 0; j < kEyelidTypeCount; ++j )
  5992. {
  5993. pEyeball->lowerflexdesc[j] = eyelidData[j].m_nFlexDesc[nRightLeftIndex];
  5994. pEyeball->lowertarget[j] = eyelidData[j].m_flTarget;
  5995. }
  5996. break;
  5997. default:
  5998. Assert(0);
  5999. break;
  6000. }
  6001. }
  6002. if ( !bRightOk )
  6003. {
  6004. TokenError( "Could not find right eye \"%s\"\n", sRightEyeball.Get() );
  6005. }
  6006. if ( !bLeftOk )
  6007. {
  6008. TokenError( "Could not find left eye \"%s\"\n", sRightEyeball.Get() );
  6009. }
  6010. }
  6011. /*
  6012. =================
  6013. =================
  6014. */
  6015. int Option_Mouth( s_model_t *pmodel )
  6016. {
  6017. // index
  6018. GetToken (false);
  6019. int index = verify_atoi( token );
  6020. if (index >= g_nummouths)
  6021. g_nummouths = index + 1;
  6022. // flex controller name
  6023. GetToken (false);
  6024. g_mouth[index].flexdesc = Add_Flexdesc( token );
  6025. // bone name
  6026. GetToken (false);
  6027. strcpyn( g_mouth[index].bonename, token );
  6028. // vector
  6029. GetToken (false);
  6030. g_mouth[index].forward[0] = verify_atof( token );
  6031. GetToken (false);
  6032. g_mouth[index].forward[1] = verify_atof( token );
  6033. GetToken (false);
  6034. g_mouth[index].forward[2] = verify_atof( token );
  6035. return 0;
  6036. }
  6037. void Option_Flexcontroller( s_model_t *pmodel )
  6038. {
  6039. char type[256];
  6040. float range_min = 0.0f;
  6041. float range_max = 1.0f;
  6042. // g_flex
  6043. GetToken (false);
  6044. strcpy( type, token );
  6045. while (TokenAvailable())
  6046. {
  6047. GetToken(false);
  6048. if (stricmp( token, "range") == 0)
  6049. {
  6050. GetToken(false);
  6051. range_min = verify_atof( token );
  6052. GetToken(false);
  6053. range_max = verify_atof( token );
  6054. }
  6055. else
  6056. {
  6057. if (g_numflexcontrollers >= MAXSTUDIOFLEXCTRL)
  6058. {
  6059. TokenError( "Too many flex controllers, max %d\n", MAXSTUDIOFLEXCTRL );
  6060. }
  6061. strcpyn( g_flexcontroller[g_numflexcontrollers].name, token );
  6062. strcpyn( g_flexcontroller[g_numflexcontrollers].type, type );
  6063. g_flexcontroller[g_numflexcontrollers].min = range_min;
  6064. g_flexcontroller[g_numflexcontrollers].max = range_max;
  6065. g_numflexcontrollers++;
  6066. }
  6067. }
  6068. // this needs to be per model.
  6069. }
  6070. void Option_NoAutoDMXRules( s_source_t *pSource )
  6071. {
  6072. // zero out the automatic flex controllers
  6073. g_numflexcontrollers = 0;
  6074. pSource->bNoAutoDMXRules = true;
  6075. }
  6076. void PrintFlexrule( s_flexrule_t *pRule )
  6077. {
  6078. printf("%s = ", g_flexdesc[pRule->flex].FACS );
  6079. for ( int i = 0; i < pRule->numops; i++)
  6080. {
  6081. switch( pRule->op[i].op )
  6082. {
  6083. case STUDIO_CONST: printf("%f ", pRule->op[i].d.value ); break;
  6084. case STUDIO_FETCH1: printf("%s ", g_flexcontroller[pRule->op[i].d.index].name ); break;
  6085. case STUDIO_FETCH2: printf("[%d] ", pRule->op[i].d.index ); break;
  6086. case STUDIO_ADD: printf("+ "); break;
  6087. case STUDIO_SUB: printf("- "); break;
  6088. case STUDIO_MUL: printf("* "); break;
  6089. case STUDIO_DIV: printf("/ "); break;
  6090. case STUDIO_NEG: printf("neg "); break;
  6091. case STUDIO_MAX: printf("max "); break;
  6092. case STUDIO_MIN: printf("min "); break;
  6093. case STUDIO_COMMA: printf(", "); break; // error
  6094. case STUDIO_OPEN: printf("( " ); break; // error
  6095. case STUDIO_CLOSE: printf(") " ); break; // error
  6096. case STUDIO_2WAY_0: printf("2WAY_0 " ); break;
  6097. case STUDIO_2WAY_1: printf("2WAY_1 " ); break;
  6098. case STUDIO_NWAY: printf("NWAY " ); break;
  6099. case STUDIO_COMBO: printf("COMBO " ); break;
  6100. case STUDIO_DOMINATE: printf("DOMINATE " ); break;
  6101. case STUDIO_DME_LOWER_EYELID: printf("DME_LOWER_EYELID " ); break;
  6102. case STUDIO_DME_UPPER_EYELID: printf("DME_UPPER_EYELID " ); break;
  6103. default:
  6104. printf("err%d ", pRule->op[i].op ); break;
  6105. }
  6106. }
  6107. printf("\n");
  6108. }
  6109. void Option_Flexrule( s_model_t * /* pmodel */, const char *name )
  6110. {
  6111. int precedence[32];
  6112. precedence[ STUDIO_CONST ] = 0;
  6113. precedence[ STUDIO_FETCH1 ] = 0;
  6114. precedence[ STUDIO_FETCH2 ] = 0;
  6115. precedence[ STUDIO_ADD ] = 1;
  6116. precedence[ STUDIO_SUB ] = 1;
  6117. precedence[ STUDIO_MUL ] = 2;
  6118. precedence[ STUDIO_DIV ] = 2;
  6119. precedence[ STUDIO_NEG ] = 4;
  6120. precedence[ STUDIO_EXP ] = 3;
  6121. precedence[ STUDIO_OPEN ] = 0; // only used in token parsing
  6122. precedence[ STUDIO_CLOSE ] = 0;
  6123. precedence[ STUDIO_COMMA ] = 0;
  6124. precedence[ STUDIO_MAX ] = 5;
  6125. precedence[ STUDIO_MIN ] = 5;
  6126. s_flexop_t stream[MAX_OPS];
  6127. int i = 0;
  6128. s_flexop_t stack[MAX_OPS];
  6129. int j = 0;
  6130. int k = 0;
  6131. s_flexrule_t *pRule = &g_flexrule[g_numflexrules++];
  6132. if (g_numflexrules > MAXSTUDIOFLEXRULES)
  6133. {
  6134. TokenError( "Too many flex rules (max %d)\n", MAXSTUDIOFLEXRULES );
  6135. }
  6136. int flexdesc;
  6137. for ( flexdesc = 0; flexdesc < g_numflexdesc; flexdesc++)
  6138. {
  6139. if (stricmp( name, g_flexdesc[flexdesc].FACS ) == 0)
  6140. {
  6141. break;
  6142. }
  6143. }
  6144. if (flexdesc >= g_numflexdesc)
  6145. {
  6146. TokenError( "Rule for unknown flex %s\n", name );
  6147. }
  6148. pRule->flex = flexdesc;
  6149. pRule->numops = 0;
  6150. // =
  6151. GetToken(false);
  6152. // parse all the tokens
  6153. bool linecontinue = false;
  6154. while ( linecontinue || TokenAvailable())
  6155. {
  6156. GetExprToken(linecontinue);
  6157. linecontinue = false;
  6158. if ( token[0] == '\\' )
  6159. {
  6160. if (!GetToken(false) || token[0] != '\\')
  6161. {
  6162. TokenError( "unknown expression token '\\%s\n", token );
  6163. }
  6164. linecontinue = true;
  6165. }
  6166. else if ( token[0] == '(' )
  6167. {
  6168. stream[i++].op = STUDIO_OPEN;
  6169. }
  6170. else if ( token[0] == ')' )
  6171. {
  6172. stream[i++].op = STUDIO_CLOSE;
  6173. }
  6174. else if ( token[0] == '+' )
  6175. {
  6176. stream[i++].op = STUDIO_ADD;
  6177. }
  6178. else if ( token[0] == '-' )
  6179. {
  6180. stream[i].op = STUDIO_SUB;
  6181. if (i > 0)
  6182. {
  6183. switch( stream[i-1].op )
  6184. {
  6185. case STUDIO_OPEN:
  6186. case STUDIO_ADD:
  6187. case STUDIO_SUB:
  6188. case STUDIO_MUL:
  6189. case STUDIO_DIV:
  6190. case STUDIO_COMMA:
  6191. // it's a unary if it's preceded by a "(+-*/,"?
  6192. stream[i].op = STUDIO_NEG;
  6193. break;
  6194. }
  6195. }
  6196. i++;
  6197. }
  6198. else if ( token[0] == '*' )
  6199. {
  6200. stream[i++].op = STUDIO_MUL;
  6201. }
  6202. else if ( token[0] == '/' )
  6203. {
  6204. stream[i++].op = STUDIO_DIV;
  6205. }
  6206. else if ( V_isdigit( token[0] ))
  6207. {
  6208. stream[i].op = STUDIO_CONST;
  6209. stream[i++].d.value = verify_atof( token );
  6210. }
  6211. else if ( token[0] == ',' )
  6212. {
  6213. stream[i++].op = STUDIO_COMMA;
  6214. }
  6215. else if ( stricmp( token, "max" ) == 0)
  6216. {
  6217. stream[i++].op = STUDIO_MAX;
  6218. }
  6219. else if ( stricmp( token, "min" ) == 0)
  6220. {
  6221. stream[i++].op = STUDIO_MIN;
  6222. }
  6223. else
  6224. {
  6225. if (token[0] == '%')
  6226. {
  6227. GetExprToken(false);
  6228. for (k = 0; k < g_numflexdesc; k++)
  6229. {
  6230. if (stricmp( token, g_flexdesc[k].FACS ) == 0)
  6231. {
  6232. stream[i].op = STUDIO_FETCH2;
  6233. stream[i++].d.index = k;
  6234. break;
  6235. }
  6236. }
  6237. if (k >= g_numflexdesc)
  6238. {
  6239. TokenError( "unknown flex %s\n", token );
  6240. }
  6241. }
  6242. else
  6243. {
  6244. for (k = 0; k < g_numflexcontrollers; k++)
  6245. {
  6246. if (stricmp( token, g_flexcontroller[k].name ) == 0)
  6247. {
  6248. stream[i].op = STUDIO_FETCH1;
  6249. stream[i++].d.index = k;
  6250. break;
  6251. }
  6252. }
  6253. if (k >= g_numflexcontrollers)
  6254. {
  6255. TokenError( "unknown controller %s\n", token );
  6256. }
  6257. }
  6258. }
  6259. }
  6260. if (i > MAX_OPS)
  6261. {
  6262. TokenError("expression %s too complicated\n", g_flexdesc[pRule->flex].FACS );
  6263. }
  6264. if (0)
  6265. {
  6266. printf("%s = ", g_flexdesc[pRule->flex].FACS );
  6267. for ( k = 0; k < i; k++)
  6268. {
  6269. switch( stream[k].op )
  6270. {
  6271. case STUDIO_CONST: printf("%f ", stream[k].d.value ); break;
  6272. case STUDIO_FETCH1: printf("%s ", g_flexcontroller[stream[k].d.index].name ); break;
  6273. case STUDIO_FETCH2: printf("[%d] ", stream[k].d.index ); break;
  6274. case STUDIO_ADD: printf("+ "); break;
  6275. case STUDIO_SUB: printf("- "); break;
  6276. case STUDIO_MUL: printf("* "); break;
  6277. case STUDIO_DIV: printf("/ "); break;
  6278. case STUDIO_NEG: printf("neg "); break;
  6279. case STUDIO_MAX: printf("max "); break;
  6280. case STUDIO_MIN: printf("min "); break;
  6281. case STUDIO_COMMA: printf(", "); break; // error
  6282. case STUDIO_OPEN: printf("( " ); break; // error
  6283. case STUDIO_CLOSE: printf(") " ); break; // error
  6284. default:
  6285. printf("err%d ", stream[k].op ); break;
  6286. }
  6287. }
  6288. printf("\n");
  6289. // exit(1);
  6290. }
  6291. j = 0;
  6292. for (k = 0; k < i; k++)
  6293. {
  6294. if (j >= MAX_OPS)
  6295. {
  6296. TokenError("expression %s too complicated\n", g_flexdesc[pRule->flex].FACS );
  6297. }
  6298. switch( stream[k].op )
  6299. {
  6300. case STUDIO_CONST:
  6301. case STUDIO_FETCH1:
  6302. case STUDIO_FETCH2:
  6303. pRule->op[pRule->numops++] = stream[k];
  6304. break;
  6305. case STUDIO_OPEN:
  6306. stack[j++] = stream[k];
  6307. break;
  6308. case STUDIO_CLOSE:
  6309. // pop all operators off of the stack until an open paren
  6310. while (j > 0 && stack[j-1].op != STUDIO_OPEN)
  6311. {
  6312. pRule->op[pRule->numops++] = stack[j-1];
  6313. j--;
  6314. }
  6315. if (j == 0)
  6316. {
  6317. TokenError( "unmatched closed parentheses\n" );
  6318. }
  6319. if (j > 0)
  6320. j--;
  6321. break;
  6322. case STUDIO_COMMA:
  6323. // pop all operators off of the stack until an open paren
  6324. while (j > 0 && stack[j-1].op != STUDIO_OPEN)
  6325. {
  6326. pRule->op[pRule->numops++] = stack[j-1];
  6327. j--;
  6328. }
  6329. // push operator onto the stack
  6330. stack[j++] = stream[k];
  6331. break;
  6332. case STUDIO_ADD:
  6333. case STUDIO_SUB:
  6334. case STUDIO_MUL:
  6335. case STUDIO_DIV:
  6336. // pop all operators off of the stack that have equal or higher precedence
  6337. while (j > 0 && precedence[stream[k].op] <= precedence[stack[j-1].op])
  6338. {
  6339. pRule->op[pRule->numops++] = stack[j-1];
  6340. j--;
  6341. }
  6342. // push operator onto the stack
  6343. stack[j++] = stream[k];
  6344. break;
  6345. case STUDIO_NEG:
  6346. if (stream[k+1].op == STUDIO_CONST)
  6347. {
  6348. // change sign of constant, skip op
  6349. stream[k+1].d.value = -stream[k+1].d.value;
  6350. }
  6351. else
  6352. {
  6353. // push operator onto the stack
  6354. stack[j++] = stream[k];
  6355. }
  6356. break;
  6357. case STUDIO_MAX:
  6358. case STUDIO_MIN:
  6359. // push operator onto the stack
  6360. stack[j++] = stream[k];
  6361. break;
  6362. }
  6363. if (pRule->numops >= MAX_OPS)
  6364. TokenError("expression for \"%s\" too complicated\n", g_flexdesc[pRule->flex].FACS );
  6365. }
  6366. // pop all operators off of the stack
  6367. while (j > 0)
  6368. {
  6369. pRule->op[pRule->numops++] = stack[j-1];
  6370. j--;
  6371. if (pRule->numops >= MAX_OPS)
  6372. TokenError("expression for \"%s\" too complicated\n", g_flexdesc[pRule->flex].FACS );
  6373. }
  6374. // reprocess the operands, eating commas for all functions
  6375. int numCommas = 0;
  6376. j = 0;
  6377. for (k = 0; k < pRule->numops; k++)
  6378. {
  6379. switch( pRule->op[k].op )
  6380. {
  6381. case STUDIO_MAX:
  6382. case STUDIO_MIN:
  6383. if (pRule->op[j-1].op != STUDIO_COMMA)
  6384. {
  6385. TokenError( "missing comma\n");
  6386. }
  6387. // eat the comma operator
  6388. numCommas--;
  6389. pRule->op[j-1] = pRule->op[k];
  6390. break;
  6391. case STUDIO_COMMA:
  6392. numCommas++;
  6393. pRule->op[j++] = pRule->op[k];
  6394. break;
  6395. default:
  6396. pRule->op[j++] = pRule->op[k];
  6397. break;
  6398. }
  6399. }
  6400. pRule->numops = j;
  6401. if (numCommas != 0)
  6402. {
  6403. TokenError( "too many comma's\n" );
  6404. }
  6405. if (pRule->numops > MAX_OPS)
  6406. {
  6407. TokenError("expression %s too complicated\n", g_flexdesc[pRule->flex].FACS );
  6408. }
  6409. if (0)
  6410. {
  6411. PrintFlexrule( pRule );
  6412. }
  6413. }
  6414. //-----------------------------------------------------------------------------
  6415. // Purpose:
  6416. //-----------------------------------------------------------------------------
  6417. void Cmd_Model( )
  6418. {
  6419. g_model[g_nummodels] = (s_model_t *)calloc( 1, sizeof( s_model_t ) );
  6420. // name
  6421. if (!GetToken(false))
  6422. return;
  6423. strcpyn( g_model[g_nummodels]->name, token );
  6424. // fake g_bodypart stuff
  6425. g_bodypart[g_numbodyparts].base = 1;
  6426. if (g_numbodyparts != 0)
  6427. {
  6428. g_bodypart[g_numbodyparts].base = g_bodypart[g_numbodyparts-1].base * g_bodypart[g_numbodyparts-1].nummodels;
  6429. }
  6430. strcpyn( g_bodypart[g_numbodyparts].name, token );
  6431. g_bodypart[g_numbodyparts].pmodel[g_bodypart[g_numbodyparts].nummodels] = g_model[g_nummodels];
  6432. g_bodypart[g_numbodyparts].nummodels = 1;
  6433. g_numbodyparts++;
  6434. Option_Studio( g_model[g_nummodels] );
  6435. if ( g_model[g_nummodels]->source )
  6436. {
  6437. // Body command should add any flex commands in the source loaded
  6438. AddBodyFlexData( g_model[g_nummodels]->source, g_nummodels );
  6439. AddBodyAttachments( g_model[g_nummodels]->source );
  6440. }
  6441. int depth = 0;
  6442. while (1)
  6443. {
  6444. char FAC[256], vtafile[256];
  6445. if (depth > 0)
  6446. {
  6447. if( !GetToken(true) )
  6448. break;
  6449. }
  6450. else
  6451. {
  6452. if ( !TokenAvailable() )
  6453. {
  6454. break;
  6455. }
  6456. else
  6457. {
  6458. GetToken (false);
  6459. }
  6460. }
  6461. if ( endofscript )
  6462. {
  6463. if (depth != 0)
  6464. {
  6465. TokenError("missing }\n" );
  6466. }
  6467. return;
  6468. }
  6469. if ( !Q_stricmp("{", token ) )
  6470. {
  6471. depth++;
  6472. }
  6473. else if ( !Q_stricmp("}", token ) )
  6474. {
  6475. depth--;
  6476. }
  6477. else if ( !Q_stricmp( "eyeball", token ) )
  6478. {
  6479. Option_Eyeball( g_model[g_nummodels] );
  6480. }
  6481. else if ( !Q_stricmp( "eyelid", token ) )
  6482. {
  6483. Option_Eyelid( g_nummodels );
  6484. }
  6485. else if ( !Q_stricmp( "dmxeyelid", token ) )
  6486. {
  6487. Option_DmxEyelid( g_nummodels );
  6488. }
  6489. else if ( !V_stricmp( "vcafile", token ) )
  6490. {
  6491. // vertex cache animation file
  6492. GetToken( false ); // file
  6493. Option_VertexCacheAnimationFile( token, g_nummodels );
  6494. }
  6495. else if ( !Q_stricmp( "flex", token ) )
  6496. {
  6497. // g_flex
  6498. GetToken (false);
  6499. strcpy( FAC, token );
  6500. if (depth == 0)
  6501. {
  6502. // file
  6503. GetToken (false);
  6504. strcpy( vtafile, token );
  6505. }
  6506. Option_Flex( FAC, vtafile, g_nummodels, 0.0 ); // FIXME: this needs to point to a model used, not loaded!!!
  6507. }
  6508. else if ( !Q_stricmp( "flexpair", token ) )
  6509. {
  6510. // g_flex
  6511. GetToken (false);
  6512. strcpy( FAC, token );
  6513. GetToken( false );
  6514. float split = atof( token );
  6515. if (depth == 0)
  6516. {
  6517. // file
  6518. GetToken (false);
  6519. strcpy( vtafile, token );
  6520. }
  6521. Option_Flex( FAC, vtafile, g_nummodels, split ); // FIXME: this needs to point to a model used, not loaded!!!
  6522. }
  6523. else if ( !Q_stricmp( "defaultflex", token ) )
  6524. {
  6525. if (depth == 0)
  6526. {
  6527. // file
  6528. GetToken (false);
  6529. strcpy( vtafile, token );
  6530. }
  6531. // g_flex
  6532. Option_Flex( "default", vtafile, g_nummodels, 0.0 ); // FIXME: this needs to point to a model used, not loaded!!!
  6533. g_defaultflexkey = &g_flexkey[g_numflexkeys-1];
  6534. }
  6535. else if ( !Q_stricmp( "flexfile", token ) )
  6536. {
  6537. // file
  6538. GetToken (false);
  6539. strcpy( vtafile, token );
  6540. }
  6541. else if ( !Q_stricmp( "localvar", token ) )
  6542. {
  6543. while (TokenAvailable())
  6544. {
  6545. GetToken( false );
  6546. Add_Flexdesc( token );
  6547. }
  6548. }
  6549. else if ( !Q_stricmp( "mouth", token ) )
  6550. {
  6551. Option_Mouth( g_model[g_nummodels] );
  6552. }
  6553. else if ( !Q_stricmp( "flexcontroller", token ) )
  6554. {
  6555. Option_Flexcontroller( g_model[g_nummodels] );
  6556. }
  6557. else if ( token[0] == '%' )
  6558. {
  6559. Option_Flexrule( g_model[g_nummodels], &token[1] );
  6560. }
  6561. else if ( !Q_stricmp("attachment", token ) )
  6562. {
  6563. // Option_Attachment( g_model[g_nummodels] );
  6564. }
  6565. else if ( !Q_stricmp( token, "spherenormals" ) )
  6566. {
  6567. Option_Spherenormals( g_model[g_nummodels]->source );
  6568. }
  6569. else if ( !Q_stricmp( token, "noautodmxrules" ) )
  6570. {
  6571. Option_NoAutoDMXRules( g_model[g_nummodels]->source );
  6572. }
  6573. else
  6574. {
  6575. TokenError( "unknown model option \"%s\"\n", token );
  6576. }
  6577. if (depth < 0)
  6578. {
  6579. TokenError("missing {\n");
  6580. }
  6581. };
  6582. if ( ! g_model[ g_nummodels ]->source->bNoAutoDMXRules )
  6583. {
  6584. // Actually connect up the expressions between the Dme Flex Controllers & Flex Descriptors
  6585. // In case there was data added by some other eyeball command (like eyelid)
  6586. AddBodyFlexRules( g_model[ g_nummodels ]->source );
  6587. }
  6588. g_nummodels++;
  6589. }
  6590. void Cmd_FakeVTA( void )
  6591. {
  6592. int depth = 0;
  6593. GetToken( false );
  6594. s_source_t *psource = (s_source_t *)calloc( 1, sizeof( s_source_t ) );
  6595. g_source[g_numsources] = psource;
  6596. strcpyn( g_source[g_numsources]->filename, token );
  6597. g_numsources++;
  6598. while (1)
  6599. {
  6600. if (depth > 0)
  6601. {
  6602. if(!GetToken(true))
  6603. {
  6604. break;
  6605. }
  6606. }
  6607. else
  6608. {
  6609. if (!TokenAvailable())
  6610. {
  6611. break;
  6612. }
  6613. else
  6614. {
  6615. GetToken (false);
  6616. }
  6617. }
  6618. if (endofscript)
  6619. {
  6620. if (depth != 0)
  6621. {
  6622. TokenError("missing }\n" );
  6623. }
  6624. return;
  6625. }
  6626. if (stricmp("{", token ) == 0)
  6627. {
  6628. depth++;
  6629. }
  6630. else if (stricmp("}", token ) == 0)
  6631. {
  6632. depth--;
  6633. }
  6634. else if (stricmp("appendvta", token ) == 0)
  6635. {
  6636. char filename[256];
  6637. // file
  6638. GetToken (false);
  6639. strcpy( filename, token );
  6640. GetToken( false );
  6641. int frame = verify_atoi( token );
  6642. AppendVTAtoOBJ( psource, filename, frame );
  6643. }
  6644. }
  6645. }
  6646. //-----------------------------------------------------------------------------
  6647. // Purpose:
  6648. //-----------------------------------------------------------------------------
  6649. void Cmd_IKChain( )
  6650. {
  6651. if (!GetToken(false))
  6652. return;
  6653. int i;
  6654. for ( i = 0; i < g_numikchains; i++)
  6655. {
  6656. if (stricmp( token, g_ikchain[i].name ) == 0)
  6657. {
  6658. break;
  6659. }
  6660. }
  6661. if (i < g_numikchains)
  6662. {
  6663. if (!g_quiet)
  6664. {
  6665. printf("duplicate ikchain \"%s\" ignored\n", token );
  6666. }
  6667. while (TokenAvailable())
  6668. {
  6669. GetToken(false);
  6670. }
  6671. return;
  6672. }
  6673. strcpyn( g_ikchain[g_numikchains].name, token );
  6674. GetToken(false);
  6675. strcpyn( g_ikchain[g_numikchains].bonename, token );
  6676. g_ikchain[g_numikchains].axis = STUDIO_Z;
  6677. g_ikchain[g_numikchains].value = 0.0;
  6678. g_ikchain[g_numikchains].height = 18.0;
  6679. g_ikchain[g_numikchains].floor = 0.0;
  6680. g_ikchain[g_numikchains].radius = 0.0;
  6681. while (TokenAvailable())
  6682. {
  6683. GetToken(false);
  6684. if (lookupControl( token ) != -1)
  6685. {
  6686. g_ikchain[g_numikchains].axis = lookupControl( token );
  6687. GetToken(false);
  6688. g_ikchain[g_numikchains].value = verify_atof( token );
  6689. }
  6690. else if (stricmp( "height", token ) == 0)
  6691. {
  6692. GetToken(false);
  6693. g_ikchain[g_numikchains].height = verify_atof( token );
  6694. }
  6695. else if (stricmp( "pad", token ) == 0)
  6696. {
  6697. GetToken(false);
  6698. g_ikchain[g_numikchains].radius = verify_atof( token ) / 2.0;
  6699. }
  6700. else if (stricmp( "floor", token ) == 0)
  6701. {
  6702. GetToken(false);
  6703. g_ikchain[g_numikchains].floor = verify_atof( token );
  6704. }
  6705. else if (stricmp( "knee", token ) == 0)
  6706. {
  6707. GetToken(false);
  6708. g_ikchain[g_numikchains].link[0].kneeDir.x = verify_atof( token );
  6709. GetToken(false);
  6710. g_ikchain[g_numikchains].link[0].kneeDir.y = verify_atof( token );
  6711. GetToken(false);
  6712. g_ikchain[g_numikchains].link[0].kneeDir.z = verify_atof( token );
  6713. }
  6714. else if (stricmp( "center", token ) == 0)
  6715. {
  6716. GetToken(false);
  6717. g_ikchain[g_numikchains].center.x = verify_atof( token );
  6718. GetToken(false);
  6719. g_ikchain[g_numikchains].center.y = verify_atof( token );
  6720. GetToken(false);
  6721. g_ikchain[g_numikchains].center.z = verify_atof( token );
  6722. }
  6723. }
  6724. g_numikchains++;
  6725. }
  6726. //-----------------------------------------------------------------------------
  6727. // Purpose:
  6728. //-----------------------------------------------------------------------------
  6729. void Cmd_IKAutoplayLock( )
  6730. {
  6731. GetToken(false);
  6732. strcpyn( g_ikautoplaylock[g_numikautoplaylocks].name, token );
  6733. GetToken(false);
  6734. g_ikautoplaylock[g_numikautoplaylocks].flPosWeight = verify_atof( token );
  6735. GetToken(false);
  6736. g_ikautoplaylock[g_numikautoplaylocks].flLocalQWeight = verify_atof( token );
  6737. g_numikautoplaylocks++;
  6738. }
  6739. //-----------------------------------------------------------------------------
  6740. // Purpose:
  6741. //-----------------------------------------------------------------------------
  6742. void Cmd_Root ()
  6743. {
  6744. if (GetToken (false))
  6745. {
  6746. strcpyn( rootname, token );
  6747. }
  6748. }
  6749. //-----------------------------------------------------------------------------
  6750. // Purpose:
  6751. //-----------------------------------------------------------------------------
  6752. void Cmd_Controller (void)
  6753. {
  6754. if (GetToken (false))
  6755. {
  6756. if (!stricmp("mouth",token))
  6757. {
  6758. g_bonecontroller[g_numbonecontrollers].inputfield = 4;
  6759. }
  6760. else
  6761. {
  6762. g_bonecontroller[g_numbonecontrollers].inputfield = verify_atoi(token);
  6763. }
  6764. if (GetToken(false))
  6765. {
  6766. strcpyn( g_bonecontroller[g_numbonecontrollers].name, token );
  6767. GetToken(false);
  6768. if ((g_bonecontroller[g_numbonecontrollers].type = lookupControl(token)) == -1)
  6769. {
  6770. MdlWarning("unknown g_bonecontroller type '%s'\n", token );
  6771. return;
  6772. }
  6773. GetToken(false);
  6774. g_bonecontroller[g_numbonecontrollers].start = verify_atof( token );
  6775. GetToken(false);
  6776. g_bonecontroller[g_numbonecontrollers].end = verify_atof( token );
  6777. if (g_bonecontroller[g_numbonecontrollers].type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
  6778. {
  6779. if (((int)(g_bonecontroller[g_numbonecontrollers].start + 360) % 360) == ((int)(g_bonecontroller[g_numbonecontrollers].end + 360) % 360))
  6780. {
  6781. g_bonecontroller[g_numbonecontrollers].type |= STUDIO_RLOOP;
  6782. }
  6783. }
  6784. g_numbonecontrollers++;
  6785. }
  6786. }
  6787. }
  6788. //-----------------------------------------------------------------------------
  6789. // Purpose:
  6790. //-----------------------------------------------------------------------------
  6791. // Debugging function that enumerate all a models bones to stdout.
  6792. static void SpewBones()
  6793. {
  6794. MdlWarning("g_numbones %i\n",g_numbones);
  6795. for ( int i = g_numbones; --i >= 0; )
  6796. {
  6797. printf("%s\n",g_bonetable[i].name);
  6798. }
  6799. }
  6800. //-----------------------------------------------------------------------------
  6801. // Purpose:
  6802. //-----------------------------------------------------------------------------
  6803. void Cmd_ScreenAlign ( void )
  6804. {
  6805. if (GetToken (false))
  6806. {
  6807. Assert( g_numscreenalignedbones < MAXSTUDIOSRCBONES );
  6808. strcpyn( g_screenalignedbone[g_numscreenalignedbones].name, token );
  6809. g_screenalignedbone[g_numscreenalignedbones].flags = BONE_SCREEN_ALIGN_SPHERE;
  6810. if( GetToken( false ) )
  6811. {
  6812. if( !stricmp( "sphere", token ) )
  6813. {
  6814. g_screenalignedbone[g_numscreenalignedbones].flags = BONE_SCREEN_ALIGN_SPHERE;
  6815. }
  6816. else if( !stricmp( "cylinder", token ) )
  6817. {
  6818. g_screenalignedbone[g_numscreenalignedbones].flags = BONE_SCREEN_ALIGN_CYLINDER;
  6819. }
  6820. }
  6821. g_numscreenalignedbones++;
  6822. } else
  6823. {
  6824. TokenError( "$screenalign: expected bone name\n" );
  6825. }
  6826. }
  6827. //-----------------------------------------------------------------------------
  6828. // Purpose:
  6829. //-----------------------------------------------------------------------------
  6830. void Cmd_WorldAlign ( void )
  6831. {
  6832. if (GetToken (false))
  6833. {
  6834. Assert( g_numworldalignedbones < MAXSTUDIOSRCBONES );
  6835. strcpyn( g_worldalignedbone[g_numworldalignedbones].name, token );
  6836. g_worldalignedbone[g_numworldalignedbones].flags = BONE_WORLD_ALIGN;
  6837. g_numworldalignedbones++;
  6838. } else
  6839. {
  6840. TokenError( "$worldalign: expected bone name\n" );
  6841. }
  6842. }
  6843. //-----------------------------------------------------------------------------
  6844. // Purpose:
  6845. //-----------------------------------------------------------------------------
  6846. void Cmd_BBox (void)
  6847. {
  6848. GetToken (false);
  6849. bbox[0][0] = verify_atof( token );
  6850. GetToken (false);
  6851. bbox[0][1] = verify_atof( token );
  6852. GetToken (false);
  6853. bbox[0][2] = verify_atof( token );
  6854. GetToken (false);
  6855. bbox[1][0] = verify_atof( token );
  6856. GetToken (false);
  6857. bbox[1][1] = verify_atof( token );
  6858. GetToken (false);
  6859. bbox[1][2] = verify_atof( token );
  6860. g_wrotebbox = true;
  6861. }
  6862. //-----------------------------------------------------------------------------
  6863. // Purpose:
  6864. //-----------------------------------------------------------------------------
  6865. void Cmd_BBoxOnlyVerts (void)
  6866. {
  6867. g_bboxonlyverts = true;
  6868. }
  6869. //-----------------------------------------------------------------------------
  6870. // Purpose:
  6871. //-----------------------------------------------------------------------------
  6872. void Cmd_CBox (void)
  6873. {
  6874. GetToken (false);
  6875. cbox[0][0] = verify_atof( token );
  6876. GetToken (false);
  6877. cbox[0][1] = verify_atof( token );
  6878. GetToken (false);
  6879. cbox[0][2] = verify_atof( token );
  6880. GetToken (false);
  6881. cbox[1][0] = verify_atof( token );
  6882. GetToken (false);
  6883. cbox[1][1] = verify_atof( token );
  6884. GetToken (false);
  6885. cbox[1][2] = verify_atof( token );
  6886. g_wrotecbox = true;
  6887. }
  6888. //-----------------------------------------------------------------------------
  6889. // Purpose:
  6890. //-----------------------------------------------------------------------------
  6891. void Cmd_Gamma (void)
  6892. {
  6893. GetToken (false);
  6894. g_gamma = verify_atof( token );
  6895. }
  6896. //-----------------------------------------------------------------------------
  6897. // Purpose:
  6898. //-----------------------------------------------------------------------------
  6899. void Cmd_TextureGroup( )
  6900. {
  6901. if( g_bCreateMakefile )
  6902. {
  6903. return;
  6904. }
  6905. int i;
  6906. int depth = 0;
  6907. int index = 0;
  6908. int group = 0;
  6909. if (!GetToken(false))
  6910. return;
  6911. if (g_numskinref == 0)
  6912. g_numskinref = g_numtextures;
  6913. while (1)
  6914. {
  6915. if(!GetToken(true))
  6916. {
  6917. break;
  6918. }
  6919. if (endofscript)
  6920. {
  6921. if (depth != 0)
  6922. {
  6923. TokenError("missing }\n" );
  6924. }
  6925. return;
  6926. }
  6927. if (token[0] == '{')
  6928. {
  6929. depth++;
  6930. }
  6931. else if (token[0] == '}')
  6932. {
  6933. depth--;
  6934. if (depth == 0)
  6935. break;
  6936. group++;
  6937. index = 0;
  6938. }
  6939. else if (depth == 2)
  6940. {
  6941. i = UseTextureAsMaterial( LookupTexture( token ) );
  6942. g_texturegroup[g_numtexturegroups][group][index] = i;
  6943. if (group != 0)
  6944. g_texture[i].parent = g_texturegroup[g_numtexturegroups][0][index];
  6945. index++;
  6946. g_numtexturereps[g_numtexturegroups] = index;
  6947. g_numtexturelayers[g_numtexturegroups] = group + 1;
  6948. }
  6949. }
  6950. g_numtexturegroups++;
  6951. }
  6952. //-----------------------------------------------------------------------------
  6953. // Purpose:
  6954. //-----------------------------------------------------------------------------
  6955. void Cmd_Hitgroup( )
  6956. {
  6957. GetToken (false);
  6958. g_hitgroup[g_numhitgroups].group = verify_atoi( token );
  6959. GetToken (false);
  6960. strcpyn( g_hitgroup[g_numhitgroups].name, token );
  6961. g_numhitgroups++;
  6962. }
  6963. //-----------------------------------------------------------------------------
  6964. // Purpose:
  6965. //-----------------------------------------------------------------------------
  6966. void Cmd_Hitbox( )
  6967. {
  6968. bool autogenerated = false;
  6969. if ( g_hitboxsets.Count() == 0 )
  6970. {
  6971. g_hitboxsets.AddToTail();
  6972. autogenerated = true;
  6973. }
  6974. // Last one
  6975. s_hitboxset *set = &g_hitboxsets[ g_hitboxsets.Count() - 1 ];
  6976. if ( autogenerated )
  6977. {
  6978. memset( set, 0, sizeof( *set ) );
  6979. // fill in name if it wasn't specified in the .qc
  6980. strcpy( set->hitboxsetname, "default" );
  6981. }
  6982. GetToken (false);
  6983. set->hitbox[set->numhitboxes].group = verify_atoi( token );
  6984. // Grab the bone name:
  6985. GetToken (false);
  6986. strcpyn( set->hitbox[set->numhitboxes].name, token );
  6987. GetToken (false);
  6988. set->hitbox[set->numhitboxes].bmin[0] = verify_atof( token );
  6989. GetToken (false);
  6990. set->hitbox[set->numhitboxes].bmin[1] = verify_atof( token );
  6991. GetToken (false);
  6992. set->hitbox[set->numhitboxes].bmin[2] = verify_atof( token );
  6993. GetToken (false);
  6994. set->hitbox[set->numhitboxes].bmax[0] = verify_atof( token );
  6995. GetToken (false);
  6996. set->hitbox[set->numhitboxes].bmax[1] = verify_atof( token );
  6997. GetToken (false);
  6998. set->hitbox[set->numhitboxes].bmax[2] = verify_atof( token );
  6999. if ( TokenAvailable() )
  7000. {
  7001. GetToken(false);
  7002. set->hitbox[set->numhitboxes].angOffsetOrientation[0] = verify_atof(token);
  7003. GetToken(false);
  7004. set->hitbox[set->numhitboxes].angOffsetOrientation[1] = verify_atof(token);
  7005. GetToken(false);
  7006. set->hitbox[set->numhitboxes].angOffsetOrientation[2] = verify_atof(token);
  7007. }
  7008. else
  7009. {
  7010. set->hitbox[set->numhitboxes].angOffsetOrientation = QAngle( 0, 0, 0 );
  7011. }
  7012. if ( TokenAvailable() )
  7013. {
  7014. GetToken(false);
  7015. set->hitbox[set->numhitboxes].flCapsuleRadius = verify_atof(token);
  7016. }
  7017. else
  7018. {
  7019. set->hitbox[set->numhitboxes].flCapsuleRadius = -1;
  7020. }
  7021. //Scale hitboxes
  7022. scale_vertex( set->hitbox[set->numhitboxes].bmin );
  7023. scale_vertex( set->hitbox[set->numhitboxes].bmax );
  7024. // clear out the hitboxname:
  7025. memset( set->hitbox[set->numhitboxes].hitboxname, 0, sizeof( set->hitbox[set->numhitboxes].hitboxname ) );
  7026. // Grab the hit box name if present:
  7027. if( TokenAvailable() )
  7028. {
  7029. GetToken (false);
  7030. strcpyn( set->hitbox[set->numhitboxes].hitboxname, token );
  7031. }
  7032. set->numhitboxes++;
  7033. }
  7034. //-----------------------------------------------------------------------------
  7035. // Purpose:
  7036. //-----------------------------------------------------------------------------
  7037. void Cmd_HitboxSet( void )
  7038. {
  7039. // Add a new hitboxset
  7040. s_hitboxset *set = &g_hitboxsets[ g_hitboxsets.AddToTail() ];
  7041. GetToken( false );
  7042. memset( set, 0, sizeof( *set ) );
  7043. strcpy( set->hitboxsetname, token );
  7044. }
  7045. //-----------------------------------------------------------------------------
  7046. // Assigns a default surface property to the entire model
  7047. //-----------------------------------------------------------------------------
  7048. struct SurfacePropName_t
  7049. {
  7050. char m_pJointName[128];
  7051. char m_pSurfaceProp[128];
  7052. };
  7053. static char s_pDefaultSurfaceProp[128] = {"default"};
  7054. static CUtlVector<SurfacePropName_t> s_JointSurfaceProp;
  7055. //-----------------------------------------------------------------------------
  7056. // Assigns a default surface property to the entire model
  7057. //-----------------------------------------------------------------------------
  7058. void SetDefaultSurfaceProp( const char *pSurfaceProperty )
  7059. {
  7060. Q_strncpy( s_pDefaultSurfaceProp, pSurfaceProperty, sizeof(s_pDefaultSurfaceProp) );
  7061. }
  7062. void Cmd_SurfaceProp ()
  7063. {
  7064. GetToken( false );
  7065. SetDefaultSurfaceProp( token );
  7066. }
  7067. //-----------------------------------------------------------------------------
  7068. // Adds a joint surface property
  7069. //-----------------------------------------------------------------------------
  7070. void AddSurfaceProp( const char *pBoneName, const char *pSurfaceProperty )
  7071. {
  7072. // Search for the name in our list
  7073. int i;
  7074. for ( i = s_JointSurfaceProp.Count(); --i >= 0; )
  7075. {
  7076. if ( !Q_stricmp( s_JointSurfaceProp[i].m_pJointName, pBoneName ) )
  7077. break;
  7078. }
  7079. // Add new entry if we haven't seen this name before
  7080. if (i < 0)
  7081. {
  7082. i = s_JointSurfaceProp.AddToTail();
  7083. Q_strncpy( s_JointSurfaceProp[i].m_pJointName, pBoneName, sizeof(s_JointSurfaceProp[i].m_pJointName) );
  7084. }
  7085. Q_strncpy( s_JointSurfaceProp[i].m_pSurfaceProp, pSurfaceProperty, sizeof(s_JointSurfaceProp[i].m_pSurfaceProp) );
  7086. }
  7087. //-----------------------------------------------------------------------------
  7088. // Assigns a surface property to a particular joint
  7089. //-----------------------------------------------------------------------------
  7090. void Cmd_JointSurfaceProp ()
  7091. {
  7092. // Get joint name...
  7093. GetToken( false );
  7094. char pJointName[MAX_PATH];
  7095. Q_strncpy( pJointName, token, sizeof(pJointName) );
  7096. // surface property name
  7097. GetToken( false );
  7098. AddSurfaceProp( pJointName, token );
  7099. }
  7100. //-----------------------------------------------------------------------------
  7101. // Returns the default surface prop name
  7102. //-----------------------------------------------------------------------------
  7103. char* GetDefaultSurfaceProp ( )
  7104. {
  7105. return s_pDefaultSurfaceProp;
  7106. }
  7107. //-----------------------------------------------------------------------------
  7108. // Returns surface property for a given joint
  7109. //-----------------------------------------------------------------------------
  7110. char* FindSurfaceProp ( const char* pJointName )
  7111. {
  7112. for ( int i = s_JointSurfaceProp.Count(); --i >= 0; )
  7113. {
  7114. if ( !Q_stricmp(s_JointSurfaceProp[i].m_pJointName, pJointName) )
  7115. return s_JointSurfaceProp[i].m_pSurfaceProp;
  7116. }
  7117. return 0;
  7118. }
  7119. //-----------------------------------------------------------------------------
  7120. // Returns surface property for a given joint
  7121. //-----------------------------------------------------------------------------
  7122. char* GetSurfaceProp ( const char* pJointName )
  7123. {
  7124. while( pJointName )
  7125. {
  7126. // First try to find this joint
  7127. char* pSurfaceProp = FindSurfaceProp( pJointName );
  7128. if (pSurfaceProp)
  7129. return pSurfaceProp;
  7130. // If we can't find the joint, then find it's parent...
  7131. if (!g_numbones)
  7132. return s_pDefaultSurfaceProp;
  7133. int i = findGlobalBone( pJointName );
  7134. if ((i >= 0) && (g_bonetable[i].parent >= 0))
  7135. {
  7136. pJointName = g_bonetable[g_bonetable[i].parent].name;
  7137. }
  7138. else
  7139. {
  7140. pJointName = 0;
  7141. }
  7142. }
  7143. // No match, return the default one
  7144. return s_pDefaultSurfaceProp;
  7145. }
  7146. //-----------------------------------------------------------------------------
  7147. // Returns surface property for a given joint
  7148. //-----------------------------------------------------------------------------
  7149. void ConsistencyCheckSurfaceProp ( )
  7150. {
  7151. for ( int i = s_JointSurfaceProp.Count(); --i >= 0; )
  7152. {
  7153. int j = findGlobalBone( s_JointSurfaceProp[i].m_pJointName );
  7154. if (j < 0)
  7155. {
  7156. MdlWarning("You specified a joint surface property for joint\n"
  7157. " \"%s\" which either doesn't exist or was optimized out.\n", s_JointSurfaceProp[i].m_pJointName );
  7158. }
  7159. }
  7160. }
  7161. //-----------------------------------------------------------------------------
  7162. // Assigns a default contents to the entire model
  7163. //-----------------------------------------------------------------------------
  7164. int s_nDefaultContents = CONTENTS_SOLID;
  7165. CUtlVector<ContentsName_t> s_JointContents;
  7166. //-----------------------------------------------------------------------------
  7167. // Parse contents flags
  7168. //-----------------------------------------------------------------------------
  7169. static void ParseContents( int *pAddFlags, int *pRemoveFlags )
  7170. {
  7171. *pAddFlags = 0;
  7172. *pRemoveFlags = 0;
  7173. do
  7174. {
  7175. GetToken (false);
  7176. if ( !stricmp( token, "grate" ) )
  7177. {
  7178. *pAddFlags |= CONTENTS_GRATE;
  7179. *pRemoveFlags |= CONTENTS_SOLID;
  7180. }
  7181. else if ( !stricmp( token, "ladder" ) )
  7182. {
  7183. *pAddFlags |= CONTENTS_LADDER;
  7184. }
  7185. else if ( !stricmp( token, "solid" ) )
  7186. {
  7187. *pAddFlags |= CONTENTS_SOLID;
  7188. }
  7189. else if ( !stricmp( token, "monster" ) )
  7190. {
  7191. *pAddFlags |= CONTENTS_MONSTER;
  7192. }
  7193. else if ( !stricmp( token, "notsolid" ) )
  7194. {
  7195. *pRemoveFlags |= CONTENTS_SOLID;
  7196. }
  7197. } while (TokenAvailable());
  7198. }
  7199. //-----------------------------------------------------------------------------
  7200. // Assigns a default contents to the entire model
  7201. //-----------------------------------------------------------------------------
  7202. void Cmd_Contents()
  7203. {
  7204. int nAddFlags, nRemoveFlags;
  7205. ParseContents( &nAddFlags, &nRemoveFlags );
  7206. s_nDefaultContents |= nAddFlags;
  7207. s_nDefaultContents &= ~nRemoveFlags;
  7208. }
  7209. //-----------------------------------------------------------------------------
  7210. // Assigns contents to a particular joint
  7211. //-----------------------------------------------------------------------------
  7212. void Cmd_JointContents ()
  7213. {
  7214. // Get joint name...
  7215. GetToken (false);
  7216. // Search for the name in our list
  7217. int i;
  7218. for ( i = s_JointContents.Count(); --i >= 0; )
  7219. {
  7220. if (!stricmp(s_JointContents[i].m_pJointName, token))
  7221. {
  7222. break;
  7223. }
  7224. }
  7225. // Add new entry if we haven't seen this name before
  7226. if (i < 0)
  7227. {
  7228. i = s_JointContents.AddToTail();
  7229. strcpyn( s_JointContents[i].m_pJointName, token );
  7230. }
  7231. int nAddFlags, nRemoveFlags;
  7232. ParseContents( &nAddFlags, &nRemoveFlags );
  7233. s_JointContents[i].m_nContents = CONTENTS_SOLID;
  7234. s_JointContents[i].m_nContents |= nAddFlags;
  7235. s_JointContents[i].m_nContents &= ~nRemoveFlags;
  7236. }
  7237. //-----------------------------------------------------------------------------
  7238. // Returns the default contents
  7239. //-----------------------------------------------------------------------------
  7240. int GetDefaultContents( )
  7241. {
  7242. return s_nDefaultContents;
  7243. }
  7244. //-----------------------------------------------------------------------------
  7245. // Returns contents for a given joint
  7246. //-----------------------------------------------------------------------------
  7247. static int FindContents( const char* pJointName )
  7248. {
  7249. for ( int i = s_JointContents.Count(); --i >= 0; )
  7250. {
  7251. if (!stricmp(s_JointContents[i].m_pJointName, pJointName))
  7252. {
  7253. return s_JointContents[i].m_nContents;
  7254. }
  7255. }
  7256. return -1;
  7257. }
  7258. //-----------------------------------------------------------------------------
  7259. // Returns contents for a given joint
  7260. //-----------------------------------------------------------------------------
  7261. int GetContents( const char* pJointName )
  7262. {
  7263. while( pJointName )
  7264. {
  7265. // First try to find this joint
  7266. int nContents = FindContents( pJointName );
  7267. if (nContents != -1)
  7268. return nContents;
  7269. // If we can't find the joint, then find it's parent...
  7270. if (!g_numbones)
  7271. return s_nDefaultContents;
  7272. int i = findGlobalBone( pJointName );
  7273. if ((i >= 0) && (g_bonetable[i].parent >= 0))
  7274. {
  7275. pJointName = g_bonetable[g_bonetable[i].parent].name;
  7276. }
  7277. else
  7278. {
  7279. pJointName = 0;
  7280. }
  7281. }
  7282. // No match, return the default one
  7283. return s_nDefaultContents;
  7284. }
  7285. //-----------------------------------------------------------------------------
  7286. // Checks specified contents
  7287. //-----------------------------------------------------------------------------
  7288. void ConsistencyCheckContents( )
  7289. {
  7290. for ( int i = s_JointContents.Count(); --i >= 0; )
  7291. {
  7292. int j = findGlobalBone( s_JointContents[i].m_pJointName );
  7293. if (j < 0)
  7294. {
  7295. MdlWarning("You specified a joint contents for joint\n"
  7296. " \"%s\" which either doesn't exist or was optimized out.\n", s_JointSurfaceProp[i].m_pJointName );
  7297. }
  7298. }
  7299. }
  7300. //-----------------------------------------------------------------------------
  7301. // Purpose:
  7302. //-----------------------------------------------------------------------------
  7303. void Cmd_BoneMerge( )
  7304. {
  7305. if( g_bCreateMakefile )
  7306. return;
  7307. int nIndex = g_BoneMerge.AddToTail();
  7308. // bone name
  7309. GetToken (false);
  7310. strcpyn( g_BoneMerge[nIndex].bonename, token );
  7311. }
  7312. //-----------------------------------------------------------------------------
  7313. // Purpose:
  7314. //-----------------------------------------------------------------------------
  7315. void Cmd_BoneAlwaysSetup( )
  7316. {
  7317. if( g_bCreateMakefile )
  7318. return;
  7319. int nIndex = g_BoneAlwaysSetup.AddToTail();
  7320. // bone name
  7321. GetToken (false);
  7322. strcpyn( g_BoneAlwaysSetup[nIndex].bonename, token );
  7323. }
  7324. //-----------------------------------------------------------------------------
  7325. // Purpose:
  7326. //-----------------------------------------------------------------------------
  7327. void Internal_Cmd_Attachment( int nAttachmentTarget = g_numattachments )
  7328. {
  7329. if( g_bCreateMakefile )
  7330. return;
  7331. // name
  7332. GetToken (false);
  7333. strcpyn( g_attachment[nAttachmentTarget].name, token );
  7334. // bone name
  7335. GetToken (false);
  7336. strcpyn( g_attachment[nAttachmentTarget].bonename, token );
  7337. Vector tmp;
  7338. // position
  7339. GetToken (false);
  7340. tmp.x = verify_atof( token );
  7341. GetToken (false);
  7342. tmp.y = verify_atof( token );
  7343. GetToken (false);
  7344. tmp.z = verify_atof( token );
  7345. scale_vertex( tmp );
  7346. // identity matrix
  7347. AngleMatrix( QAngle( 0, 0, 0 ), g_attachment[nAttachmentTarget].local );
  7348. while (TokenAvailable())
  7349. {
  7350. GetToken (false);
  7351. if (stricmp(token,"absolute") == 0)
  7352. {
  7353. g_attachment[nAttachmentTarget].type |= IS_ABSOLUTE;
  7354. AngleIMatrix( g_defaultrotation, g_attachment[nAttachmentTarget].local );
  7355. // AngleIMatrix( Vector( 0, 0, 0 ), g_attachment[nAttachmentTarget].local );
  7356. }
  7357. else if (stricmp(token,"rigid") == 0)
  7358. {
  7359. g_attachment[nAttachmentTarget].type |= IS_RIGID;
  7360. }
  7361. else if (stricmp(token,"world_align") == 0)
  7362. {
  7363. g_attachment[nAttachmentTarget].flags |= ATTACHMENT_FLAG_WORLD_ALIGN;
  7364. }
  7365. else if (stricmp(token,"rotate") == 0)
  7366. {
  7367. QAngle angles;
  7368. for (int i = 0; i < 3; ++i)
  7369. {
  7370. if (!TokenAvailable())
  7371. break;
  7372. GetToken(false);
  7373. angles[i] = verify_atof( token );
  7374. }
  7375. AngleMatrix( angles, g_attachment[nAttachmentTarget].local );
  7376. }
  7377. else if (stricmp(token,"x_and_z_axes") == 0)
  7378. {
  7379. int i;
  7380. Vector xaxis, yaxis, zaxis;
  7381. for (i = 0; i < 3; ++i)
  7382. {
  7383. if (!TokenAvailable())
  7384. break;
  7385. GetToken(false);
  7386. xaxis[i] = verify_atof( token );
  7387. }
  7388. for (i = 0; i < 3; ++i)
  7389. {
  7390. if (!TokenAvailable())
  7391. break;
  7392. GetToken(false);
  7393. zaxis[i] = verify_atof( token );
  7394. }
  7395. VectorNormalize( xaxis );
  7396. VectorMA( zaxis, -DotProduct( zaxis, xaxis ), xaxis, zaxis );
  7397. VectorNormalize( zaxis );
  7398. CrossProduct( zaxis, xaxis, yaxis );
  7399. MatrixSetColumn( xaxis, 0, g_attachment[nAttachmentTarget].local );
  7400. MatrixSetColumn( yaxis, 1, g_attachment[nAttachmentTarget].local );
  7401. MatrixSetColumn( zaxis, 2, g_attachment[nAttachmentTarget].local );
  7402. MatrixSetColumn( vec3_origin, 3, g_attachment[nAttachmentTarget].local );
  7403. }
  7404. else
  7405. {
  7406. TokenError("unknown attachment (%s) option: ", g_attachment[nAttachmentTarget].name, token );
  7407. }
  7408. }
  7409. g_attachment[nAttachmentTarget].local[0][3] = tmp.x;
  7410. g_attachment[nAttachmentTarget].local[1][3] = tmp.y;
  7411. g_attachment[nAttachmentTarget].local[2][3] = tmp.z;
  7412. if ( nAttachmentTarget == g_numattachments )
  7413. g_numattachments++;
  7414. }
  7415. void Cmd_RedefineAttachment( )
  7416. {
  7417. // find a pre-existing attachment of the given name and re-populate its values
  7418. if( g_bCreateMakefile )
  7419. return;
  7420. // name
  7421. GetToken (false);
  7422. UnGetToken();
  7423. for ( int n=0; n<g_numattachments; n++ )
  7424. {
  7425. if ( !stricmp( token, g_attachment[n].name ) )
  7426. {
  7427. Msg( "Found pre-existing attachment matching name: %s\n", token );
  7428. printf( "Found pre-existing attachment matching name: %s\n", token );
  7429. Internal_Cmd_Attachment( n );
  7430. return;
  7431. }
  7432. }
  7433. MdlError( "Can't redefine attachment \"%s\" because it wasn't found.\n", token );
  7434. }
  7435. void Cmd_Attachment( )
  7436. {
  7437. Internal_Cmd_Attachment( g_numattachments );
  7438. }
  7439. //-----------------------------------------------------------------------------
  7440. // Purpose:
  7441. //-----------------------------------------------------------------------------
  7442. int LookupAttachment( const char *name )
  7443. {
  7444. int i;
  7445. for (i = 0; i < g_numattachments; i++)
  7446. {
  7447. if (stricmp( g_attachment[i].name, name ) == 0)
  7448. {
  7449. return i;
  7450. }
  7451. }
  7452. return -1;
  7453. }
  7454. //-----------------------------------------------------------------------------
  7455. // Purpose:
  7456. //-----------------------------------------------------------------------------
  7457. void Cmd_Renamebone( )
  7458. {
  7459. // from
  7460. GetToken (false);
  7461. strcpyn( g_renamedbone[g_numrenamedbones].from, token );
  7462. // to
  7463. GetToken (false);
  7464. strcpyn( g_renamedbone[g_numrenamedbones].to, token );
  7465. g_numrenamedbones++;
  7466. }
  7467. //-----------------------------------------------------------------------------
  7468. // Purpose:
  7469. //-----------------------------------------------------------------------------
  7470. void Cmd_StripBonePrefix( )
  7471. {
  7472. if ( g_numStripBonePrefixes < MAXSTUDIOSRCBONES )
  7473. {
  7474. GetToken (false);
  7475. // make sure it's not a duplicate
  7476. for ( int k = 0; k < g_numStripBonePrefixes; k++)
  7477. {
  7478. if ( !Q_strcmp( token, g_szStripBonePrefix[k] ) )
  7479. {
  7480. MdlWarning( "Ignoring duplicate $bonestripprefix for token %s\n", token );
  7481. return;
  7482. }
  7483. }
  7484. strcpyn( g_szStripBonePrefix[g_numStripBonePrefixes], token );
  7485. g_numStripBonePrefixes++;
  7486. }
  7487. else
  7488. {
  7489. MdlError( "Too many bone strip prefixes!\n" );
  7490. }
  7491. }
  7492. void Cmd_RenameBoneSubstr( )
  7493. {
  7494. // from
  7495. GetToken (false);
  7496. strcpyn( g_szRenameBoneSubstr[g_numRenameBoneSubstr].from, token );
  7497. // to
  7498. GetToken (false);
  7499. strcpyn( g_szRenameBoneSubstr[g_numRenameBoneSubstr].to, token );
  7500. g_numRenameBoneSubstr++;
  7501. }
  7502. //-----------------------------------------------------------------------------
  7503. // Purpose:
  7504. //-----------------------------------------------------------------------------
  7505. void Cmd_Skiptransition( )
  7506. {
  7507. int nskips = 0;
  7508. int list[10];
  7509. while (TokenAvailable())
  7510. {
  7511. GetToken (false);
  7512. list[nskips++] = LookupXNode( token );
  7513. }
  7514. for (int i = 0; i < nskips; i++)
  7515. {
  7516. for (int j = 0; j < nskips; j++)
  7517. {
  7518. if (list[i] != list[j])
  7519. {
  7520. g_xnodeskip[g_numxnodeskips][0] = list[i];
  7521. g_xnodeskip[g_numxnodeskips][1] = list[j];
  7522. g_numxnodeskips++;
  7523. }
  7524. }
  7525. }
  7526. }
  7527. //-----------------------------------------------------------------------------
  7528. //
  7529. // The following code is all related to LODs
  7530. //
  7531. //-----------------------------------------------------------------------------
  7532. //-----------------------------------------------------------------------------
  7533. // Parse replacemodel command, causes an LOD to use a new model
  7534. //-----------------------------------------------------------------------------
  7535. static void Cmd_ReplaceModel( LodScriptData_t& lodData )
  7536. {
  7537. int i = lodData.modelReplacements.AddToTail();
  7538. CLodScriptReplacement_t& newReplacement = lodData.modelReplacements[i];
  7539. // from
  7540. GetToken( false );
  7541. // Strip off extensions for the source...
  7542. char* pDot = strrchr( token, '.' );
  7543. if (pDot)
  7544. {
  7545. *pDot = 0;
  7546. }
  7547. if (!FindCachedSource( token, "" ))
  7548. {
  7549. // must have prior knowledge of the from
  7550. TokenError( "Unknown replace model '%s'\n", token );
  7551. }
  7552. newReplacement.SetSrcName( token );
  7553. // to
  7554. GetToken( false );
  7555. newReplacement.SetDstName( token );
  7556. // check for "reverse"
  7557. bool reverse = false;
  7558. if( TokenAvailable() && GetToken( false ) )
  7559. {
  7560. if( stricmp( "reverse", token ) == 0 )
  7561. {
  7562. reverse = true;
  7563. }
  7564. else
  7565. {
  7566. TokenError( "\"%s\" unexpected\n", token );
  7567. }
  7568. }
  7569. // If the LOD system tells us to replace "blank", let's forget
  7570. // we ever read this. Have to do it here so parsing works
  7571. if( !stricmp( newReplacement.GetSrcName(), "blank" ) )
  7572. {
  7573. lodData.modelReplacements.FastRemove( i );
  7574. return;
  7575. }
  7576. // Load the source right here baby! That way its bones will get converted
  7577. if ( !lodData.IsStrippedFromModel() )
  7578. {
  7579. newReplacement.m_pSource = Load_Source( newReplacement.GetDstName(), "smd", reverse, false );
  7580. }
  7581. else if ( !g_quiet )
  7582. {
  7583. printf( "Stripped lod \"%s\" @ %.1f\n", newReplacement.GetDstName(), lodData.switchValue );
  7584. }
  7585. }
  7586. //-----------------------------------------------------------------------------
  7587. // Parse removemodel command, causes an LOD to stop using a model
  7588. //-----------------------------------------------------------------------------
  7589. static void Cmd_RemoveModel( LodScriptData_t& lodData )
  7590. {
  7591. int i = lodData.modelReplacements.AddToTail();
  7592. CLodScriptReplacement_t& newReplacement = lodData.modelReplacements[i];
  7593. // from
  7594. GetToken( false );
  7595. // Strip off extensions...
  7596. char* pDot = strrchr( token, '.' );
  7597. if (pDot)
  7598. *pDot = 0;
  7599. newReplacement.SetSrcName( token );
  7600. // to
  7601. newReplacement.SetDstName( "" );
  7602. // If the LOD system tells us to replace "blank", let's forget
  7603. // we ever read this. Have to do it here so parsing works
  7604. if( !stricmp( newReplacement.GetSrcName(), "blank" ) )
  7605. {
  7606. lodData.modelReplacements.FastRemove( i );
  7607. }
  7608. }
  7609. //-----------------------------------------------------------------------------
  7610. // Parse replacebone command, causes a part of an LOD model to use a different bone
  7611. //-----------------------------------------------------------------------------
  7612. static void Cmd_ReplaceBone( LodScriptData_t& lodData )
  7613. {
  7614. int i = lodData.boneReplacements.AddToTail();
  7615. CLodScriptReplacement_t& newReplacement = lodData.boneReplacements[i];
  7616. // from
  7617. GetToken( false );
  7618. newReplacement.SetSrcName( token );
  7619. // to
  7620. GetToken( false );
  7621. newReplacement.SetDstName( token );
  7622. }
  7623. //-----------------------------------------------------------------------------
  7624. // Parse bonetreecollapse command, causes the entire subtree to use the same bone as the node
  7625. //-----------------------------------------------------------------------------
  7626. static void Cmd_BoneTreeCollapse( LodScriptData_t& lodData )
  7627. {
  7628. int i = lodData.boneTreeCollapses.AddToTail();
  7629. CLodScriptReplacement_t& newCollapse = lodData.boneTreeCollapses[i];
  7630. // from
  7631. GetToken( false );
  7632. newCollapse.SetSrcName( token );
  7633. }
  7634. //-----------------------------------------------------------------------------
  7635. // Parse replacematerial command, causes a material to be used in place of another
  7636. //-----------------------------------------------------------------------------
  7637. static void Cmd_ReplaceMaterial( LodScriptData_t& lodData )
  7638. {
  7639. int i = lodData.materialReplacements.AddToTail();
  7640. CLodScriptReplacement_t& newReplacement = lodData.materialReplacements[i];
  7641. // from
  7642. GetToken( false );
  7643. newReplacement.SetSrcName( token );
  7644. // to
  7645. GetToken( false );
  7646. newReplacement.SetDstName( token );
  7647. if ( !lodData.IsStrippedFromModel() )
  7648. {
  7649. // make sure it goes into the master list
  7650. UseTextureAsMaterial( LookupTexture( token ) );
  7651. }
  7652. }
  7653. //-----------------------------------------------------------------------------
  7654. // Parse removemesh command, causes a mesh to not be used anymore
  7655. //-----------------------------------------------------------------------------
  7656. static void Cmd_RemoveMesh( LodScriptData_t& lodData )
  7657. {
  7658. int i = lodData.meshRemovals.AddToTail();
  7659. CLodScriptReplacement_t& newReplacement = lodData.meshRemovals[i];
  7660. // from
  7661. GetToken( false );
  7662. Q_FixSlashes( token );
  7663. newReplacement.SetSrcName( token );
  7664. }
  7665. void Cmd_LOD( const char *cmdname )
  7666. {
  7667. if ( gflags & STUDIOHDR_FLAGS_HASSHADOWLOD )
  7668. {
  7669. MdlError( "Model can only have one $shadowlod and it must be the last lod in the " SRC_FILE_EXT " (%d) : %s\n", g_iLinecount, g_szLine );
  7670. }
  7671. int i = g_ScriptLODs.AddToTail();
  7672. LodScriptData_t& newLOD = g_ScriptLODs[i];
  7673. if( g_ScriptLODs.Count() > MAX_NUM_LODS )
  7674. {
  7675. MdlError( "Too many LODs (MAX_NUM_LODS==%d)\n", ( int )MAX_NUM_LODS );
  7676. }
  7677. // Shadow lod reserves -1 as switch value
  7678. // which uniquely identifies a shadow lod
  7679. newLOD.switchValue = -1.0f;
  7680. bool isShadowCall = ( !stricmp( cmdname, "$shadowlod" ) ) ? true : false;
  7681. if ( isShadowCall )
  7682. {
  7683. if ( TokenAvailable() )
  7684. {
  7685. GetToken( false );
  7686. MdlWarning( "(%d) : %s: Ignoring switch value on %s command line\n", cmdname, g_iLinecount, g_szLine );
  7687. }
  7688. // Disable facial animation by default
  7689. newLOD.EnableFacialAnimation( false );
  7690. }
  7691. else
  7692. {
  7693. if ( TokenAvailable() )
  7694. {
  7695. GetToken( false );
  7696. newLOD.switchValue = verify_atof( token );
  7697. if ( newLOD.switchValue < 0.0f )
  7698. {
  7699. MdlError( "Negative switch value reserved for $shadowlod (%d) : %s\n", g_iLinecount, g_szLine );
  7700. }
  7701. }
  7702. else
  7703. {
  7704. MdlError( "Expected LOD switch value (%d) : %s\n", g_iLinecount, g_szLine );
  7705. }
  7706. }
  7707. GetToken( true );
  7708. if( stricmp( "{", token ) != 0 )
  7709. {
  7710. MdlError( "\"{\" expected while processing %s (%d) : %s", cmdname, g_iLinecount, g_szLine );
  7711. }
  7712. // In case we are stripping all lods and it's not Lod0, strip it
  7713. if ( i && g_bStripLods )
  7714. newLOD.StripFromModel( true );
  7715. while( 1 )
  7716. {
  7717. GetToken( true );
  7718. if( stricmp( "replacemodel", token ) == 0 )
  7719. {
  7720. Cmd_ReplaceModel(newLOD);
  7721. }
  7722. else if( stricmp( "removemodel", token ) == 0 )
  7723. {
  7724. Cmd_RemoveModel(newLOD);
  7725. }
  7726. else if( stricmp( "replacebone", token ) == 0 )
  7727. {
  7728. Cmd_ReplaceBone( newLOD );
  7729. }
  7730. else if( stricmp( "bonetreecollapse", token ) == 0 )
  7731. {
  7732. Cmd_BoneTreeCollapse( newLOD );
  7733. }
  7734. else if( stricmp( "replacematerial", token ) == 0 )
  7735. {
  7736. Cmd_ReplaceMaterial( newLOD );
  7737. }
  7738. else if( stricmp( "removemesh", token ) == 0 )
  7739. {
  7740. Cmd_RemoveMesh( newLOD );
  7741. }
  7742. else if( stricmp( "nofacial", token ) == 0 )
  7743. {
  7744. newLOD.EnableFacialAnimation( false );
  7745. }
  7746. else if( stricmp( "facial", token ) == 0 )
  7747. {
  7748. if (isShadowCall)
  7749. {
  7750. // facial animation has no reasonable purpose on a shadow lod
  7751. TokenError( "Facial animation is not allowed for $shadowlod\n" );
  7752. }
  7753. newLOD.EnableFacialAnimation( true );
  7754. }
  7755. else if ( stricmp( "use_shadowlod_materials", token ) == 0 )
  7756. {
  7757. if (isShadowCall)
  7758. {
  7759. gflags |= STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS;
  7760. }
  7761. }
  7762. else if( stricmp( "}", token ) == 0 )
  7763. {
  7764. break;
  7765. }
  7766. else
  7767. {
  7768. MdlError( "invalid input while processing %s (%d) : %s", cmdname, g_iLinecount, g_szLine );
  7769. }
  7770. }
  7771. // If the LOD is stripped, then forget we saw it
  7772. if ( newLOD.IsStrippedFromModel() )
  7773. {
  7774. g_ScriptLODs.FastRemove( i );
  7775. }
  7776. }
  7777. void Cmd_ShadowLOD( void )
  7778. {
  7779. if (!g_quiet)
  7780. {
  7781. printf( "Processing $shadowlod\n" );
  7782. }
  7783. // Act like it's a regular lod entry
  7784. Cmd_LOD( "$shadowlod" );
  7785. // Mark .mdl as having shadow lod (we also check above that we have only one of these
  7786. // and that it's the last entered lod )
  7787. gflags |= STUDIOHDR_FLAGS_HASSHADOWLOD;
  7788. }
  7789. //-----------------------------------------------------------------------------
  7790. // A couple commands related to translucency sorting
  7791. //-----------------------------------------------------------------------------
  7792. void Cmd_Opaque( )
  7793. {
  7794. // Force Opaque has precedence
  7795. gflags |= STUDIOHDR_FLAGS_FORCE_OPAQUE;
  7796. gflags &= ~STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS;
  7797. }
  7798. void Cmd_TranslucentTwoPass( )
  7799. {
  7800. // Force Opaque has precedence
  7801. if ((gflags & STUDIOHDR_FLAGS_FORCE_OPAQUE) == 0)
  7802. {
  7803. gflags |= STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS;
  7804. }
  7805. }
  7806. //-----------------------------------------------------------------------------
  7807. // Indicates the model be rendered with ambient boost heuristic (first used on Alyx in Episode 1)
  7808. //-----------------------------------------------------------------------------
  7809. void Cmd_AmbientBoost()
  7810. {
  7811. gflags |= STUDIOHDR_FLAGS_AMBIENT_BOOST;
  7812. }
  7813. //-----------------------------------------------------------------------------
  7814. // Indicates the model contains a quad-only Catmull-Clark subd mesh
  7815. //-----------------------------------------------------------------------------
  7816. void Cmd_SubdivisionSurface()
  7817. {
  7818. gflags |= STUDIOHDR_FLAGS_SUBDIVISION_SURFACE;
  7819. }
  7820. //-----------------------------------------------------------------------------
  7821. // Indicates the model should not cast shadows (useful for first-person models as used in L4D)
  7822. //-----------------------------------------------------------------------------
  7823. void Cmd_DoNotCastShadows()
  7824. {
  7825. gflags |= STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS;
  7826. }
  7827. //-----------------------------------------------------------------------------
  7828. // Indicates the model should cast texture-based shadows in vrad (NOTE: only applicable to prop_static)
  7829. //-----------------------------------------------------------------------------
  7830. void Cmd_CastTextureShadows()
  7831. {
  7832. gflags |= STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS;
  7833. }
  7834. //-----------------------------------------------------------------------------
  7835. // Indicates the model should not fade out even if the level or fallback settings say to
  7836. //-----------------------------------------------------------------------------
  7837. void Cmd_NoForcedFade()
  7838. {
  7839. gflags |= STUDIOHDR_FLAGS_NO_FORCED_FADE;
  7840. }
  7841. //-----------------------------------------------------------------------------
  7842. // Indicates the model should not use the bone origin when calculating bboxes, sequence boxes, etc.
  7843. //-----------------------------------------------------------------------------
  7844. void Cmd_SkipBoneInBBox()
  7845. {
  7846. g_bUseBoneInBBox = false;
  7847. }
  7848. //-----------------------------------------------------------------------------
  7849. // Indicates the model will lengthen the viseme check to always include two phonemes
  7850. //-----------------------------------------------------------------------------
  7851. void Cmd_ForcePhonemeCrossfade()
  7852. {
  7853. gflags |= STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE;
  7854. }
  7855. //-----------------------------------------------------------------------------
  7856. // Indicates the model should keep pre-defined bone lengths regardless of animation changes
  7857. //-----------------------------------------------------------------------------
  7858. void Cmd_LockBoneLengths()
  7859. {
  7860. g_bLockBoneLengths = true;
  7861. }
  7862. //-----------------------------------------------------------------------------
  7863. // Indicates the model should replace pre-defined bone bind poses
  7864. //-----------------------------------------------------------------------------
  7865. void Cmd_UnlockDefineBones()
  7866. {
  7867. g_bDefineBonesLockedByDefault = false;
  7868. }
  7869. //-----------------------------------------------------------------------------
  7870. // Mark this model as obsolete so that it'll show the obsolete material in game.
  7871. //-----------------------------------------------------------------------------
  7872. void Cmd_Obsolete( )
  7873. {
  7874. // Force Opaque has precedence
  7875. gflags |= STUDIOHDR_FLAGS_OBSOLETE;
  7876. }
  7877. //-----------------------------------------------------------------------------
  7878. // The bones should be moved so that they center themselves on the verts they own.
  7879. //-----------------------------------------------------------------------------
  7880. void Cmd_CenterBonesOnVerts( )
  7881. {
  7882. // force centering on bones
  7883. g_bCenterBonesOnVerts = true;
  7884. }
  7885. //-----------------------------------------------------------------------------
  7886. // How far back should simple motion extract pull back from the last frame
  7887. //-----------------------------------------------------------------------------
  7888. void Cmd_MotionExtractionRollBack( )
  7889. {
  7890. GetToken( false );
  7891. g_flDefaultMotionRollback = atof( token );
  7892. }
  7893. //-----------------------------------------------------------------------------
  7894. // rules for breaking up long animations into multiple sub anims
  7895. //-----------------------------------------------------------------------------
  7896. void Cmd_SectionFrames( )
  7897. {
  7898. GetToken( false );
  7899. g_sectionFrames = atof( token );
  7900. GetToken( false );
  7901. g_minSectionFrameLimit = atoi( token );
  7902. }
  7903. //-----------------------------------------------------------------------------
  7904. // world space clamping boundaries for animations
  7905. //-----------------------------------------------------------------------------
  7906. void Cmd_ClampWorldspace( )
  7907. {
  7908. GetToken (false);
  7909. g_vecMinWorldspace[0] = verify_atof( token );
  7910. GetToken (false);
  7911. g_vecMinWorldspace[1] = verify_atof( token );
  7912. GetToken (false);
  7913. g_vecMinWorldspace[2] = verify_atof( token );
  7914. GetToken (false);
  7915. g_vecMaxWorldspace[0] = verify_atof( token );
  7916. GetToken (false);
  7917. g_vecMaxWorldspace[1] = verify_atof( token );
  7918. GetToken (false);
  7919. g_vecMaxWorldspace[2] = verify_atof( token );
  7920. }
  7921. //-----------------------------------------------------------------------------
  7922. // Key value block!
  7923. //-----------------------------------------------------------------------------
  7924. void Option_KeyValues( CUtlVector< char > *pKeyValue )
  7925. {
  7926. // Simply read in the block between { }s as text
  7927. // and plop it out unchanged into the .mdl file.
  7928. // Make sure to respect the fact that we may have nested {}s
  7929. int nLevel = 1;
  7930. if ( !GetToken( true ) )
  7931. return;
  7932. if ( token[0] != '{' )
  7933. return;
  7934. while ( GetToken(true) )
  7935. {
  7936. if ( !stricmp( token, "}" ) )
  7937. {
  7938. nLevel--;
  7939. if ( nLevel <= 0 )
  7940. break;
  7941. AppendKeyValueText( pKeyValue, " }\n" );
  7942. }
  7943. else if ( !stricmp( token, "{" ) )
  7944. {
  7945. AppendKeyValueText( pKeyValue, "{\n" );
  7946. nLevel++;
  7947. }
  7948. else
  7949. {
  7950. // tokens inside braces are quoted
  7951. if ( nLevel > 1 )
  7952. {
  7953. AppendKeyValueText( pKeyValue, "\"" );
  7954. AppendKeyValueText( pKeyValue, token );
  7955. AppendKeyValueText( pKeyValue, "\" " );
  7956. }
  7957. else
  7958. {
  7959. AppendKeyValueText( pKeyValue, token );
  7960. AppendKeyValueText( pKeyValue, " " );
  7961. }
  7962. }
  7963. }
  7964. if ( nLevel >= 1 )
  7965. {
  7966. TokenError( "Keyvalue block missing matching braces.\n" );
  7967. }
  7968. }
  7969. //-----------------------------------------------------------------------------
  7970. // Purpose: force a specific parent child relationship
  7971. //-----------------------------------------------------------------------------
  7972. void Cmd_ForcedHierarchy( )
  7973. {
  7974. // child name
  7975. GetToken (false);
  7976. strcpyn( g_forcedhierarchy[g_numforcedhierarchy].childname, token );
  7977. // parent name
  7978. GetToken (false);
  7979. strcpyn( g_forcedhierarchy[g_numforcedhierarchy].parentname, token );
  7980. g_numforcedhierarchy++;
  7981. }
  7982. //-----------------------------------------------------------------------------
  7983. // Purpose: insert a virtual bone between a child and parent (currently unsupported)
  7984. //-----------------------------------------------------------------------------
  7985. void Cmd_InsertHierarchy( )
  7986. {
  7987. // child name
  7988. GetToken (false);
  7989. strcpyn( g_forcedhierarchy[g_numforcedhierarchy].childname, token );
  7990. // subparent name
  7991. GetToken (false);
  7992. strcpyn( g_forcedhierarchy[g_numforcedhierarchy].subparentname, token );
  7993. // parent name
  7994. GetToken (false);
  7995. strcpyn( g_forcedhierarchy[g_numforcedhierarchy].parentname, token );
  7996. g_numforcedhierarchy++;
  7997. }
  7998. //-----------------------------------------------------------------------------
  7999. // Purpose: rotate a specific bone
  8000. //-----------------------------------------------------------------------------
  8001. void Cmd_ForceRealign( )
  8002. {
  8003. // bone name
  8004. GetToken (false);
  8005. strcpyn( g_forcedrealign[g_numforcedrealign].name, token );
  8006. // skip
  8007. GetToken (false);
  8008. // X axis
  8009. GetToken (false);
  8010. g_forcedrealign[g_numforcedrealign].rot.x = DEG2RAD( verify_atof( token ) );
  8011. // Y axis
  8012. GetToken (false);
  8013. g_forcedrealign[g_numforcedrealign].rot.y = DEG2RAD( verify_atof( token ) );
  8014. // Z axis
  8015. GetToken (false);
  8016. g_forcedrealign[g_numforcedrealign].rot.z = DEG2RAD( verify_atof( token ) );
  8017. g_numforcedrealign++;
  8018. }
  8019. //-----------------------------------------------------------------------------
  8020. // Purpose: specify a bone to allow > 180 but < 360 rotation (forces a calculated "mid point" to rotation)
  8021. //-----------------------------------------------------------------------------
  8022. void Cmd_LimitRotation( )
  8023. {
  8024. // bone name
  8025. GetToken (false);
  8026. strcpyn( g_limitrotation[g_numlimitrotation].name, token );
  8027. while (TokenAvailable())
  8028. {
  8029. // sequence name
  8030. GetToken (false);
  8031. strcpyn( g_limitrotation[g_numlimitrotation].sequencename[g_limitrotation[g_numlimitrotation].numseq++], token );
  8032. }
  8033. g_numlimitrotation++;
  8034. }
  8035. //-----------------------------------------------------------------------------
  8036. // Purpose: artist controlled sanity check for expected state of the model.
  8037. // The idea is to allow artists to anticipate and prevent content errors by adding 'qc asserts'
  8038. // into commonly iterated models. This could be anything from bones that are expected (or not)
  8039. // to polycounts, material references, etc.- It's just an "Assert" for content.
  8040. //-----------------------------------------------------------------------------
  8041. void Cmd_QCAssert( )
  8042. {
  8043. //get the assert type
  8044. GetToken (false);
  8045. //Msg( "Validating QC Assert '%s'\n", token );
  8046. //start building assert description line
  8047. char szAssertLine[1024] = "QC Assert: ";
  8048. strcat( szAssertLine, token );
  8049. bool bQueryValue = false;
  8050. if ( !Q_stricmp( token, "boneexists" ) )
  8051. {
  8052. // bone name
  8053. GetToken (false);
  8054. char szBoneName[MAXSTUDIONAME];
  8055. strcpyn( szBoneName, token );
  8056. strcat( szAssertLine, " " );
  8057. strcat( szAssertLine, szBoneName );
  8058. // src name
  8059. GetToken (false);
  8060. s_source_t *pSrc = Load_Source( token, "" );
  8061. strcat( szAssertLine, " " );
  8062. strcat( szAssertLine, token );
  8063. for ( int n=0; n<pSrc->numbones; n++ )
  8064. {
  8065. if ( !Q_stricmp( szBoneName, pSrc->localBone[n].name ) )
  8066. {
  8067. bQueryValue = true;
  8068. break;
  8069. }
  8070. }
  8071. }
  8072. else if ( !Q_stricmp( token, "importboneexists" ) )
  8073. {
  8074. // bone name
  8075. GetToken (false);
  8076. char szBoneName[MAXSTUDIONAME];
  8077. strcpyn( szBoneName, token );
  8078. strcat( szAssertLine, " " );
  8079. strcat( szAssertLine, szBoneName );
  8080. for ( int n=0; n<g_numimportbones; n++ )
  8081. {
  8082. if ( !Q_stricmp( szBoneName, g_importbone[n].name ) )
  8083. {
  8084. bQueryValue = true;
  8085. break;
  8086. }
  8087. }
  8088. }
  8089. // add more possible qc asserts here...
  8090. // is the assert value positive or negative
  8091. GetToken (false);
  8092. strcat( szAssertLine, " " );
  8093. strcat( szAssertLine, token );
  8094. bool bAssertValue = !Q_stricmp( token, "true" );
  8095. // print the result
  8096. strcat( szAssertLine, " RESULT: " );
  8097. if ( bQueryValue != bAssertValue )
  8098. {
  8099. strcat( szAssertLine, "[Fail]\n" );
  8100. //// show helpful message, if one exists
  8101. //if ( TokenAvailable() )
  8102. //{
  8103. // GetToken (false);
  8104. // strcat( szAssertLine, token );
  8105. //}
  8106. MdlError( szAssertLine );
  8107. }
  8108. else
  8109. {
  8110. strcat( szAssertLine, "[Success]\n" );
  8111. }
  8112. printf( szAssertLine );
  8113. }
  8114. //-----------------------------------------------------------------------------
  8115. // Purpose: specify bones to store, even if nothing references them
  8116. //-----------------------------------------------------------------------------
  8117. void Cmd_DefineBone( )
  8118. {
  8119. // bone name
  8120. GetToken (false);
  8121. strcpyn( g_importbone[g_numimportbones].name, token );
  8122. // parent name
  8123. GetToken (false);
  8124. strcpyn( g_importbone[g_numimportbones].parent, token );
  8125. g_importbone[g_numimportbones].bUnlocked = !g_bDefineBonesLockedByDefault;
  8126. GetToken( false );
  8127. if ( !V_strcmp( token, "unlocked" ) )
  8128. {
  8129. g_importbone[g_numimportbones].bUnlocked = true;
  8130. }
  8131. else if ( !V_strcmp( token, "locked" ) )
  8132. {
  8133. g_importbone[g_numimportbones].bUnlocked = false;
  8134. }
  8135. else
  8136. {
  8137. UnGetToken();
  8138. }
  8139. Vector pos;
  8140. QAngle angles;
  8141. // default pos
  8142. GetToken (false);
  8143. pos.x = verify_atof( token );
  8144. GetToken (false);
  8145. pos.y = verify_atof( token );
  8146. GetToken (false);
  8147. pos.z = verify_atof( token );
  8148. GetToken (false);
  8149. angles.x = verify_atof( token );
  8150. GetToken (false);
  8151. angles.y = verify_atof( token );
  8152. GetToken (false);
  8153. angles.z = verify_atof( token );
  8154. AngleMatrix( angles, pos, g_importbone[g_numimportbones].rawLocal );
  8155. if (TokenAvailable())
  8156. {
  8157. g_importbone[g_numimportbones].bPreAligned = true;
  8158. // realign pos
  8159. GetToken (false);
  8160. pos.x = verify_atof( token );
  8161. GetToken (false);
  8162. pos.y = verify_atof( token );
  8163. GetToken (false);
  8164. pos.z = verify_atof( token );
  8165. GetToken (false);
  8166. angles.x = verify_atof( token );
  8167. GetToken (false);
  8168. angles.y = verify_atof( token );
  8169. GetToken (false);
  8170. angles.z = verify_atof( token );
  8171. AngleMatrix( angles, pos, g_importbone[g_numimportbones].srcRealign );
  8172. }
  8173. else
  8174. {
  8175. SetIdentityMatrix( g_importbone[g_numimportbones].srcRealign );
  8176. }
  8177. g_numimportbones++;
  8178. }
  8179. //----------------------------------------------------------------------------------------------
  8180. float ParseJiggleStiffness( void )
  8181. {
  8182. if ( !GetToken( false ) )
  8183. {
  8184. MdlError( "$jigglebone: expecting stiffness value\n", g_iLinecount, g_szLine );
  8185. return 0.0f;
  8186. }
  8187. float stiffness = verify_atof( token );
  8188. const float minStiffness = 0.0f;
  8189. const float maxStiffness = 1000.0f;
  8190. return clamp( stiffness, minStiffness, maxStiffness );
  8191. }
  8192. //----------------------------------------------------------------------------------------------
  8193. float ParseJiggleDamping( void )
  8194. {
  8195. if ( !GetToken( false ) )
  8196. {
  8197. MdlError( "$jigglebone: expecting damping value\n", g_iLinecount, g_szLine );
  8198. return 0.0f;
  8199. }
  8200. float damping = verify_atof( token );
  8201. const float minDamping = 0.0f;
  8202. const float maxDamping = 10.0f;
  8203. return clamp( damping, minDamping, maxDamping );
  8204. }
  8205. //----------------------------------------------------------------------------------------------
  8206. bool ParseJiggleAngleConstraint( s_jigglebone_t *jiggleInfo )
  8207. {
  8208. jiggleInfo->data.flags |= JIGGLE_HAS_ANGLE_CONSTRAINT;
  8209. if ( !GetToken( false ) )
  8210. {
  8211. MdlError( "$jigglebone: expecting angle value\n", g_iLinecount, g_szLine );
  8212. return false;
  8213. }
  8214. jiggleInfo->data.angleLimit = verify_atof( token ) * M_PI / 180.0f;
  8215. return true;
  8216. }
  8217. //----------------------------------------------------------------------------------------------
  8218. bool ParseJiggleYawConstraint( s_jigglebone_t *jiggleInfo )
  8219. {
  8220. jiggleInfo->data.flags |= JIGGLE_HAS_YAW_CONSTRAINT;
  8221. if ( !GetToken( false ) )
  8222. {
  8223. MdlError( "$jigglebone: expecting minimum yaw value\n", g_iLinecount, g_szLine );
  8224. return false;
  8225. }
  8226. jiggleInfo->data.minYaw = verify_atof( token ) * M_PI / 180.0f;
  8227. if ( !GetToken( false ) )
  8228. {
  8229. MdlError( "$jigglebone: expecting maximum yaw value\n", g_iLinecount, g_szLine );
  8230. return false;
  8231. }
  8232. jiggleInfo->data.maxYaw = verify_atof( token ) * M_PI / 180.0f;
  8233. return true;
  8234. }
  8235. //----------------------------------------------------------------------------------------------
  8236. bool ParseJigglePitchConstraint( s_jigglebone_t *jiggleInfo )
  8237. {
  8238. jiggleInfo->data.flags |= JIGGLE_HAS_PITCH_CONSTRAINT;
  8239. if ( !GetToken( false ) )
  8240. {
  8241. MdlError( "$jigglebone: expecting minimum pitch value\n", g_iLinecount, g_szLine );
  8242. return false;
  8243. }
  8244. jiggleInfo->data.minPitch = verify_atof( token ) * M_PI / 180.0f;
  8245. if ( !GetToken( false ) )
  8246. {
  8247. MdlError( "$jigglebone: expecting maximum pitch value\n", g_iLinecount, g_szLine );
  8248. return false;
  8249. }
  8250. jiggleInfo->data.maxPitch = verify_atof( token ) * M_PI / 180.0f;
  8251. return true;
  8252. }
  8253. //----------------------------------------------------------------------------------------------
  8254. /**
  8255. * Parse common parameters.
  8256. * This assumes a token has already been read, and returns true if
  8257. * the token is recognized and parsed.
  8258. */
  8259. bool ParseCommonJiggle( s_jigglebone_t *jiggleInfo )
  8260. {
  8261. if (!stricmp( token, "tip_mass" ))
  8262. {
  8263. if ( !GetToken( false ) )
  8264. {
  8265. return false;
  8266. }
  8267. jiggleInfo->data.tipMass = verify_atof( token );
  8268. }
  8269. else if (!stricmp( token, "length" ))
  8270. {
  8271. if ( !GetToken( false ) )
  8272. {
  8273. return false;
  8274. }
  8275. jiggleInfo->data.length = verify_atof( token );
  8276. }
  8277. else if (!stricmp( token, "angle_constraint" ))
  8278. {
  8279. if (ParseJiggleAngleConstraint( jiggleInfo ) == false)
  8280. {
  8281. return false;
  8282. }
  8283. }
  8284. else if (!stricmp( token, "yaw_constraint" ))
  8285. {
  8286. if (ParseJiggleYawConstraint( jiggleInfo ) == false)
  8287. {
  8288. return false;
  8289. }
  8290. }
  8291. else if (!stricmp( token, "yaw_friction" ))
  8292. {
  8293. if ( !GetToken( false ) )
  8294. {
  8295. return false;
  8296. }
  8297. jiggleInfo->data.yawFriction = verify_atof( token );
  8298. }
  8299. else if (!stricmp( token, "yaw_bounce" ))
  8300. {
  8301. if ( !GetToken( false ) )
  8302. {
  8303. return false;
  8304. }
  8305. jiggleInfo->data.yawBounce = verify_atof( token );
  8306. }
  8307. else if (!stricmp( token, "pitch_constraint" ))
  8308. {
  8309. if (ParseJigglePitchConstraint( jiggleInfo ) == false)
  8310. {
  8311. return false;
  8312. }
  8313. }
  8314. else if (!stricmp( token, "pitch_friction" ))
  8315. {
  8316. if ( !GetToken( false ) )
  8317. {
  8318. return false;
  8319. }
  8320. jiggleInfo->data.pitchFriction = verify_atof( token );
  8321. }
  8322. else if (!stricmp( token, "pitch_bounce" ))
  8323. {
  8324. if ( !GetToken( false ) )
  8325. {
  8326. return false;
  8327. }
  8328. jiggleInfo->data.pitchBounce = verify_atof( token );
  8329. }
  8330. else
  8331. {
  8332. // unknown token
  8333. MdlError( "$jigglebone: invalid syntax '%s'\n", token );
  8334. return false;
  8335. }
  8336. return true;
  8337. }
  8338. //----------------------------------------------------------------------------------------------
  8339. /**
  8340. * Parse parameters for is_flexible subsection
  8341. */
  8342. bool ParseFlexibleJiggle( s_jigglebone_t *jiggleInfo )
  8343. {
  8344. jiggleInfo->data.flags |= (JIGGLE_IS_FLEXIBLE | JIGGLE_HAS_LENGTH_CONSTRAINT);
  8345. bool gotOpenBracket = false;
  8346. while (true)
  8347. {
  8348. if (GetToken( true ) == false)
  8349. {
  8350. MdlError( "$jigglebone:is_flexible: parse error\n", g_iLinecount, g_szLine );
  8351. return false;
  8352. }
  8353. if (!stricmp( token, "{" ))
  8354. {
  8355. gotOpenBracket = true;
  8356. }
  8357. else if (!gotOpenBracket)
  8358. {
  8359. MdlError( "$jigglebone:is_flexible: missing '{'\n", g_iLinecount, g_szLine );
  8360. return false;
  8361. }
  8362. else if (!stricmp( token, "}" ))
  8363. {
  8364. // definition complete
  8365. break;
  8366. }
  8367. else if (!stricmp( token, "yaw_stiffness" ))
  8368. {
  8369. jiggleInfo->data.yawStiffness = ParseJiggleStiffness();
  8370. }
  8371. else if (!stricmp( token, "yaw_damping" ))
  8372. {
  8373. jiggleInfo->data.yawDamping = ParseJiggleStiffness();
  8374. }
  8375. else if (!stricmp( token, "pitch_stiffness" ))
  8376. {
  8377. jiggleInfo->data.pitchStiffness = ParseJiggleStiffness();
  8378. }
  8379. else if (!stricmp( token, "pitch_damping" ))
  8380. {
  8381. jiggleInfo->data.pitchDamping = ParseJiggleStiffness();
  8382. }
  8383. else if (!stricmp( token, "along_stiffness" ))
  8384. {
  8385. jiggleInfo->data.alongStiffness = ParseJiggleStiffness();
  8386. }
  8387. else if (!stricmp( token, "along_damping" ))
  8388. {
  8389. jiggleInfo->data.alongDamping = ParseJiggleStiffness();
  8390. }
  8391. else if (!stricmp( token, "allow_length_flex" ))
  8392. {
  8393. jiggleInfo->data.flags &= ~JIGGLE_HAS_LENGTH_CONSTRAINT;
  8394. }
  8395. else if (ParseCommonJiggle( jiggleInfo ) == false)
  8396. {
  8397. MdlError( "$jigglebone:is_flexible: invalid syntax '%s'\n", token );
  8398. return false;
  8399. }
  8400. }
  8401. return true;
  8402. }
  8403. //----------------------------------------------------------------------------------------------
  8404. /**
  8405. * Parse parameters for is_rigid subsection
  8406. */
  8407. bool ParseRigidJiggle( s_jigglebone_t *jiggleInfo )
  8408. {
  8409. jiggleInfo->data.flags |= (JIGGLE_IS_RIGID | JIGGLE_HAS_LENGTH_CONSTRAINT);
  8410. bool gotOpenBracket = false;
  8411. while (true)
  8412. {
  8413. if (GetToken( true ) == false)
  8414. {
  8415. MdlError( "$jigglebone:is_rigid: parse error\n", g_iLinecount, g_szLine );
  8416. return false;
  8417. }
  8418. if (!stricmp( token, "{" ))
  8419. {
  8420. gotOpenBracket = true;
  8421. }
  8422. else if (!gotOpenBracket)
  8423. {
  8424. MdlError( "$jigglebone:is_rigid: missing '{'\n", g_iLinecount, g_szLine );
  8425. return false;
  8426. }
  8427. else if (!stricmp( token, "}" ))
  8428. {
  8429. // definition complete
  8430. break;
  8431. }
  8432. else if (ParseCommonJiggle( jiggleInfo ) == false)
  8433. {
  8434. MdlError( "$jigglebone:is_rigid: invalid syntax '%s'\n", token );
  8435. return false;
  8436. }
  8437. }
  8438. return true;
  8439. }
  8440. //----------------------------------------------------------------------------------------------
  8441. /**
  8442. * Parse parameters for has_base_spring subsection
  8443. */
  8444. bool ParseBaseSpringJiggle( s_jigglebone_t *jiggleInfo )
  8445. {
  8446. jiggleInfo->data.flags |= JIGGLE_HAS_BASE_SPRING;
  8447. bool gotOpenBracket = false;
  8448. while (true)
  8449. {
  8450. if (GetToken( true ) == false)
  8451. {
  8452. MdlError( "$jigglebone:is_rigid: parse error\n", g_iLinecount, g_szLine );
  8453. return false;
  8454. }
  8455. if (!stricmp( token, "{" ))
  8456. {
  8457. gotOpenBracket = true;
  8458. }
  8459. else if (!gotOpenBracket)
  8460. {
  8461. MdlError( "$jigglebone:is_rigid: missing '{'\n", g_iLinecount, g_szLine );
  8462. return false;
  8463. }
  8464. else if (!stricmp( token, "}" ))
  8465. {
  8466. // definition complete
  8467. break;
  8468. }
  8469. else if (!stricmp( token, "stiffness" ))
  8470. {
  8471. jiggleInfo->data.baseStiffness = ParseJiggleStiffness();
  8472. }
  8473. else if (!stricmp( token, "damping" ))
  8474. {
  8475. jiggleInfo->data.baseDamping = ParseJiggleStiffness();
  8476. }
  8477. else if (!stricmp( token, "left_constraint" ))
  8478. {
  8479. if ( !GetToken( false ) )
  8480. {
  8481. return false;
  8482. }
  8483. jiggleInfo->data.baseMinLeft = verify_atof( token );
  8484. if ( !GetToken( false ) )
  8485. {
  8486. return false;
  8487. }
  8488. jiggleInfo->data.baseMaxLeft = verify_atof( token );
  8489. }
  8490. else if (!stricmp( token, "left_friction" ))
  8491. {
  8492. if ( !GetToken( false ) )
  8493. {
  8494. return false;
  8495. }
  8496. jiggleInfo->data.baseLeftFriction = verify_atof( token );
  8497. }
  8498. else if (!stricmp( token, "up_constraint" ))
  8499. {
  8500. if ( !GetToken( false ) )
  8501. {
  8502. return false;
  8503. }
  8504. jiggleInfo->data.baseMinUp = verify_atof( token );
  8505. if ( !GetToken( false ) )
  8506. {
  8507. return false;
  8508. }
  8509. jiggleInfo->data.baseMaxUp = verify_atof( token );
  8510. }
  8511. else if (!stricmp( token, "up_friction" ))
  8512. {
  8513. if ( !GetToken( false ) )
  8514. {
  8515. return false;
  8516. }
  8517. jiggleInfo->data.baseUpFriction = verify_atof( token );
  8518. }
  8519. else if (!stricmp( token, "forward_constraint" ))
  8520. {
  8521. if ( !GetToken( false ) )
  8522. {
  8523. return false;
  8524. }
  8525. jiggleInfo->data.baseMinForward = verify_atof( token );
  8526. if ( !GetToken( false ) )
  8527. {
  8528. return false;
  8529. }
  8530. jiggleInfo->data.baseMaxForward = verify_atof( token );
  8531. }
  8532. else if (!stricmp( token, "forward_friction" ))
  8533. {
  8534. if ( !GetToken( false ) )
  8535. {
  8536. return false;
  8537. }
  8538. jiggleInfo->data.baseForwardFriction = verify_atof( token );
  8539. }
  8540. else if (!stricmp( token, "base_mass" ))
  8541. {
  8542. if ( !GetToken( false ) )
  8543. {
  8544. return false;
  8545. }
  8546. jiggleInfo->data.baseMass = verify_atof( token );
  8547. }
  8548. else if (ParseCommonJiggle( jiggleInfo ) == false)
  8549. {
  8550. MdlError( "$jigglebone:has_base_spring: invalid syntax '%s'\n", token );
  8551. return false;
  8552. }
  8553. }
  8554. return true;
  8555. }
  8556. //----------------------------------------------------------------------------------------------
  8557. /**
  8558. * Parse $jigglebone parameters
  8559. */
  8560. void Cmd_JiggleBone( void )
  8561. {
  8562. struct s_jigglebone_t *jiggleInfo = &g_jigglebones[ g_numjigglebones ];
  8563. // bone name
  8564. GetToken( false );
  8565. strcpyn( jiggleInfo->bonename, token );
  8566. // default values
  8567. memset( &jiggleInfo->data, 0, sizeof( mstudiojigglebone_t ) );
  8568. jiggleInfo->data.length = 10.0f;
  8569. jiggleInfo->data.yawStiffness = 100.0f;
  8570. jiggleInfo->data.pitchStiffness = 100.0f;
  8571. jiggleInfo->data.alongStiffness = 100.0f;
  8572. jiggleInfo->data.baseStiffness = 100.0f;
  8573. jiggleInfo->data.baseMinUp = -100.0f;
  8574. jiggleInfo->data.baseMaxUp = 100.0f;
  8575. jiggleInfo->data.baseMinLeft = -100.0f;
  8576. jiggleInfo->data.baseMaxLeft = 100.0f;
  8577. jiggleInfo->data.baseMinForward = -100.0f;
  8578. jiggleInfo->data.baseMaxForward = 100.0f;
  8579. bool gotOpenBracket = false;
  8580. while (true)
  8581. {
  8582. if (GetToken( true ) == false)
  8583. {
  8584. MdlError( "$jigglebone: parse error\n", g_iLinecount, g_szLine );
  8585. return;
  8586. }
  8587. if (!stricmp( token, "{" ))
  8588. {
  8589. gotOpenBracket = true;
  8590. }
  8591. else if (!gotOpenBracket)
  8592. {
  8593. MdlError( "$jigglebone: missing '{'\n", g_iLinecount, g_szLine );
  8594. return;
  8595. }
  8596. else if (!stricmp( token, "}" ))
  8597. {
  8598. // definition complete
  8599. break;
  8600. }
  8601. else if (!stricmp( token, "is_flexible" ))
  8602. {
  8603. if (ParseFlexibleJiggle( jiggleInfo ) == false)
  8604. {
  8605. return;
  8606. }
  8607. }
  8608. else if (!stricmp( token, "is_rigid" ))
  8609. {
  8610. if (ParseRigidJiggle( jiggleInfo ) == false)
  8611. {
  8612. return;
  8613. }
  8614. }
  8615. else if (!stricmp( token, "has_base_spring" ))
  8616. {
  8617. if (ParseBaseSpringJiggle( jiggleInfo ) == false)
  8618. {
  8619. return;
  8620. }
  8621. }
  8622. else
  8623. {
  8624. MdlError( "$jigglebone: invalid syntax '%s'\n", token );
  8625. return;
  8626. }
  8627. }
  8628. if (!g_quiet)
  8629. Msg( "Marking bone %s as a jiggle bone\n", jiggleInfo->bonename );
  8630. g_numjigglebones++;
  8631. }
  8632. //-----------------------------------------------------------------------------
  8633. // Purpose: specify bones to store, even if nothing references them
  8634. //-----------------------------------------------------------------------------
  8635. void Cmd_IncludeModel( )
  8636. {
  8637. GetToken( false );
  8638. strcpyn( g_includemodel[g_numincludemodels].name, "models/" );
  8639. strcat( g_includemodel[g_numincludemodels].name, token );
  8640. g_numincludemodels++;
  8641. }
  8642. /*
  8643. =================
  8644. =================
  8645. */
  8646. void Grab_Vertexanimation( s_source_t *psource, const char *pAnimName )
  8647. {
  8648. char cmd[1024];
  8649. int index;
  8650. Vector pos;
  8651. Vector normal;
  8652. int t = -1;
  8653. int count = 0;
  8654. static s_vertanim_t tmpvanim[MAXSTUDIOVERTS*4];
  8655. s_sourceanim_t *pAnim = FindSourceAnim( psource, pAnimName );
  8656. if ( !pAnim )
  8657. {
  8658. MdlError( "Unknown animation %s(%d) : %s\n", pAnimName, g_iLinecount, g_szLine );
  8659. }
  8660. while (GetLineInput())
  8661. {
  8662. if (sscanf( g_szLine, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &normal[0], &normal[1], &normal[2] ) == 7)
  8663. {
  8664. if ( pAnim->startframe < 0 )
  8665. {
  8666. MdlError( "Missing frame start(%d) : %s", g_iLinecount, g_szLine );
  8667. }
  8668. if (t < 0)
  8669. {
  8670. MdlError( "VTA Frame Sync (%d) : %s", g_iLinecount, g_szLine );
  8671. }
  8672. tmpvanim[count].vertex = index;
  8673. VectorCopy( pos, tmpvanim[count].pos );
  8674. VectorCopy( normal, tmpvanim[count].normal );
  8675. count++;
  8676. if ( index >= psource->numvertices )
  8677. {
  8678. psource->numvertices = index + 1;
  8679. }
  8680. }
  8681. else
  8682. {
  8683. // flush data
  8684. if (count)
  8685. {
  8686. pAnim->numvanims[t] = count;
  8687. pAnim->vanim[t] = (s_vertanim_t *)calloc( count, sizeof( s_vertanim_t ) );
  8688. memcpy( pAnim->vanim[t], tmpvanim, count * sizeof( s_vertanim_t ) );
  8689. }
  8690. else if (t > 0)
  8691. {
  8692. pAnim->numvanims[t] = 0;
  8693. }
  8694. // next command
  8695. if (sscanf( g_szLine, "%1023s %d", cmd, &index ))
  8696. {
  8697. if (stricmp( cmd, "time" ) == 0)
  8698. {
  8699. t = index;
  8700. count = 0;
  8701. if ( t < pAnim->startframe )
  8702. {
  8703. MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine );
  8704. }
  8705. if ( t > pAnim->endframe )
  8706. {
  8707. MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine );
  8708. }
  8709. t -= pAnim->startframe;
  8710. }
  8711. else if ( !Q_stricmp( cmd, "end" ) )
  8712. {
  8713. pAnim->numframes = pAnim->endframe - pAnim->startframe + 1;
  8714. return;
  8715. }
  8716. else
  8717. {
  8718. MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
  8719. }
  8720. }
  8721. else
  8722. {
  8723. MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
  8724. }
  8725. }
  8726. }
  8727. MdlError( "unexpected EOF: %s\n", psource->filename );
  8728. }
  8729. bool GetGlobalFilePath( const char *pSrc, char *pFullPath, int nMaxLen )
  8730. {
  8731. char pFileName[1024];
  8732. Q_strncpy( pFileName, ExpandPath( (char*)pSrc ), sizeof(pFileName) );
  8733. // This is kinda gross. . . doing the same work in cmdlib on SafeOpenRead.
  8734. int nPathLength;
  8735. if( CmdLib_HasBasePath( pFileName, nPathLength ) )
  8736. {
  8737. char tmp[1024];
  8738. int i;
  8739. int nNumBasePaths = CmdLib_GetNumBasePaths();
  8740. for( i = 0; i < nNumBasePaths; i++ )
  8741. {
  8742. strcpy( tmp, CmdLib_GetBasePath( i ) );
  8743. strcat( tmp, pFileName + nPathLength );
  8744. struct _stat buf;
  8745. int rt = _stat( tmp, &buf );
  8746. if ( rt != -1 && ( buf.st_size > 0 ) && ( ( buf.st_mode & _S_IFDIR ) == 0 ) )
  8747. {
  8748. Q_strncpy( pFullPath, tmp, nMaxLen );
  8749. return true;
  8750. }
  8751. }
  8752. return false;
  8753. }
  8754. struct _stat buf;
  8755. int rt = _stat( pFileName, &buf );
  8756. if ( rt != -1 && ( buf.st_size > 0 ) && ( ( buf.st_mode & _S_IFDIR ) == 0 ) )
  8757. {
  8758. Q_strncpy( pFullPath, pFileName, nMaxLen );
  8759. return true;
  8760. }
  8761. return false;
  8762. }
  8763. int OpenGlobalFile( char *src )
  8764. {
  8765. int time1;
  8766. char filename[1024];
  8767. strcpy( filename, ExpandPath( src ) );
  8768. // if the file doesn't exist it might be a relative content dir path
  8769. if ( g_bContentRootRelative && !g_pFullFileSystem->FileExists( filename ) )
  8770. g_pFullFileSystem->RelativePathToFullPath( src, "CONTENT", filename, sizeof( filename ) );
  8771. int pathLength;
  8772. int numBasePaths = CmdLib_GetNumBasePaths();
  8773. // This is kinda gross. . . doing the same work in cmdlib on SafeOpenRead.
  8774. if( CmdLib_HasBasePath( filename, pathLength ) )
  8775. {
  8776. char tmp[1024];
  8777. int i;
  8778. for( i = 0; i < numBasePaths; i++ )
  8779. {
  8780. strcpy( tmp, CmdLib_GetBasePath( i ) );
  8781. strcat( tmp, filename + pathLength );
  8782. if( g_bCreateMakefile )
  8783. {
  8784. CreateMakefile_AddDependency( tmp );
  8785. return 0;
  8786. }
  8787. time1 = FileTime( tmp );
  8788. if( time1 != -1 )
  8789. {
  8790. if ((g_fpInput = fopen(tmp, "r" ) ) == 0)
  8791. {
  8792. MdlWarning( "reader: could not open file '%s'\n", src );
  8793. return 0;
  8794. }
  8795. else
  8796. {
  8797. return 1;
  8798. }
  8799. }
  8800. }
  8801. return 0;
  8802. }
  8803. else
  8804. {
  8805. time1 = FileTime (filename);
  8806. if (time1 == -1)
  8807. return 0;
  8808. if( g_bCreateMakefile )
  8809. {
  8810. CreateMakefile_AddDependency( filename );
  8811. return 0;
  8812. }
  8813. if ((g_fpInput = fopen(filename, "r" ) ) == 0)
  8814. {
  8815. MdlWarning( "reader: could not open file '%s'\n", src );
  8816. return 0;
  8817. }
  8818. return 1;
  8819. }
  8820. }
  8821. int Load_VTA( s_source_t *psource )
  8822. {
  8823. char cmd[1024];
  8824. int option;
  8825. if (!OpenGlobalFile( psource->filename ))
  8826. return 0;
  8827. if (!g_quiet)
  8828. printf ("VTA MODEL %s\n", psource->filename);
  8829. g_iLinecount = 0;
  8830. while (GetLineInput())
  8831. {
  8832. g_iLinecount++;
  8833. const int numRead = sscanf( g_szLine, "%s %d", cmd, &option );
  8834. // No Command Was Parsed, Blank Line Usually
  8835. if ((numRead == EOF) || (numRead == 0))
  8836. continue;
  8837. if (stricmp( cmd, "version" ) == 0)
  8838. {
  8839. if (option != 1)
  8840. {
  8841. MdlError("bad version\n");
  8842. }
  8843. }
  8844. else if (stricmp( cmd, "nodes" ) == 0)
  8845. {
  8846. psource->numbones = Grab_Nodes( psource->localBone );
  8847. }
  8848. else if (stricmp( cmd, "skeleton" ) == 0)
  8849. {
  8850. Grab_Animation( psource, "VertexAnimation" );
  8851. }
  8852. else if (stricmp( cmd, "vertexanimation" ) == 0)
  8853. {
  8854. Grab_Vertexanimation( psource, "VertexAnimation" );
  8855. }
  8856. else
  8857. {
  8858. MdlWarning("unknown studio command \"%s\" in vta file: \"%s\" line: %d\n", cmd, psource->filename, g_iLinecount - 1 );
  8859. }
  8860. }
  8861. fclose( g_fpInput );
  8862. return 1;
  8863. }
  8864. void Grab_AxisInterpBones( )
  8865. {
  8866. char cmd[1024], tmp[1025];
  8867. Vector basepos;
  8868. s_axisinterpbone_t *pAxis = NULL;
  8869. s_axisinterpbone_t *pBone = &g_axisinterpbones[g_numaxisinterpbones];
  8870. while (GetLineInput())
  8871. {
  8872. if (IsEnd( g_szLine ))
  8873. {
  8874. return;
  8875. }
  8876. int i = sscanf( g_szLine, "%1023s \"%[^\"]\" \"%[^\"]\" \"%[^\"]\" \"%[^\"]\" %d", cmd, pBone->bonename, tmp, pBone->controlname, tmp, &pBone->axis );
  8877. if (i == 6 && stricmp( cmd, "bone") == 0)
  8878. {
  8879. // printf( "\"%s\" \"%s\" \"%s\" \"%s\"\n", cmd, pBone->bonename, tmp, pBone->controlname );
  8880. pAxis = pBone;
  8881. pBone->axis = pBone->axis - 1; // MAX uses 1..3, engine 0..2
  8882. g_numaxisinterpbones++;
  8883. pBone = &g_axisinterpbones[g_numaxisinterpbones];
  8884. }
  8885. else if (stricmp( cmd, "display" ) == 0)
  8886. {
  8887. // skip all display info
  8888. }
  8889. else if (stricmp( cmd, "type" ) == 0)
  8890. {
  8891. // skip all type info
  8892. }
  8893. else if (stricmp( cmd, "basepos" ) == 0)
  8894. {
  8895. i = sscanf( g_szLine, "basepos %f %f %f", &basepos.x, &basepos.y, &basepos.z );
  8896. // skip all type info
  8897. }
  8898. else if (stricmp( cmd, "axis" ) == 0)
  8899. {
  8900. Vector pos;
  8901. QAngle rot;
  8902. int j;
  8903. i = sscanf( g_szLine, "axis %d %f %f %f %f %f %f", &j, &pos[0], &pos[1], &pos[2], &rot[2], &rot[0], &rot[1] );
  8904. if (i == 7)
  8905. {
  8906. VectorAdd( basepos, pos, pAxis->pos[j] );
  8907. AngleQuaternion( rot, pAxis->quat[j] );
  8908. }
  8909. }
  8910. }
  8911. }
  8912. bool Grab_AimAtBones( )
  8913. {
  8914. s_aimatbone_t *pAimAtBone( &g_aimatbones[g_numaimatbones] );
  8915. // Already know it's <aimconstraint> in the first string, otherwise wouldn't be here
  8916. if ( sscanf( g_szLine, "%*s %127s %127s %127s", pAimAtBone->bonename, pAimAtBone->parentname, pAimAtBone->aimname ) == 3 )
  8917. {
  8918. g_numaimatbones++;
  8919. char cmd[1024];
  8920. Vector vector;
  8921. while ( GetLineInput() )
  8922. {
  8923. g_iLinecount++;
  8924. if (IsEnd( g_szLine ))
  8925. {
  8926. return false;
  8927. }
  8928. if ( sscanf( g_szLine, "%1024s %f %f %f", cmd, &vector[0], &vector[1], &vector[2] ) != 4 )
  8929. {
  8930. // Allow blank lines to be skipped without error
  8931. bool allSpace( true );
  8932. for ( const char *pC( g_szLine ); *pC != '\0' && pC < ( g_szLine + 4096 ); ++pC )
  8933. {
  8934. if ( !V_isspace( *pC ) )
  8935. {
  8936. allSpace = false;
  8937. break;
  8938. }
  8939. }
  8940. if ( allSpace )
  8941. {
  8942. continue;
  8943. }
  8944. return true;
  8945. }
  8946. if ( stricmp( cmd, "<aimvector>" ) == 0)
  8947. {
  8948. // Make sure these are unit length on read
  8949. VectorNormalize( vector );
  8950. pAimAtBone->aimvector = vector;
  8951. }
  8952. else if ( stricmp( cmd, "<upvector>" ) == 0)
  8953. {
  8954. // Make sure these are unit length on read
  8955. VectorNormalize( vector );
  8956. pAimAtBone->upvector = vector;
  8957. }
  8958. else if ( stricmp( cmd, "<basepos>" ) == 0)
  8959. {
  8960. pAimAtBone->basepos = vector;
  8961. }
  8962. else
  8963. {
  8964. return true;
  8965. }
  8966. }
  8967. }
  8968. // If we get here, we're at EOF
  8969. return false;
  8970. }
  8971. void Grab_QuatInterpBones( )
  8972. {
  8973. char cmd[1024];
  8974. Vector basepos;
  8975. RadianEuler rotateaxis( 0.0f, 0.0f, 0.0f );
  8976. RadianEuler jointorient( 0.0f, 0.0f, 0.0f );
  8977. s_quatinterpbone_t *pAxis = NULL;
  8978. s_quatinterpbone_t *pBone = &g_quatinterpbones[g_numquatinterpbones];
  8979. while (GetLineInput())
  8980. {
  8981. g_iLinecount++;
  8982. if (IsEnd( g_szLine ))
  8983. {
  8984. return;
  8985. }
  8986. int i = sscanf( g_szLine, "%s %s %s %s %s", cmd, pBone->bonename, pBone->parentname, pBone->controlparentname, pBone->controlname );
  8987. while ( i == 4 && stricmp( cmd, "<aimconstraint>" ) == 0 )
  8988. {
  8989. // If Grab_AimAtBones() returns false, there file is at EOF
  8990. if ( !Grab_AimAtBones() )
  8991. {
  8992. return;
  8993. }
  8994. // Grab_AimAtBones will read input into g_szLine same as here until it gets a line it doesn't understand, at which point
  8995. // it will exit leaving that line in g_szLine, so check for the end and scan the current buffer again and continue on with
  8996. // the normal QuatInterpBones process
  8997. i = sscanf( g_szLine, "%s %s %s %s %s", cmd, pBone->bonename, pBone->parentname, pBone->controlparentname, pBone->controlname );
  8998. }
  8999. if (i == 5 && stricmp( cmd, "<helper>") == 0)
  9000. {
  9001. // printf( "\"%s\" \"%s\" \"%s\" \"%s\"\n", cmd, pBone->bonename, tmp, pBone->controlname );
  9002. pAxis = pBone;
  9003. g_numquatinterpbones++;
  9004. pBone = &g_quatinterpbones[g_numquatinterpbones];
  9005. }
  9006. else if ( i > 0 )
  9007. {
  9008. // There was a bug before which could cause the same command to be parsed twice
  9009. // because if the sscanf above completely fails, it will return 0 and not
  9010. // change the contents of cmd, so i should be greater than 0 in order for
  9011. // any of these checks to be valid... Still kind of buggy as these checks
  9012. // do case insensitive stricmp but then the sscanf does case sensitive
  9013. // matching afterwards... Should probably change those to
  9014. // sscanf( g_szLine, "%*s %f ... ) etc...
  9015. if ( stricmp( cmd, "<display>" ) == 0)
  9016. {
  9017. // skip all display info
  9018. Vector size;
  9019. float distance;
  9020. i = sscanf( g_szLine, "<display> %f %f %f %f",
  9021. &size[0], &size[1], &size[2],
  9022. &distance );
  9023. if (i == 4)
  9024. {
  9025. pAxis->percentage = distance / 100.0;
  9026. pAxis->size = size;
  9027. }
  9028. else
  9029. {
  9030. MdlError( "Line %d: Unable to parse procedual <display> bone: %s", g_iLinecount, g_szLine );
  9031. }
  9032. }
  9033. else if ( stricmp( cmd, "<basepos>" ) == 0)
  9034. {
  9035. i = sscanf( g_szLine, "<basepos> %f %f %f", &basepos.x, &basepos.y, &basepos.z );
  9036. // skip all type info
  9037. }
  9038. else if ( stricmp( cmd, "<rotateaxis>" ) == 0)
  9039. {
  9040. i = sscanf( g_szLine, "%*s %f %f %f", &rotateaxis.x, &rotateaxis.y, &rotateaxis.z );
  9041. rotateaxis.x = DEG2RAD( rotateaxis.x );
  9042. rotateaxis.y = DEG2RAD( rotateaxis.y );
  9043. rotateaxis.z = DEG2RAD( rotateaxis.z );
  9044. }
  9045. else if ( stricmp( cmd, "<jointorient>" ) == 0)
  9046. {
  9047. i = sscanf( g_szLine, "%*s %f %f %f", &jointorient.x, &jointorient.y, &jointorient.z );
  9048. jointorient.x = DEG2RAD( jointorient.x );
  9049. jointorient.y = DEG2RAD( jointorient.y );
  9050. jointorient.z = DEG2RAD( jointorient.z );
  9051. }
  9052. else if ( stricmp( cmd, "<trigger>" ) == 0)
  9053. {
  9054. float tolerance;
  9055. RadianEuler trigger;
  9056. Vector pos;
  9057. RadianEuler ang;
  9058. QAngle rot;
  9059. int j;
  9060. i = sscanf( g_szLine, "<trigger> %f %f %f %f %f %f %f %f %f %f",
  9061. &tolerance,
  9062. &trigger.x, &trigger.y, &trigger.z,
  9063. &ang.x, &ang.y, &ang.z,
  9064. &pos.x, &pos.y, &pos.z );
  9065. if (i == 10)
  9066. {
  9067. trigger.x = DEG2RAD( trigger.x );
  9068. trigger.y = DEG2RAD( trigger.y );
  9069. trigger.z = DEG2RAD( trigger.z );
  9070. ang.x = DEG2RAD( ang.x );
  9071. ang.y = DEG2RAD( ang.y );
  9072. ang.z = DEG2RAD( ang.z );
  9073. Quaternion q;
  9074. AngleQuaternion( ang, q );
  9075. if ( rotateaxis.x != 0.0 || rotateaxis.y != 0.0 || rotateaxis.z != 0.0 )
  9076. {
  9077. Quaternion q1;
  9078. Quaternion q2;
  9079. AngleQuaternion( rotateaxis, q1 );
  9080. QuaternionMult( q1, q, q2 );
  9081. q = q2;
  9082. }
  9083. if ( jointorient.x != 0.0 || jointorient.y != 0.0 || jointorient.z != 0.0 )
  9084. {
  9085. Quaternion q1;
  9086. Quaternion q2;
  9087. AngleQuaternion( jointorient, q1 );
  9088. QuaternionMult( q, q1, q2 );
  9089. q = q2;
  9090. }
  9091. j = pAxis->numtriggers++;
  9092. pAxis->tolerance[j] = DEG2RAD( tolerance );
  9093. AngleQuaternion( trigger, pAxis->trigger[j] );
  9094. VectorAdd( basepos, pos, pAxis->pos[j] );
  9095. pAxis->quat[j] = q;
  9096. }
  9097. else
  9098. {
  9099. MdlError( "Line %d: Unable to parse procedual <trigger> bone: %s", g_iLinecount, g_szLine );
  9100. }
  9101. }
  9102. else
  9103. {
  9104. MdlError( "Line %d: Unable to parse procedual bone data: %s", g_iLinecount, g_szLine );
  9105. }
  9106. }
  9107. else
  9108. {
  9109. // Allow blank lines to be skipped without error
  9110. bool allSpace( true );
  9111. for ( const char *pC( g_szLine ); *pC != '\0' && pC < ( g_szLine + 4096 ); ++pC )
  9112. {
  9113. if ( !V_isspace( *pC ) )
  9114. {
  9115. allSpace = false;
  9116. break;
  9117. }
  9118. }
  9119. if ( !allSpace )
  9120. {
  9121. MdlError( "Line %d: Unable to parse procedual bone data: %s", g_iLinecount, g_szLine );
  9122. }
  9123. }
  9124. }
  9125. }
  9126. void Load_ProceduralBones( )
  9127. {
  9128. char filename[256];
  9129. char cmd[1024];
  9130. int option;
  9131. GetToken( false );
  9132. strcpy( filename, token );
  9133. if (!OpenGlobalFile( filename ))
  9134. {
  9135. Error("unknown $procedural file \"%s\"\n", filename );
  9136. return;
  9137. }
  9138. g_iLinecount = 0;
  9139. char ext[32];
  9140. Q_ExtractFileExtension( filename, ext, sizeof( ext ) );
  9141. if (stricmp( ext, "vrd") == 0)
  9142. {
  9143. Grab_QuatInterpBones( );
  9144. }
  9145. else
  9146. {
  9147. while (GetLineInput())
  9148. {
  9149. g_iLinecount++;
  9150. const int numRead = sscanf( g_szLine, "%s", cmd, &option );
  9151. // No Command Was Parsed, Blank Line Usually
  9152. if ((numRead == EOF) || (numRead == 0))
  9153. continue;
  9154. if (stricmp( cmd, "version" ) == 0)
  9155. {
  9156. if (option != 1)
  9157. {
  9158. MdlError("bad version\n");
  9159. }
  9160. }
  9161. else if (stricmp( cmd, "proceduralbones" ) == 0)
  9162. {
  9163. Grab_AxisInterpBones( );
  9164. }
  9165. }
  9166. }
  9167. fclose( g_fpInput );
  9168. }
  9169. void Cmd_CD()
  9170. {
  9171. if (cdset)
  9172. MdlError ("Two $cd in one model");
  9173. cdset = true;
  9174. GetToken (false);
  9175. strcpy (cddir[0], token);
  9176. strcat (cddir[0], "/" );
  9177. numdirs = 0;
  9178. }
  9179. void Cmd_ContentRootRelative()
  9180. {
  9181. g_bContentRootRelative = true;
  9182. }
  9183. void Cmd_CDMaterials()
  9184. {
  9185. while (TokenAvailable())
  9186. {
  9187. GetToken (false);
  9188. char szPath[512];
  9189. Q_strncpy( szPath, token, sizeof( szPath ) );
  9190. int len = strlen( szPath );
  9191. if ( len > 0 && szPath[len-1] != '/' && szPath[len-1] != '\\' )
  9192. {
  9193. Q_strncat( szPath, "/", sizeof( szPath ), COPY_ALL_CHARACTERS );
  9194. }
  9195. Q_FixSlashes( szPath );
  9196. cdtextures[numcdtextures] = strdup( szPath );
  9197. numcdtextures++;
  9198. }
  9199. }
  9200. void Cmd_Pushd()
  9201. {
  9202. GetToken(false);
  9203. strcpy( cddir[numdirs+1], cddir[numdirs] );
  9204. strcat( cddir[numdirs+1], token );
  9205. strcat( cddir[numdirs+1], "/" );
  9206. numdirs++;
  9207. }
  9208. void Cmd_Popd()
  9209. {
  9210. if (numdirs > 0)
  9211. numdirs--;
  9212. }
  9213. void Cmd_CollisionModel()
  9214. {
  9215. DoCollisionModel( false );
  9216. }
  9217. void Cmd_CollisionJoints()
  9218. {
  9219. DoCollisionModel( true );
  9220. }
  9221. void Cmd_ExternalTextures()
  9222. {
  9223. MdlWarning( "ignoring $externaltextures, obsolete..." );
  9224. }
  9225. void Cmd_ClipToTextures()
  9226. {
  9227. clip_texcoords = 1;
  9228. }
  9229. void Cmd_CollapseBones()
  9230. {
  9231. g_collapse_bones = true;
  9232. }
  9233. void Cmd_SkinnedLODs()
  9234. {
  9235. g_bSkinnedLODs = true;
  9236. }
  9237. void Cmd_CollapseBonesAggressive()
  9238. {
  9239. g_collapse_bones = true;
  9240. g_collapse_bones_aggressive = true;
  9241. }
  9242. void Cmd_AlwaysCollapse()
  9243. {
  9244. g_collapse_bones = true;
  9245. GetToken(false);
  9246. g_collapse.AddToTail( strdup( token ) );
  9247. }
  9248. void Cmd_CalcTransitions()
  9249. {
  9250. g_bMultistageGraph = true;
  9251. }
  9252. void ProcessStaticProp()
  9253. {
  9254. g_staticprop = true;
  9255. gflags |= STUDIOHDR_FLAGS_STATIC_PROP;
  9256. }
  9257. void Cmd_StaticProp()
  9258. {
  9259. ProcessStaticProp();
  9260. }
  9261. void Cmd_ZBrush()
  9262. {
  9263. g_bZBrush = true;
  9264. }
  9265. void Cmd_RealignBones()
  9266. {
  9267. g_realignbones = true;
  9268. }
  9269. void Cmd_BaseLOD()
  9270. {
  9271. Cmd_LOD( "$lod" );
  9272. }
  9273. void Cmd_KeyValues()
  9274. {
  9275. Option_KeyValues( &g_KeyValueText );
  9276. }
  9277. void Cmd_ConstDirectionalLight()
  9278. {
  9279. gflags |= STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT;
  9280. GetToken (false);
  9281. g_constdirectionalightdot = (byte)( verify_atof(token) * 255.0f );
  9282. }
  9283. void Cmd_MinLOD()
  9284. {
  9285. GetToken( false );
  9286. g_minLod = atoi( token );
  9287. // "minlod" rules over "allowrootlods"
  9288. if ( g_numAllowedRootLODs > 0 && g_numAllowedRootLODs < g_minLod )
  9289. {
  9290. MdlWarning( "$minlod %d overrides $allowrootlods %d, proceeding with $allowrootlods %d.\n", g_minLod, g_numAllowedRootLODs, g_minLod );
  9291. g_numAllowedRootLODs = g_minLod;
  9292. }
  9293. }
  9294. void Cmd_AllowRootLODs()
  9295. {
  9296. GetToken( false );
  9297. g_numAllowedRootLODs = atoi( token );
  9298. // Root LOD restriction has to obey "minlod" request
  9299. if ( g_numAllowedRootLODs > 0 && g_numAllowedRootLODs < g_minLod )
  9300. {
  9301. MdlWarning( "$allowrootlods %d is conflicting with $minlod %d, proceeding with $allowrootlods %d.\n", g_numAllowedRootLODs, g_minLod, g_minLod );
  9302. g_numAllowedRootLODs = g_minLod;
  9303. }
  9304. }
  9305. void Cmd_BoneSaveFrame( )
  9306. {
  9307. s_bonesaveframe_t tmp;
  9308. // bone name
  9309. GetToken( false );
  9310. strcpyn( tmp.name, token );
  9311. tmp.bSavePos = false;
  9312. tmp.bSaveRot = false;
  9313. tmp.bSaveRot64 = false;
  9314. while (TokenAvailable( ))
  9315. {
  9316. GetToken( false );
  9317. if (stricmp( "position", token ) == 0)
  9318. {
  9319. tmp.bSavePos = true;
  9320. }
  9321. else if (stricmp( "rotation", token ) == 0)
  9322. {
  9323. tmp.bSaveRot = true;
  9324. }
  9325. else if (stricmp( "rotation64", token ) == 0)
  9326. {
  9327. tmp.bSaveRot64 = true;
  9328. }
  9329. else
  9330. {
  9331. MdlError( "unknown option \"%s\" on $bonesaveframe : %s\n", token, tmp.name );
  9332. }
  9333. }
  9334. g_bonesaveframe.AddToTail( tmp );
  9335. }
  9336. CClothProxyCompiler *GetClothProxyCompiler()
  9337. {
  9338. if ( !g_pClothProxyCompiler )
  9339. {
  9340. // just create a default cloth compiler with default options and start appending cloth to it
  9341. g_pClothProxyCompiler = new CClothProxyCompiler( new CAuthPhysFx );
  9342. CVClothProxyMeshOptions clothOptions;
  9343. clothOptions.m_bDriveMeshesWithBacksolvedJointsOnly = true;
  9344. g_pClothProxyCompiler->Init( clothOptions );
  9345. }
  9346. return g_pClothProxyCompiler;
  9347. }
  9348. CAuthPhysFx *GetAuthPhysFx()
  9349. {
  9350. return GetClothProxyCompiler()->GetFx();
  9351. }
  9352. bool EatClothBool( const char *pName, bool &dst )
  9353. {
  9354. if ( !GetToken( true ) )
  9355. {
  9356. TokenError( "Cloth bool value %s is missing\n", pName );
  9357. return false;
  9358. }
  9359. if ( !V_stricmp( token, "true" ) || !V_stricmp( token, "on" ) || !V_stricmp( token, "yes" ) )
  9360. {
  9361. dst = true;
  9362. }
  9363. else if ( !V_stricmp( token, "false" ) || !V_stricmp( token, "off" ) || !V_stricmp( token, "no" ) )
  9364. {
  9365. dst = false;
  9366. }
  9367. else
  9368. {
  9369. int nBool;
  9370. if ( sscanf( token, "%d", &nBool ) != 1 )
  9371. {
  9372. TokenError( "Cloth value %s is malformed \"%s\", must be a number\n", pName, token );
  9373. return false;
  9374. }
  9375. dst = nBool != 0;
  9376. if ( nBool != 0 && nBool != 1 )
  9377. {
  9378. Warning( "Please use true/false or 0/1 for value %s\n", pName );
  9379. }
  9380. }
  9381. return true;
  9382. }
  9383. bool EatClothFloat( const char *pName, float &dst )
  9384. {
  9385. if ( !GetToken( true ) )
  9386. {
  9387. TokenError( "Cloth value %s is missing\n", pName );
  9388. return false;
  9389. }
  9390. if ( sscanf( token, "%f", &dst ) != 1 )
  9391. {
  9392. TokenError( "Cloth value %s is malformed \"%s\", must be a number\n", pName, token );
  9393. return false;
  9394. }
  9395. return true;
  9396. }
  9397. static bool s_bFlexClothBorderJoints = false;
  9398. QAngle s_angClothPrerotate(0,0,0);
  9399. void ParseClothKeyvalues()
  9400. {
  9401. // Simply read in the block between { }s as text
  9402. // and plop it out unchanged into the .mdl file.
  9403. // Make sure to respect the fact that we may have nested {}s
  9404. int nLevel = 0;
  9405. Assert( token[ 0 ] == '{' );
  9406. struct BoolVal_t
  9407. {
  9408. const char *pKey;
  9409. bool *pBool;
  9410. };
  9411. struct FloatVal_t
  9412. {
  9413. const char *pKey;
  9414. float *pFloat;
  9415. };
  9416. BoolVal_t boolVals[] = {
  9417. { "world_collision", &GetAuthPhysFx()->m_bCanCollideWithWorldCapsulesAndSpheres },
  9418. { "add_stiffness_rods", &GetAuthPhysFx()->m_bAddStiffnessRods },
  9419. { "rigid_edge_hinges", &GetAuthPhysFx()->m_bRigidEdgeHinges },
  9420. { "flex_borders", &s_bFlexClothBorderJoints }
  9421. };
  9422. FloatVal_t floatVals[] = {
  9423. { "local_position", &GetAuthPhysFx()->m_flLocalForce },
  9424. { "local_rotation", &GetAuthPhysFx()->m_flLocalRotation },
  9425. { "surface_stretch", &GetAuthPhysFx()->m_flDefaultSurfaceStretch },
  9426. { "thread_stretch", &GetAuthPhysFx()->m_flDefaultThreadStretch },
  9427. { "gravity_scale", &GetAuthPhysFx()->m_flDefaultGravityScale },
  9428. { "vel_air_drag", &GetAuthPhysFx()->m_flDefaultVelAirDrag },
  9429. { "exp_air_drag", &GetAuthPhysFx()->m_flDefaultExpAirDrag },
  9430. { "vel_quad_air_drag", &GetAuthPhysFx()->m_flDefaultVelQuadAirDrag },
  9431. { "exp_quad_air_drag", &GetAuthPhysFx()->m_flDefaultExpQuadAirDrag },
  9432. { "vel_rod_air_drag", &GetAuthPhysFx()->m_flDefaultVelRodAirDrag },
  9433. { "exp_rod_air_drag", &GetAuthPhysFx()->m_flDefaultExpRodAirDrag },
  9434. { "quad_vel_smooth_rate", &GetAuthPhysFx()->m_flQuadVelocitySmoothRate },
  9435. { "rod_vel_smooth_rate", &GetAuthPhysFx()->m_flRodVelocitySmoothRate },
  9436. { "windage", &GetAuthPhysFx()->m_flWindage },
  9437. { "wind_drag", &GetAuthPhysFx()->m_flWindDrag },
  9438. { "curvature", &GetAuthPhysFx()->m_flAddCurvature },
  9439. { "quad_velocity_smooth_rate", &GetAuthPhysFx()->m_flQuadVelocitySmoothRate },
  9440. { "rod_velocity_smooth_rate", &GetAuthPhysFx()->m_flRodVelocitySmoothRate }
  9441. };
  9442. while ( true )
  9443. {
  9444. char *pToken = token;
  9445. if ( pToken[ 0 ] == '{' )
  9446. {
  9447. nLevel++;
  9448. pToken++;
  9449. }
  9450. else if ( pToken[ 0 ] == '}' )
  9451. {
  9452. nLevel--;
  9453. pToken++;
  9454. }
  9455. if ( *pToken )
  9456. {
  9457. bool bFound = false;
  9458. for ( int i = 0; !bFound && i < ARRAYSIZE( boolVals ); ++i )
  9459. {
  9460. if ( !V_stricmp( pToken, boolVals[ i].pKey ) )
  9461. {
  9462. if ( EatClothBool( boolVals[ i ].pKey, *boolVals[ i ].pBool ) )
  9463. {
  9464. bFound = true;
  9465. break;
  9466. }
  9467. else
  9468. {
  9469. return;
  9470. }
  9471. }
  9472. }
  9473. for ( int i = 0; !bFound && i < ARRAYSIZE( floatVals ); ++i )
  9474. {
  9475. if ( !V_stricmp( pToken, floatVals[ i ].pKey ) )
  9476. {
  9477. if ( EatClothFloat( floatVals[ i ].pKey, *floatVals[ i ].pFloat ) )
  9478. {
  9479. bFound = true;
  9480. break;
  9481. }
  9482. else
  9483. {
  9484. return;
  9485. }
  9486. }
  9487. }
  9488. if ( !bFound )
  9489. {
  9490. if ( !V_stricmp( pToken, "prerotate" ) )
  9491. {
  9492. if ( EatClothFloat( pToken, s_angClothPrerotate.x ) && EatClothFloat( pToken, s_angClothPrerotate.y ) && EatClothFloat( pToken, s_angClothPrerotate.z ) )
  9493. {
  9494. bFound = true;
  9495. }
  9496. else
  9497. {
  9498. return;
  9499. }
  9500. }
  9501. else
  9502. {
  9503. TokenError( "Cloth keyvalue \"%s\" is not recognized\n", pToken );
  9504. return;
  9505. }
  9506. }
  9507. if ( pToken[ V_strlen( pToken ) - 1 ] == '}' )
  9508. {
  9509. nLevel--;
  9510. }
  9511. }
  9512. if ( nLevel <= 0 )
  9513. break;
  9514. if ( !GetToken( true ) )
  9515. break;
  9516. }
  9517. if ( nLevel > 0 )
  9518. {
  9519. TokenError( "Cloth Keyvalue block missing matching braces.\n" );
  9520. }
  9521. }
  9522. void Cmd_Cloth()
  9523. {
  9524. if ( !GetToken( false ) )
  9525. return;
  9526. if ( *token == '{' )
  9527. {
  9528. ParseClothKeyvalues();
  9529. return;
  9530. }
  9531. // append cloth piece to the cloth builder
  9532. // use the full search tree, including mod hierarchy to find the file
  9533. char pFullPath[ MAX_PATH ];
  9534. if ( !GetGlobalFilePath( token, pFullPath, sizeof( pFullPath ) ) )
  9535. {
  9536. TokenError( "Cannot find file %s", token );
  9537. return;
  9538. }
  9539. // When reading, keep the CRLF; this will make ReadFile read it in binary format
  9540. // and also append a couple 0s to the end of the buffer.
  9541. CDmElement *pRoot;
  9542. if ( g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot ) == DMFILEID_INVALID )
  9543. {
  9544. TokenError( "Cannot read file %s", pFullPath );
  9545. return;
  9546. }
  9547. // Load model info: LoadModelInfo( pRoot, pFullPath );
  9548. // Load constraints: LoadConstraints( pRoot );
  9549. //CDmeDag *pSkeleton = pRoot->GetValueElement< CDmeDag >( "skeleton" );
  9550. if ( CDmeModel *pModel = pRoot->GetValueElement< CDmeModel >( "model" ) )
  9551. {
  9552. CVClothProxyMesh meshOptions;
  9553. meshOptions.m_bFlexClothBorders = s_bFlexClothBorderJoints;
  9554. GetClothProxyCompiler()->Append( pModel, .5f, meshOptions );
  9555. g_pDataModel->RemoveFileId( pRoot->GetFileId() );
  9556. }
  9557. else
  9558. {
  9559. TokenError( "File %d has no DmeModel in it\n", pFullPath );
  9560. }
  9561. }
  9562. void Cmd_ClothPlaneCollision()
  9563. {
  9564. if ( !GetToken( false ) )
  9565. return;
  9566. char pFullPath[ MAX_PATH ];
  9567. if ( !GetGlobalFilePath( token, pFullPath, sizeof( pFullPath ) ) )
  9568. {
  9569. TokenError( "Cannot find file %s", token );
  9570. return;
  9571. }
  9572. CDmElement *pRoot;
  9573. if ( g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot ) == DMFILEID_INVALID )
  9574. {
  9575. TokenError( "Cannot read file %s", pFullPath );
  9576. return;
  9577. }
  9578. if ( CDmeModel *pModel = pRoot->GetValueElement< CDmeModel >( "model" ) )
  9579. {
  9580. GetClothProxyCompiler()->AppendPlaneCollision( pModel );
  9581. g_pDataModel->RemoveFileId( pRoot->GetFileId() );
  9582. }
  9583. else
  9584. {
  9585. TokenError( "File %d has no DmeModel in it\n", pFullPath );
  9586. }
  9587. }
  9588. void Cmd_SetDefaultFadeInTime( )
  9589. {
  9590. if ( !GetToken( false ) )
  9591. return;
  9592. g_flDefaultFadeInTime = verify_atof( token );
  9593. }
  9594. void Cmd_SetDefaultFadeOutTime( )
  9595. {
  9596. if ( !GetToken( false ) )
  9597. return;
  9598. g_flDefaultFadeOutTime = verify_atof( token );
  9599. }
  9600. void Cmd_LCaseAllSequences( )
  9601. {
  9602. g_bLCaseAllSequences = true;
  9603. }
  9604. void Cmd_AllowActivityName( )
  9605. {
  9606. if ( !GetToken( false ) )
  9607. return;
  9608. g_AllowedActivityNames.AddToTail( token );
  9609. }
  9610. void Cmd_CollisionPrecision( )
  9611. {
  9612. if ( !GetToken( false ) )
  9613. return;
  9614. g_flCollisionPrecision = verify_atof( token );
  9615. }
  9616. void Cmd_ErrorOnSeqRemapFail( )
  9617. {
  9618. g_bErrorOnSeqRemapFail = true;
  9619. }
  9620. void Cmd_SetModelIntentionallyHasZeroSequences( )
  9621. {
  9622. g_bModelIntentionallyHasZeroSequences = true;
  9623. }
  9624. //
  9625. // This is the master list of the commands a QC file supports.
  9626. // To add a new command to the QC files, add it here.
  9627. //
  9628. MDLCommand_t g_Commands[] =
  9629. {
  9630. { "$cd", Cmd_CD, MC_CURRENT_VERSION },
  9631. { "$modelname", Cmd_Modelname, MC_CURRENT_VERSION },
  9632. { "$internalname", Cmd_InternalName, MC_CURRENT_VERSION },
  9633. { "$cdmaterials", Cmd_CDMaterials, MC_CURRENT_VERSION },
  9634. { "$pushd", Cmd_Pushd, MC_CURRENT_VERSION },
  9635. { "$popd", Cmd_Popd, MC_CURRENT_VERSION },
  9636. { "$scale", Cmd_ScaleUp, MC_CURRENT_VERSION },
  9637. { "$root", Cmd_Root, MC_CURRENT_VERSION },
  9638. { "$controller", Cmd_Controller, MC_CURRENT_VERSION },
  9639. { "$screenalign", Cmd_ScreenAlign, MC_CURRENT_VERSION },
  9640. { "$worldalign", Cmd_WorldAlign, MC_CURRENT_VERSION },
  9641. { "$model", Cmd_Model, MC_CURRENT_VERSION },
  9642. { "$collisionmodel", Cmd_CollisionModel, MC_CURRENT_VERSION },
  9643. { "$collisionjoints", Cmd_CollisionJoints, MC_CURRENT_VERSION },
  9644. { "$collisiontext", Cmd_CollisionText, MC_CURRENT_VERSION },
  9645. { "$appendsource", Cmd_AppendSource, MC_CURRENT_VERSION },
  9646. { "$body", Cmd_Body, MC_CURRENT_VERSION },
  9647. { "$prefer_fbx", Cmd_PreferFbx, MC_CURRENT_VERSION },
  9648. { "$bodygroup", Cmd_Bodygroup, MC_CURRENT_VERSION },
  9649. { "$appendblankbodygroup", Cmd_AppendBlankBodygroup, MC_CURRENT_VERSION },
  9650. { "$bodygrouppreset", Cmd_BodygroupPreset, MC_CURRENT_VERSION },
  9651. { "$animation", Cmd_Animation, MC_CURRENT_VERSION },
  9652. { "$autocenter", Cmd_Autocenter, MC_CURRENT_VERSION },
  9653. { "$sequence", Cmd_Sequence, MC_CURRENT_VERSION },
  9654. { "$append", Cmd_Append, MC_CURRENT_VERSION },
  9655. { "$prepend", Cmd_Prepend, MC_CURRENT_VERSION },
  9656. { "$continue", Cmd_Continue, MC_CURRENT_VERSION },
  9657. { "$declaresequence", Cmd_DeclareSequence, MC_CURRENT_VERSION },
  9658. { "$declareanimation", Cmd_DeclareAnimation, MC_CURRENT_VERSION },
  9659. { "$cmdlist", Cmd_Cmdlist, MC_CURRENT_VERSION },
  9660. { "$animblocksize", Cmd_AnimBlockSize, MC_CURRENT_VERSION },
  9661. { "$weightlist", Cmd_Weightlist, MC_CURRENT_VERSION },
  9662. { "$defaultweightlist", Cmd_DefaultWeightlist, MC_CURRENT_VERSION },
  9663. { "$ikchain", Cmd_IKChain, MC_CURRENT_VERSION },
  9664. { "$ikautoplaylock", Cmd_IKAutoplayLock, MC_CURRENT_VERSION },
  9665. { "$eyeposition", Cmd_Eyeposition, MC_CURRENT_VERSION },
  9666. { "$illumposition", Cmd_Illumposition, MC_CURRENT_VERSION },
  9667. { "$origin", Cmd_Origin, MC_CURRENT_VERSION },
  9668. { "$upaxis", Cmd_UpAxis, MC_CURRENT_VERSION },
  9669. { "$bbox", Cmd_BBox, MC_CURRENT_VERSION },
  9670. { "$bboxonlyverts", Cmd_BBoxOnlyVerts, MC_CURRENT_VERSION },
  9671. { "$cbox", Cmd_CBox, MC_CURRENT_VERSION },
  9672. { "$gamma", Cmd_Gamma, MC_CURRENT_VERSION },
  9673. { "$texturegroup", Cmd_TextureGroup, MC_CURRENT_VERSION },
  9674. { "$hgroup", Cmd_Hitgroup, MC_CURRENT_VERSION },
  9675. { "$hbox", Cmd_Hitbox, 0 },
  9676. { "$hboxset", Cmd_HitboxSet, 0 },
  9677. { "$surfaceprop", Cmd_SurfaceProp, MC_CURRENT_VERSION },
  9678. { "$jointsurfaceprop", Cmd_JointSurfaceProp, MC_CURRENT_VERSION },
  9679. { "$contents", Cmd_Contents, MC_CURRENT_VERSION },
  9680. { "$jointcontents", Cmd_JointContents, MC_CURRENT_VERSION },
  9681. { "$attachment", Cmd_Attachment, MC_CURRENT_VERSION },
  9682. { "$redefineattachment", Cmd_RedefineAttachment, MC_CURRENT_VERSION },
  9683. { "$bonemerge", Cmd_BoneMerge, MC_CURRENT_VERSION },
  9684. { "$bonealwayssetup", Cmd_BoneAlwaysSetup, MC_CURRENT_VERSION },
  9685. { "$externaltextures", Cmd_ExternalTextures, MC_CURRENT_VERSION },
  9686. { "$cliptotextures", Cmd_ClipToTextures, MC_CURRENT_VERSION },
  9687. { "$skinnedLODs", Cmd_SkinnedLODs, MC_CURRENT_VERSION },
  9688. { "$renamebone", Cmd_Renamebone, MC_CURRENT_VERSION },
  9689. { "$stripboneprefix", Cmd_StripBonePrefix, MC_CURRENT_VERSION },
  9690. { "$renamebonesubstr", Cmd_RenameBoneSubstr, MC_CURRENT_VERSION },
  9691. { "$collapsebones", Cmd_CollapseBones, MC_CURRENT_VERSION },
  9692. { "$collapsebonesaggressive", Cmd_CollapseBonesAggressive, MC_CURRENT_VERSION },
  9693. { "$alwayscollapse", Cmd_AlwaysCollapse, MC_CURRENT_VERSION },
  9694. { "$proceduralbones", Load_ProceduralBones, MC_CURRENT_VERSION },
  9695. { "$skiptransition", Cmd_Skiptransition, MC_CURRENT_VERSION },
  9696. { "$calctransitions", Cmd_CalcTransitions, MC_CURRENT_VERSION },
  9697. { "$staticprop", Cmd_StaticProp, MC_CURRENT_VERSION },
  9698. { "$zbrush", Cmd_ZBrush, MC_CURRENT_VERSION },
  9699. { "$realignbones", Cmd_RealignBones, MC_CURRENT_VERSION },
  9700. { "$forcerealign", Cmd_ForceRealign, MC_CURRENT_VERSION },
  9701. { "$lod", Cmd_BaseLOD, MC_CURRENT_VERSION },
  9702. { "$shadowlod", Cmd_ShadowLOD, MC_CURRENT_VERSION },
  9703. { "$poseparameter", Cmd_PoseParameter, MC_CURRENT_VERSION },
  9704. { "$heirarchy", Cmd_ForcedHierarchy, MC_CURRENT_VERSION },
  9705. { "$hierarchy", Cmd_ForcedHierarchy, MC_CURRENT_VERSION },
  9706. { "$insertbone", Cmd_InsertHierarchy, MC_CURRENT_VERSION },
  9707. { "$limitrotation", Cmd_LimitRotation, MC_CURRENT_VERSION },
  9708. { "$definebone", Cmd_DefineBone, MC_CURRENT_VERSION },
  9709. { "$jigglebone", Cmd_JiggleBone, MC_CURRENT_VERSION },
  9710. { "$includemodel", Cmd_IncludeModel, MC_CURRENT_VERSION },
  9711. { "$opaque", Cmd_Opaque, MC_CURRENT_VERSION },
  9712. { "$mostlyopaque", Cmd_TranslucentTwoPass, MC_CURRENT_VERSION },
  9713. //{ "$platform", Cmd_Platform, MC_CURRENT_VERSION },
  9714. { "$keyvalues", Cmd_KeyValues, MC_CURRENT_VERSION },
  9715. { "$obsolete", Cmd_Obsolete, MC_CURRENT_VERSION },
  9716. { "$renamematerial", Cmd_RenameMaterial, MC_CURRENT_VERSION },
  9717. { "$overridematerial", Cmd_OverrideMaterial, MC_CURRENT_VERSION },
  9718. { "$fakevta", Cmd_FakeVTA, MC_CURRENT_VERSION },
  9719. { "$noforcedfade", Cmd_NoForcedFade, MC_CURRENT_VERSION },
  9720. { "$skipboneinbbox", Cmd_SkipBoneInBBox, MC_CURRENT_VERSION },
  9721. { "$forcephonemecrossfade", Cmd_ForcePhonemeCrossfade, MC_CURRENT_VERSION },
  9722. { "$lockbonelengths", Cmd_LockBoneLengths, MC_CURRENT_VERSION },
  9723. { "$unlockdefinebones", Cmd_UnlockDefineBones, MC_CURRENT_VERSION },
  9724. { "$constantdirectionallight", Cmd_ConstDirectionalLight, MC_CURRENT_VERSION },
  9725. { "$minlod", Cmd_MinLOD, MC_CURRENT_VERSION },
  9726. { "$allowrootlods", Cmd_AllowRootLODs, MC_CURRENT_VERSION },
  9727. { "$bonesaveframe", Cmd_BoneSaveFrame, MC_CURRENT_VERSION },
  9728. { "$ambientboost", Cmd_AmbientBoost, MC_CURRENT_VERSION },
  9729. { "$centerbonesonverts", Cmd_CenterBonesOnVerts, MC_CURRENT_VERSION },
  9730. { "$donotcastshadows", Cmd_DoNotCastShadows, MC_CURRENT_VERSION },
  9731. { "$casttextureshadows", Cmd_CastTextureShadows, MC_CURRENT_VERSION },
  9732. { "$motionrollback", Cmd_MotionExtractionRollBack, MC_CURRENT_VERSION },
  9733. { "$sectionframes", Cmd_SectionFrames, MC_CURRENT_VERSION },
  9734. { "$clampworldspace", Cmd_ClampWorldspace, MC_CURRENT_VERSION },
  9735. { "$maxeyedeflection", Cmd_MaxEyeDeflection, MC_CURRENT_VERSION },
  9736. { "$addsearchdir", Cmd_AddSearchDir, MC_CURRENT_VERSION },
  9737. { "$phyname", Cmd_Phyname, MC_CURRENT_VERSION },
  9738. { "$subd", Cmd_SubdivisionSurface, MC_CURRENT_VERSION },
  9739. { "$boneflexdriver", Cmd_BoneFlexDriver, MC_CURRENT_VERSION },
  9740. { "$maxverts", Cmd_maxVerts, MC_CURRENT_VERSION },
  9741. { "$preservetriangleorder", Cmd_PreserveTriangleOrder, MC_CURRENT_VERSION },
  9742. { "$qcassert", Cmd_QCAssert, MC_CURRENT_VERSION },
  9743. { "$lcaseallsequences", Cmd_LCaseAllSequences, MC_CURRENT_VERSION },
  9744. { "$defaultfadein", Cmd_SetDefaultFadeInTime, MC_CURRENT_VERSION },
  9745. { "$defaultfadeout", Cmd_SetDefaultFadeOutTime, MC_CURRENT_VERSION },
  9746. { "$cloth", Cmd_Cloth, MC_CURRENT_VERSION },
  9747. { "$clothplanecollision", Cmd_ClothPlaneCollision, MC_CURRENT_VERSION },
  9748. { "$allowactivityname", Cmd_AllowActivityName, MC_CURRENT_VERSION },
  9749. { "$collisionprecision", Cmd_CollisionPrecision, MC_CURRENT_VERSION },
  9750. { "$erroronsequenceremappingfailure", Cmd_ErrorOnSeqRemapFail, MC_CURRENT_VERSION },
  9751. { "$modelhasnosequences", Cmd_SetModelIntentionallyHasZeroSequences, MC_CURRENT_VERSION },
  9752. { "$contentrootrelative", Cmd_ContentRootRelative, MC_CURRENT_VERSION },
  9753. };
  9754. int g_nMDLCommandCount = ARRAYSIZE( g_Commands );
  9755. MDLCommand_t *g_pMDLCommands = g_Commands;
  9756. /*
  9757. ===============
  9758. ParseScript
  9759. ===============
  9760. */
  9761. void ParseScript ( const char *pExt )
  9762. {
  9763. while (1)
  9764. {
  9765. GetToken (true);
  9766. if (endofscript)
  9767. return;
  9768. // Check all the commands we know about.
  9769. int i;
  9770. for ( i=0; i < ARRAYSIZE( g_Commands ); i++ )
  9771. {
  9772. if ( Q_stricmp( g_Commands[i].m_pName, token ) )
  9773. continue;
  9774. g_Commands[i].m_pCmd();
  9775. break;
  9776. }
  9777. if ( i == ARRAYSIZE( g_Commands ) )
  9778. {
  9779. if ( true )
  9780. {
  9781. if( !g_bCreateMakefile )
  9782. {
  9783. TokenError("bad command %s\n", token);
  9784. }
  9785. }
  9786. }
  9787. }
  9788. }
  9789. //-----------------------------------------------------------------------------
  9790. // For preprocessed files, all data lies in the g_fullpath.
  9791. // The DMX loader will take care of it.
  9792. //-----------------------------------------------------------------------------
  9793. bool ParsePreprocessedFile( const char *pFullPath )
  9794. {
  9795. char pFullPathBuf[ MAX_PATH ];
  9796. Q_strcpy( pFullPathBuf, pFullPath );
  9797. Q_FixSlashes( pFullPathBuf );
  9798. if ( !LoadPreprocessedFile( pFullPathBuf, 1.0f ) )
  9799. return false;
  9800. if ( !g_bHasModelName )
  9801. {
  9802. // The output name can be set via a "mdlPath" attribute on the root
  9803. // node of the preprocessed filename. If it wasn't set then derive it
  9804. // from the input filename
  9805. // The output name is directly derived from the input name
  9806. // NOTE: We use directory names when using preprocessed files
  9807. // Fix up passed pathname to use correct path separators otherwise
  9808. // functions below will fail
  9809. char pOutputBuf[MAX_PATH], pTemp[MAX_PATH], pOutputBuf2[MAX_PATH], pRelativeBuf[MAX_PATH];
  9810. char *pOutputName = pOutputBuf;
  9811. ComputeModFilename( pFullPathBuf, pTemp, sizeof(pTemp) );
  9812. Q_ExtractFilePath( pTemp, pOutputBuf, sizeof(pOutputBuf) );
  9813. Q_StripTrailingSlash( pOutputBuf );
  9814. if ( !Q_stricmp( Q_UnqualifiedFileName( pOutputBuf ), "preprocess" ) || !Q_stricmp( Q_UnqualifiedFileName( pOutputBuf ), ".preprocess" ) )
  9815. {
  9816. Q_ExtractFilePath( pOutputBuf, pOutputBuf2, sizeof(pOutputBuf2) );
  9817. Q_StripTrailingSlash( pOutputBuf2 );
  9818. pOutputName = pOutputBuf2;
  9819. }
  9820. int nBufLen = sizeof(pOutputBuf);
  9821. if ( Q_IsAbsolutePath( pOutputName ) )
  9822. {
  9823. if ( !g_pFullFileSystem->FullPathToRelativePathEx( pOutputName, "GAME", pRelativeBuf, sizeof(pRelativeBuf) ) )
  9824. {
  9825. MdlError( "Full path %s is not associated with the current mod!\n", pOutputName );
  9826. return false;
  9827. }
  9828. Q_FixSlashes( pRelativeBuf );
  9829. if ( Q_strnicmp( pRelativeBuf, "models\\", 7 ) )
  9830. {
  9831. MdlError( "Full path %s is not under the 'models' directory\n", pOutputName );
  9832. return false;
  9833. }
  9834. pOutputName = pRelativeBuf + 7;
  9835. nBufLen -= 7;
  9836. }
  9837. Q_SetExtension( pOutputName, "mdl", nBufLen );
  9838. ProcessModelName( pOutputName );
  9839. }
  9840. return true;
  9841. }
  9842. // Used by the CheckSurfaceProps.py script.
  9843. // They specify the .mdl file and it prints out all the surface props that the model uses.
  9844. bool HandlePrintSurfaceProps( int &returnValue )
  9845. {
  9846. const char *pFilename = CommandLine()->ParmValue( "-PrintSurfaceProps", (const char*)NULL );
  9847. if ( pFilename )
  9848. {
  9849. CUtlVector<char> buf;
  9850. FILE *fp = fopen( pFilename, "rb" );
  9851. if ( fp )
  9852. {
  9853. fseek( fp, 0, SEEK_END );
  9854. buf.SetSize( ftell( fp ) );
  9855. fseek( fp, 0, SEEK_SET );
  9856. fread( buf.Base(), 1, buf.Count(), fp );
  9857. fclose( fp );
  9858. studiohdr_t *pHdr = (studiohdr_t*)buf.Base();
  9859. Studio_ConvertStudioHdrToNewVersion( pHdr );
  9860. if ( pHdr->version == STUDIO_VERSION )
  9861. {
  9862. for ( int i=0; i < pHdr->numbones; i++ )
  9863. {
  9864. const mstudiobone_t *pBone = pHdr->pBone( i );
  9865. printf( "%s\n", pBone->pszSurfaceProp() );
  9866. }
  9867. returnValue = 0;
  9868. }
  9869. else
  9870. {
  9871. printf( "-PrintSurfaceProps: '%s' is wrong version (%d should be %d).\n",
  9872. pFilename, pHdr->version, STUDIO_VERSION );
  9873. returnValue = 1;
  9874. }
  9875. }
  9876. else
  9877. {
  9878. printf( "-PrintSurfaceProps: can't open '%s'\n", pFilename );
  9879. returnValue = 1;
  9880. }
  9881. return true;
  9882. }
  9883. else
  9884. {
  9885. return false;
  9886. }
  9887. }
  9888. // Used by the modelstats.pl script.
  9889. // They specify the .mdl file and it prints out perf info.
  9890. bool HandleMdlReport( int &returnValue )
  9891. {
  9892. const char *pFilename = CommandLine()->ParmValue( "-mdlreport", (const char*)NULL );
  9893. if ( pFilename )
  9894. {
  9895. CUtlVector<char> buf;
  9896. FILE *fp = fopen( pFilename, "rb" );
  9897. if ( fp )
  9898. {
  9899. fseek( fp, 0, SEEK_END );
  9900. buf.SetSize( ftell( fp ) );
  9901. fseek( fp, 0, SEEK_SET );
  9902. fread( buf.Base(), 1, buf.Count(), fp );
  9903. fclose( fp );
  9904. studiohdr_t *pHdr = (studiohdr_t*)buf.Base();
  9905. Studio_ConvertStudioHdrToNewVersion( pHdr );
  9906. if ( pHdr->version == STUDIO_VERSION )
  9907. {
  9908. int flags = SPEWPERFSTATS_SHOWPERF;
  9909. if( CommandLine()->CheckParm( "-mdlreportspreadsheet", NULL ) )
  9910. {
  9911. flags |= SPEWPERFSTATS_SPREADSHEET;
  9912. }
  9913. SpewPerfStats( pHdr, pFilename, flags );
  9914. returnValue = 0;
  9915. }
  9916. else
  9917. {
  9918. printf( "-mdlreport: '%s' is wrong version (%d should be %d).\n",
  9919. pFilename, pHdr->version, STUDIO_VERSION );
  9920. returnValue = 1;
  9921. }
  9922. }
  9923. else
  9924. {
  9925. printf( "-mdlreport: can't open '%s'\n", pFilename );
  9926. returnValue = 1;
  9927. }
  9928. return true;
  9929. }
  9930. else
  9931. {
  9932. return false;
  9933. }
  9934. }
  9935. void UsageAndExit()
  9936. {
  9937. MdlError( "Bad or missing options\n"
  9938. #ifdef MDLCOMPILE
  9939. "usage: mdlcompile [options] <file.mc>\n"
  9940. #else
  9941. "usage: studiomdl [options] <file.qc>\n"
  9942. #endif
  9943. "options:\n"
  9944. "[-a <normal_blend_angle>]\n"
  9945. "[-checklengths]\n"
  9946. "[-d] - dump glview files\n"
  9947. "[-definebones]\n"
  9948. "[-f] - flip all triangles\n"
  9949. "[-fullcollide] - don't truncate really big collisionmodels\n"
  9950. "[-game <gamedir>]\n"
  9951. "[-h] - dump hboxes\n"
  9952. "[-i] - ignore warnings\n"
  9953. "[-minlod <lod>] - truncate to highest detail <lod>\n"
  9954. "[-n] - tag bad normals\n"
  9955. "[-perf] report perf info upon compiling model\n"
  9956. "[-printbones]\n"
  9957. "[-printgraph]\n"
  9958. "[-quiet] - operate silently\n"
  9959. "[-r] - tag reversed\n"
  9960. "[-t <texture>]\n"
  9961. "[-x360] - generate xbox360 output\n"
  9962. "[-nox360] - disable xbox360 output(default)\n"
  9963. "[-fastbuild] - write a single vertex windings file\n"
  9964. "[-nowarnings] - disable warnings\n"
  9965. "[-dumpmaterials] - dump out material names\n"
  9966. "[-mdlreport] model.mdl - report perf info\n"
  9967. "[-mdlreportspreadsheet] - report perf info as a comma-delimited spreadsheet\n"
  9968. "[-striplods] - use only lod0\n"
  9969. "[-overridedefinebones] - equivalent to specifying $unlockdefinebones in " SRC_FILE_EXT " file\n"
  9970. "[-stripmodel] - process binary model files and strip extra lod data\n"
  9971. "[-stripvhv] - strip hardware verts to match the stripped model\n"
  9972. "[-vsi] - generate stripping information .vsi file - can be used on .mdl files too\n"
  9973. "[-allowdebug]\n"
  9974. "[-ihvtest]\n"
  9975. "[-overridedefinebones]\n"
  9976. "[-verbose]\n"
  9977. "[-makefile]\n"
  9978. "[-verify]\n"
  9979. "[-fastbuild]\n"
  9980. "[-maxwarnings]\n"
  9981. "[-preview]\n"
  9982. "[-dumpmaterials]\n"
  9983. "[-basedir]\n"
  9984. "[-tempcontent]\n"
  9985. "[-nop4]\n"
  9986. );
  9987. }
  9988. #ifndef _DEBUG
  9989. LONG __stdcall VExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
  9990. {
  9991. MdlExceptionFilter( ExceptionInfo->ExceptionRecord->ExceptionCode );
  9992. return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway)
  9993. }
  9994. #endif
  9995. /*
  9996. ==============
  9997. main
  9998. ==============
  9999. */
  10000. //-----------------------------------------------------------------------------
  10001. // The application object
  10002. //-----------------------------------------------------------------------------
  10003. class CStudioMDLApp : public CDefaultAppSystemGroup< CSteamAppSystemGroup >
  10004. {
  10005. typedef CDefaultAppSystemGroup< CSteamAppSystemGroup > BaseClass;
  10006. public:
  10007. // Methods of IApplication
  10008. virtual bool Create();
  10009. virtual bool PreInit( );
  10010. virtual int Main();
  10011. virtual void PostShutdown();
  10012. virtual void Destroy();
  10013. private:
  10014. int Main_StripModel();
  10015. int Main_StripVhv();
  10016. int Main_MakeVsi();
  10017. private:
  10018. bool ParseArguments();
  10019. };
  10020. static bool CStudioMDLApp_SuggestGameInfoDirFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
  10021. {
  10022. const char *pProcessFileName = NULL;
  10023. int nParmCount = CommandLine()->ParmCount();
  10024. if ( nParmCount > 1 )
  10025. {
  10026. pProcessFileName = CommandLine()->GetParm( nParmCount - 1 );
  10027. }
  10028. if ( pProcessFileName )
  10029. {
  10030. Q_MakeAbsolutePath( pchPathBuffer, nBufferLength, pProcessFileName );
  10031. if ( pbBubbleDirectories )
  10032. *pbBubbleDirectories = true;
  10033. return true;
  10034. }
  10035. return false;
  10036. }
  10037. int main( int argc, char **argv )
  10038. {
  10039. SetSuggestGameInfoDirFn( CStudioMDLApp_SuggestGameInfoDirFn );
  10040. CStudioMDLApp s_ApplicationObject;
  10041. CSteamApplication s_SteamApplicationObject( &s_ApplicationObject );
  10042. return AppMain( argc, argv, &s_SteamApplicationObject );
  10043. }
  10044. //-----------------------------------------------------------------------------
  10045. // The application object
  10046. //-----------------------------------------------------------------------------
  10047. bool CStudioMDLApp::Create()
  10048. {
  10049. // Ensure that cmdlib spew function & associated state is initialized
  10050. InstallSpewFunction();
  10051. // Override the cmdlib spew function
  10052. LoggingSystem_PushLoggingState();
  10053. LoggingSystem_RegisterLoggingListener( &s_MdlLoggingListener );
  10054. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  10055. #ifndef _DEBUG
  10056. SetUnhandledExceptionFilter( VExceptionFilter );
  10057. #endif
  10058. if ( CommandLine()->ParmCount() == 1 )
  10059. {
  10060. UsageAndExit();
  10061. return false;
  10062. }
  10063. int nReturnValue;
  10064. if ( HandlePrintSurfaceProps( nReturnValue ) )
  10065. return false;
  10066. if ( !ParseArguments() )
  10067. return false;
  10068. AppSystemInfo_t appSystems[] =
  10069. {
  10070. { "vstdlib.dll", PROCESS_UTILS_INTERFACE_VERSION },
  10071. { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
  10072. { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION },
  10073. { "mdllib.dll", MDLLIB_INTERFACE_VERSION },
  10074. { "", "" } // Required to terminate the list
  10075. };
  10076. AddSystem( g_pDataModel, VDATAMODEL_INTERFACE_VERSION );
  10077. AddSystem( g_pDmElementFramework, VDMELEMENTFRAMEWORK_VERSION );
  10078. AddSystem( g_pDmSerializers, DMSERIALIZERS_INTERFACE_VERSION );
  10079. // Add in the locally-defined studio data cache
  10080. AppModule_t studioDataCacheModule = LoadModule( Sys_GetFactoryThis() );
  10081. AddSystem( studioDataCacheModule, STUDIO_DATA_CACHE_INTERFACE_VERSION );
  10082. // Add the P4 module separately so that if it is absent (say in the SDK) then the other system will initialize properly
  10083. if ( !CommandLine()->FindParm( "-nop4" ) )
  10084. {
  10085. AppModule_t p4Module = LoadModule( "p4lib.dll" );
  10086. AddSystem( p4Module, P4_INTERFACE_VERSION );
  10087. }
  10088. bool bOk = AddSystems( appSystems );
  10089. if ( !bOk )
  10090. return false;
  10091. IMaterialSystem *pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
  10092. if ( !pMaterialSystem )
  10093. return false;
  10094. pMaterialSystem->SetShaderAPI( "shaderapiempty.dll" );
  10095. return true;
  10096. }
  10097. void CStudioMDLApp::Destroy()
  10098. {
  10099. LoggingSystem_PopLoggingState();
  10100. }
  10101. bool CStudioMDLApp::PreInit( )
  10102. {
  10103. CreateInterfaceFn factory = GetFactory();
  10104. ConnectTier1Libraries( &factory, 1 );
  10105. ConnectTier2Libraries( &factory, 1 );
  10106. ConnectTier3Libraries( &factory, 1 );
  10107. if ( !g_pFullFileSystem || !g_pDataModel || !g_pMaterialSystem || !g_pStudioRender )
  10108. {
  10109. Warning( "StudioMDL is missing a required interface!\n" );
  10110. return false;
  10111. }
  10112. if ( !SetupSearchPaths( g_path, false, true ) )
  10113. return false;
  10114. // NOTE: This is necessary to get the cmdlib filesystem stuff to work.
  10115. g_pFileSystem = g_pFullFileSystem;
  10116. // NOTE: This is stuff copied out of cmdlib necessary to get
  10117. // the tools in cmdlib working
  10118. FileSystem_SetupStandardDirectories( g_path, GetGameInfoPath() );
  10119. return true;
  10120. }
  10121. void CStudioMDLApp::PostShutdown()
  10122. {
  10123. DisconnectTier3Libraries();
  10124. DisconnectTier2Libraries();
  10125. DisconnectTier1Libraries();
  10126. }
  10127. //-----------------------------------------------------------------------------
  10128. // Method which parses arguments
  10129. //-----------------------------------------------------------------------------
  10130. bool CStudioMDLApp::ParseArguments()
  10131. {
  10132. g_currentscale = g_defaultscale = 1.0;
  10133. g_defaultrotation = RadianEuler( 0, 0, M_PI / 2 );
  10134. // skip weightlist 0
  10135. g_numweightlist = 1;
  10136. eyeposition = Vector( 0, 0, 0 );
  10137. gflags = 0;
  10138. numrep = 0;
  10139. tag_reversed = 0;
  10140. tag_normals = 0;
  10141. normal_blend = cos( DEG2RAD( 2.0 ));
  10142. g_gamma = 2.2;
  10143. g_staticprop = false;
  10144. g_centerstaticprop = false;
  10145. g_realignbones = false;
  10146. g_constdirectionalightdot = 0;
  10147. g_bDumpGLViewFiles = false;
  10148. g_quiet = false;
  10149. g_illumpositionattachment = 0;
  10150. g_flMaxEyeDeflection = 0.0f;
  10151. g_collapse_bones_message = false;
  10152. int argc = CommandLine()->ParmCount();
  10153. int i;
  10154. for ( i = 1; i < argc - 1; i++ )
  10155. {
  10156. const char *pArgv = CommandLine()->GetParm( i );
  10157. if ( pArgv[0] != '-' )
  10158. continue;
  10159. if ( !Q_stricmp( pArgv, "-collapsereport" ) )
  10160. {
  10161. g_collapse_bones_message = true;
  10162. continue;
  10163. }
  10164. if ( !Q_stricmp( pArgv, "-parsecompletion" ) )
  10165. {
  10166. // reliably prints output we can parse for automatically
  10167. g_parseable_completion_output = true;
  10168. continue;
  10169. }
  10170. if ( !Q_stricmp( pArgv, "-allowdebug" ) )
  10171. {
  10172. // Ignore, used by interface system to catch debug builds checked into release tree
  10173. continue;
  10174. }
  10175. if ( !Q_stricmp( pArgv, "-mdlreport" ) )
  10176. {
  10177. // Will reparse later, ignore rest of arguments.
  10178. return true;
  10179. }
  10180. if ( !Q_stricmp( pArgv, "-mdlreportspreadsheet" ) )
  10181. {
  10182. // Will reparse later, ignore for now.
  10183. continue;
  10184. }
  10185. if ( !Q_stricmp( pArgv, "-ihvtest" ) )
  10186. {
  10187. ++i;
  10188. g_IHVTest = true;
  10189. continue;
  10190. }
  10191. if ( !Q_stricmp( pArgv, "-overridedefinebones" ) )
  10192. {
  10193. g_bDefineBonesLockedByDefault = false;
  10194. continue;
  10195. }
  10196. if ( !Q_stricmp( pArgv, "-striplods" ) )
  10197. {
  10198. g_bStripLods = true;
  10199. continue;
  10200. }
  10201. if ( !Q_stricmp( pArgv, "-stripmodel" ) )
  10202. {
  10203. g_eRunMode = RUN_MODE_STRIP_MODEL;
  10204. continue;
  10205. }
  10206. if ( !Q_stricmp( pArgv, "-stripvhv" ) )
  10207. {
  10208. g_eRunMode = RUN_MODE_STRIP_VHV;
  10209. continue;
  10210. }
  10211. if ( !Q_stricmp( pArgv, "-vsi" ) )
  10212. {
  10213. g_bMakeVsi = true;
  10214. continue;
  10215. }
  10216. if ( !Q_stricmp( pArgv, "-quiet" ) )
  10217. {
  10218. g_quiet = true;
  10219. g_verbose = false;
  10220. continue;
  10221. }
  10222. if ( !Q_stricmp( pArgv, "-verbose" ) )
  10223. {
  10224. g_quiet = false;
  10225. g_verbose = true;
  10226. continue;
  10227. }
  10228. if ( !Q_stricmp( pArgv, "-fullcollide" ) )
  10229. {
  10230. g_badCollide = true;
  10231. continue;
  10232. }
  10233. if ( !Q_stricmp( pArgv, "-checklengths" ) )
  10234. {
  10235. g_bCheckLengths = true;
  10236. continue;
  10237. }
  10238. if ( !Q_stricmp( pArgv, "-printbones" ) )
  10239. {
  10240. g_bPrintBones = true;
  10241. continue;
  10242. }
  10243. if ( !Q_stricmp( pArgv, "-perf" ) )
  10244. {
  10245. g_bPerf = true;
  10246. continue;
  10247. }
  10248. if ( !Q_stricmp( pArgv, "-printgraph" ) )
  10249. {
  10250. g_bDumpGraph = true;
  10251. continue;
  10252. }
  10253. if ( !Q_stricmp( pArgv, "-definebones" ) )
  10254. {
  10255. g_definebones = true;
  10256. continue;
  10257. }
  10258. if ( !Q_stricmp( pArgv, "-makefile" ) )
  10259. {
  10260. g_bCreateMakefile = true;
  10261. g_quiet = true;
  10262. continue;
  10263. }
  10264. if ( !Q_stricmp( pArgv, "-verify" ) )
  10265. {
  10266. g_bVerifyOnly = true;
  10267. continue;
  10268. }
  10269. if ( !Q_stricmp( pArgv, "-minlod" ) )
  10270. {
  10271. g_minLod = atoi( CommandLine()->GetParm( ++i ) );
  10272. continue;
  10273. }
  10274. if (!Q_stricmp( pArgv, "-fastbuild"))
  10275. {
  10276. g_bFastBuild = true;
  10277. continue;
  10278. }
  10279. if (!Q_stricmp( pArgv, "-x360"))
  10280. {
  10281. StudioByteSwap::ActivateByteSwapping( true ); // Set target to big endian
  10282. g_bX360 = true;
  10283. continue;
  10284. }
  10285. if (!Q_stricmp( pArgv, "-nox360"))
  10286. {
  10287. g_bX360 = false;
  10288. continue;
  10289. }
  10290. if ( !Q_stricmp( pArgv, "-nowarnings" ) )
  10291. {
  10292. g_bNoWarnings = true;
  10293. continue;
  10294. }
  10295. if ( !Q_stricmp( pArgv, "-maxwarnings" ) )
  10296. {
  10297. g_maxWarnings = atoi( CommandLine()->GetParm( ++i ) );
  10298. continue;
  10299. }
  10300. if ( !Q_stricmp( pArgv, "-preview" ) )
  10301. {
  10302. g_bBuildPreview = true;
  10303. continue;
  10304. }
  10305. if ( !Q_stricmp( pArgv, "-dumpmaterials" ) )
  10306. {
  10307. g_bDumpMaterials = true;
  10308. continue;
  10309. }
  10310. if ( pArgv[1] && pArgv[2] == '\0' )
  10311. {
  10312. switch( pArgv[1] )
  10313. {
  10314. case 't':
  10315. i++;
  10316. strcpy( defaulttexture[numrep], pArgv );
  10317. if (i < argc - 2 && CommandLine()->GetParm(i + 1)[0] != '-')
  10318. {
  10319. i++;
  10320. strcpy( sourcetexture[numrep], pArgv );
  10321. printf("Replacing %s with %s\n", sourcetexture[numrep], defaulttexture[numrep] );
  10322. }
  10323. printf( "Using default texture: %s\n", defaulttexture );
  10324. numrep++;
  10325. break;
  10326. case 'r':
  10327. tag_reversed = 1;
  10328. break;
  10329. case 'n':
  10330. tag_normals = 1;
  10331. break;
  10332. case 'a':
  10333. i++;
  10334. normal_blend = cos( DEG2RAD( verify_atof( pArgv ) ) );
  10335. break;
  10336. case 'h':
  10337. dump_hboxes = 1;
  10338. break;
  10339. case 'i':
  10340. ignore_warnings = 1;
  10341. break;
  10342. case 'd':
  10343. g_bDumpGLViewFiles = true;
  10344. break;
  10345. // case 'p':
  10346. // i++;
  10347. // strcpy( qproject, pArgv );
  10348. // break;
  10349. }
  10350. }
  10351. }
  10352. if ( i >= argc )
  10353. {
  10354. // misformed arguments
  10355. // otherwise generating unintended results
  10356. UsageAndExit();
  10357. return false;
  10358. }
  10359. const char *pArgv = CommandLine()->GetParm( i );
  10360. Q_strncpy( g_path, pArgv, sizeof(g_path) );
  10361. if ( Q_IsAbsolutePath( g_path ) )
  10362. {
  10363. // Set the working directory to be the path of the qc file
  10364. // so the relative-file fopen code works
  10365. char pQCDir[MAX_PATH];
  10366. Q_ExtractFilePath( g_path, pQCDir, sizeof(pQCDir) );
  10367. _chdir( pQCDir );
  10368. }
  10369. Q_StripExtension( pArgv, g_outname, sizeof( g_outname ) );
  10370. return true;
  10371. }
  10372. //-----------------------------------------------------------------------------
  10373. // Purpose: search through the "GamePath" key and create a mirrored version in the content path searches
  10374. //-----------------------------------------------------------------------------
  10375. void AddContentPaths( )
  10376. {
  10377. // look for the "content" in the path to the initial QC file
  10378. char *match = "content\\";
  10379. char *sp = strstr( qdir, match );
  10380. if (!sp)
  10381. return;
  10382. // copy off everything before and including "content"
  10383. char pre[1024];
  10384. strncpy( pre, qdir, sp - qdir + strlen( match ) );
  10385. pre[sp - qdir + strlen( match )] = '\0';
  10386. sp = sp + strlen( match );
  10387. // copy off everything following the word after "content"
  10388. char post[1024];
  10389. sp = strstr( sp+1, "\\" );
  10390. strcpy( post, sp );
  10391. // get a copy of the game search paths
  10392. char paths[1024];
  10393. g_pFullFileSystem->GetSearchPath( "GAME", false, paths, sizeof( paths ) );
  10394. if (!g_quiet)
  10395. printf("all paths:%s\n", paths );
  10396. // pull out the game names and insert them into a content path string
  10397. sp = strstr( paths, "game\\" );
  10398. while (sp)
  10399. {
  10400. char temp[1024];
  10401. sp = sp + 5;
  10402. char *sz = strstr( sp, "\\" );
  10403. if (!sz)
  10404. return;
  10405. strcpy( temp, pre );
  10406. strncat( temp, sp, sz - sp );
  10407. strcat( temp, post );
  10408. sp = sz;
  10409. sp = strstr( sp, "game\\" );
  10410. CmdLib_AddBasePath( temp );
  10411. if (!g_quiet)
  10412. printf("content:%s\n", temp );
  10413. }
  10414. }
  10415. //////////////////////////////////////////////////////////////////////////
  10416. // Purpose: parses the game info file to retrieve relevant settings
  10417. //////////////////////////////////////////////////////////////////////////
  10418. struct GameInfo_t g_gameinfo;
  10419. void ParseGameInfo()
  10420. {
  10421. bool bParsed = false;
  10422. GameInfo_t gameinfoDefault;
  10423. gameinfoDefault.bSupportsXBox360 = false;
  10424. gameinfoDefault.bSupportsDX8 = true;
  10425. KeyValues *pKeyValues = new KeyValues( "gameinfo.txt" );
  10426. if ( pKeyValues != NULL )
  10427. {
  10428. if ( g_pFileSystem && pKeyValues->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) )
  10429. {
  10430. g_gameinfo.bSupportsXBox360 = !!pKeyValues->GetInt( "SupportsXBox360", (int) gameinfoDefault.bSupportsXBox360 );
  10431. g_gameinfo.bSupportsDX8 = !!pKeyValues->GetInt( "SupportsDX8", (int) gameinfoDefault.bSupportsDX8 );
  10432. bParsed = true;
  10433. }
  10434. pKeyValues->deleteThis();
  10435. }
  10436. if ( !bParsed )
  10437. {
  10438. g_gameinfo = gameinfoDefault;
  10439. }
  10440. }
  10441. //-----------------------------------------------------------------------------
  10442. // The application object
  10443. //-----------------------------------------------------------------------------
  10444. int CStudioMDLApp::Main()
  10445. {
  10446. const bool bP4DLLExists = g_pFullFileSystem->FileExists( "p4lib.dll", "EXECUTABLE_PATH" );
  10447. // No p4 mode if specified on the command line or no p4lib.dll found
  10448. if ( ( CommandLine()->FindParm( "-nop4" ) ) || ( !bP4DLLExists ) || CommandLine()->FindParm( "-nop4checkout" ) )
  10449. {
  10450. g_bNoP4 = true;
  10451. g_p4factory->SetDummyMode( true );
  10452. }
  10453. g_numverts = g_numnormals = g_numfaces = 0;
  10454. for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
  10455. {
  10456. g_numtexcoords[i] = 0;
  10457. }
  10458. // Set the named changelist
  10459. #ifdef MDLCOMPILE
  10460. g_p4factory->SetDummyMode( true ); // Don't use perforce with mdlcompile
  10461. #else
  10462. g_p4factory->SetOpenFileChangeList( "StudioMDL Auto Checkout" );
  10463. #endif
  10464. // This bit of hackery allows us to access files on the harddrive
  10465. g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
  10466. g_pMaterialSystem->ModInit();
  10467. MaterialSystem_Config_t config;
  10468. g_pMaterialSystem->OverrideConfig( config, false );
  10469. int nReturnValue;
  10470. if ( HandleMdlReport( nReturnValue ) )
  10471. return false;
  10472. // Don't bother with undo here
  10473. g_pDataModel->SetUndoEnabled( false );
  10474. // look for the "content\hl2x" string in the qdir and add what should be the correct path as an alternate
  10475. // FIXME: add these to an envvar if folks are using complicated directory mappings instead of defaults
  10476. char *match = "content\\hl2x\\";
  10477. char *sp = strstr( qdir, match );
  10478. if (sp)
  10479. {
  10480. char temp[1024];
  10481. strncpy( temp, qdir, sp - qdir + strlen( match ) );
  10482. temp[sp - qdir + strlen( match )] = '\0';
  10483. CmdLib_AddBasePath( temp );
  10484. strcat( temp, "..\\..\\..\\..\\main\\content\\hl2\\" );
  10485. CmdLib_AddBasePath( temp );
  10486. }
  10487. AddContentPaths();
  10488. ParseGameInfo();
  10489. if (!g_quiet)
  10490. {
  10491. printf("qdir: \"%s\"\n", qdir );
  10492. printf("gamedir: \"%s\"\n", gamedir );
  10493. printf("g_path: \"%s\"\n", g_path );
  10494. }
  10495. switch ( g_eRunMode )
  10496. {
  10497. case RUN_MODE_STRIP_MODEL:
  10498. return Main_StripModel();
  10499. case RUN_MODE_STRIP_VHV:
  10500. return Main_StripVhv();
  10501. case RUN_MODE_BUILD:
  10502. default:
  10503. break;
  10504. }
  10505. const char *pExt = Q_GetFileExtension( g_path );
  10506. // Look for the presence of a .mdl file (only -vsi is currently supported for .mdl files)
  10507. if ( pExt && !Q_stricmp( pExt, "mdl" ) )
  10508. {
  10509. if ( g_bMakeVsi )
  10510. return Main_MakeVsi();
  10511. printf( "ERROR: " SRC_FILE_EXT " or .dmx file should be specified to build.\n" );
  10512. return 1;
  10513. }
  10514. if ( !g_quiet )
  10515. printf( "Building binary model files...\n" );
  10516. bool bLoadingPreprocessedFile = false;
  10517. #ifdef MDLCOMPILE
  10518. if ( pExt && !Q_stricmp( pExt, "mpp" ) )
  10519. {
  10520. bLoadingPreprocessedFile = true;
  10521. // Handle relative path names because g_path is appended onto qdir which is the
  10522. // absolute path to the file minus the filename
  10523. Q_FileBase( g_path, g_path, sizeof( g_path ) );
  10524. Q_DefaultExtension( g_path, "mpp" , sizeof( g_path ) );
  10525. }
  10526. if ( !pExt && !bLoadingPreprocessedFile )
  10527. {
  10528. #endif
  10529. Q_FileBase( g_path, g_path, sizeof( g_path ) );
  10530. Q_DefaultExtension( g_path, SRC_FILE_EXT, sizeof( g_path ) );
  10531. if ( !pExt )
  10532. {
  10533. pExt = SRC_FILE_EXT;
  10534. }
  10535. #ifdef MDLCOMPILE
  10536. }
  10537. #endif
  10538. if (!g_quiet)
  10539. {
  10540. printf( "Working on \"%s\"\n", g_path );
  10541. }
  10542. // Set up script loading callback, discarding default callback
  10543. ( void ) SetScriptLoadedCallback( StudioMdl_ScriptLoadedCallback );
  10544. // load the script
  10545. if ( !bLoadingPreprocessedFile )
  10546. {
  10547. LoadScriptFile(g_path);
  10548. }
  10549. strcpy( g_fullpath, g_path );
  10550. strcpy( g_fullpath, ExpandPath( g_fullpath ) );
  10551. strcpy( g_fullpath, ExpandArg( g_fullpath ) );
  10552. // default to having one entry in the LOD list that doesn't do anything so
  10553. // that we don't have to do any special cases for the first LOD.
  10554. g_ScriptLODs.Purge();
  10555. g_ScriptLODs.AddToTail(); // add an empty one
  10556. g_ScriptLODs[0].switchValue = 0.0f;
  10557. //
  10558. // parse it
  10559. //
  10560. ClearModel();
  10561. // strcpy( g_pPlatformName, "" );
  10562. if ( bLoadingPreprocessedFile )
  10563. {
  10564. if ( !ParsePreprocessedFile( g_fullpath ) )
  10565. {
  10566. MdlError( "Invalid MPP File: %s\n", g_path );
  10567. return 1;
  10568. }
  10569. }
  10570. else
  10571. {
  10572. ParseScript( pExt );
  10573. }
  10574. if ( !g_bCreateMakefile )
  10575. {
  10576. int nCount = g_numsources;
  10577. for (int i = 0; i < nCount; i++)
  10578. {
  10579. if ( g_source[i]->isActiveModel )
  10580. {
  10581. ClampMaxVerticesPerModel( g_source[i] );
  10582. }
  10583. }
  10584. SetSkinValues();
  10585. SimplifyModel();
  10586. ConsistencyCheckSurfaceProp();
  10587. ConsistencyCheckContents();
  10588. CollisionModel_Build();
  10589. // ValidateSharedAnimationGroups();
  10590. WriteModelFiles();
  10591. }
  10592. if ( g_bCreateMakefile )
  10593. {
  10594. CreateMakefile_OutputMakefile();
  10595. }
  10596. else if ( g_bMakeVsi )
  10597. {
  10598. Q_snprintf( g_path, ARRAYSIZE( g_path ), "%smodels/%s", gamedir, g_outname );
  10599. Main_MakeVsi();
  10600. }
  10601. if (!g_quiet)
  10602. {
  10603. printf("\nCompleted \"%s\"\n", g_path);
  10604. }
  10605. if ( g_parseable_completion_output )
  10606. {
  10607. printf("\nRESULT: SUCCESS\n");
  10608. }
  10609. g_pDataModel->UnloadFile( DMFILEID_INVALID );
  10610. return 0;
  10611. }
  10612. //
  10613. // WriteFileToDisk
  10614. // Equivalent to g_pFullFileSystem->WriteFile( pFileName, pPath, buf ), but works
  10615. // for relative paths.
  10616. //
  10617. bool WriteFileToDisk( const char *pFileName, const char *pPath, CUtlBuffer &buf )
  10618. {
  10619. // For some reason calling full filesystem will write into hl2 root dir
  10620. // return g_pFullFileSystem->WriteFile( pFileName, pPath, buf );
  10621. FILE *f = fopen( pFileName, "wb" );
  10622. if ( !f )
  10623. return false;
  10624. fwrite( buf.Base(), 1, buf.TellPut(), f );
  10625. fclose( f );
  10626. return true;
  10627. }
  10628. //
  10629. // WriteBufferToFile
  10630. // Helper to concatenate file base and extension.
  10631. //
  10632. bool WriteBufferToFile( CUtlBuffer &buf, const char *szFilebase, const char *szExt )
  10633. {
  10634. char szFilename[ 1024 ];
  10635. Q_snprintf( szFilename, ARRAYSIZE( szFilename ), "%s%s", szFilebase, szExt );
  10636. return WriteFileToDisk( szFilename, NULL, buf );
  10637. }
  10638. //
  10639. // LoadBufferFromFile
  10640. // Loads the buffer from file, return true on success, false otherwise.
  10641. // If bError is true prints an error upon failure.
  10642. //
  10643. bool LoadBufferFromFile( CUtlBuffer &buffer, const char *szFilebase, const char *szExt, bool bError = true )
  10644. {
  10645. char szFilename[1024];
  10646. Q_snprintf( szFilename, ARRAYSIZE( szFilename ), "%s%s", szFilebase, szExt );
  10647. if ( g_pFullFileSystem->ReadFile( szFilename, NULL, buffer ) )
  10648. return true;
  10649. if ( bError )
  10650. MdlError( "Failed to open '%s'!\n", szFilename );
  10651. return false;
  10652. }
  10653. bool Load3ModelBuffers( CUtlBuffer &bufMDL, CUtlBuffer &bufVVD, CUtlBuffer &bufVTX, const char *szFilebase )
  10654. {
  10655. // Load up the mdl file
  10656. if ( !LoadBufferFromFile( bufMDL, szFilebase, ".mdl" ) )
  10657. return false;
  10658. // Load up the vvd file
  10659. if ( !LoadBufferFromFile( bufVVD, szFilebase, ".vvd" ) )
  10660. return false;
  10661. // Load up the dx90.vtx file
  10662. if ( !LoadBufferFromFile( bufVTX, szFilebase, ".dx90.vtx" ) )
  10663. return false;
  10664. return true;
  10665. }
  10666. //////////////////////////////////////////////////////////////////////////
  10667. //
  10668. // Studiomdl hooks to call the stripping routines:
  10669. // Main_StripVhv
  10670. // Main_StripModel
  10671. //
  10672. //////////////////////////////////////////////////////////////////////////
  10673. int CStudioMDLApp::Main_StripVhv()
  10674. {
  10675. if ( !g_quiet )
  10676. {
  10677. printf( "Stripping vhv data...\n" );
  10678. }
  10679. if ( !mdllib )
  10680. {
  10681. printf( "ERROR: mdllib is not available!\n" );
  10682. return 1;
  10683. }
  10684. Q_StripExtension( g_path, g_path, sizeof( g_path ) );
  10685. char *pExt = g_path + strlen( g_path );
  10686. *pExt = 0;
  10687. //
  10688. // ====== Load files
  10689. //
  10690. // Load up the vhv file
  10691. CUtlBuffer bufVHV;
  10692. if ( !LoadBufferFromFile( bufVHV, g_path, ".vhv" ) )
  10693. return 1;
  10694. // Load up the info.strip file
  10695. CUtlBuffer bufRemapping;
  10696. if ( !LoadBufferFromFile( bufRemapping, g_path, ".info.strip", false ) &&
  10697. !LoadBufferFromFile( bufRemapping, g_path, ".vsi" ) )
  10698. return 1;
  10699. //
  10700. // ====== Process file contents
  10701. //
  10702. bool bResult = false;
  10703. {
  10704. LoggingSystem_SetChannelSpewLevelByName( "ModelLib", LS_MESSAGE );
  10705. IMdlStripInfo *pMdlStripInfo = NULL;
  10706. if ( mdllib->CreateNewStripInfo( &pMdlStripInfo ) )
  10707. {
  10708. pMdlStripInfo->UnSerialize( bufRemapping );
  10709. bResult = pMdlStripInfo->StripHardwareVertsBuffer( bufVHV );
  10710. }
  10711. if ( pMdlStripInfo )
  10712. pMdlStripInfo->DeleteThis();
  10713. }
  10714. if ( !bResult )
  10715. {
  10716. printf( "ERROR: stripping failed!\n" );
  10717. return 1;
  10718. }
  10719. //
  10720. // ====== Save out processed data
  10721. //
  10722. // Save vhv
  10723. if ( !WriteBufferToFile( bufVHV, g_path, ".vhv.strip" ) )
  10724. {
  10725. printf( "ERROR: Failed to save '%s'!\n", g_path );
  10726. return 1;
  10727. }
  10728. return 0;
  10729. }
  10730. int CStudioMDLApp::Main_MakeVsi()
  10731. {
  10732. if ( !mdllib )
  10733. {
  10734. printf( "ERROR: mdllib is not available!\n" );
  10735. return 1;
  10736. }
  10737. Q_StripExtension( g_path, g_path, sizeof( g_path ) );
  10738. char *pExt = g_path + strlen( g_path );
  10739. *pExt = 0;
  10740. // Load up the files
  10741. CUtlBuffer bufMDL;
  10742. CUtlBuffer bufVVD;
  10743. CUtlBuffer bufVTX;
  10744. if ( !Load3ModelBuffers( bufMDL, bufVVD, bufVTX, g_path ) )
  10745. return 1;
  10746. //
  10747. // ====== Process file contents
  10748. //
  10749. CUtlBuffer bufMappingTable;
  10750. bool bResult = false;
  10751. {
  10752. if ( !g_quiet )
  10753. {
  10754. printf( "---------------------\n" );
  10755. printf( "Generating .vsi stripping information...\n" );
  10756. LoggingSystem_SetChannelSpewLevelByName( "ModelLib", LS_MESSAGE );
  10757. }
  10758. IMdlStripInfo *pMdlStripInfo = NULL;
  10759. bResult =
  10760. mdllib->StripModelBuffers( bufMDL, bufVVD, bufVTX, &pMdlStripInfo ) &&
  10761. pMdlStripInfo->Serialize( bufMappingTable );
  10762. if ( pMdlStripInfo )
  10763. pMdlStripInfo->DeleteThis();
  10764. }
  10765. if ( !bResult )
  10766. {
  10767. printf( "ERROR: stripping failed!\n" );
  10768. return 1;
  10769. }
  10770. //
  10771. // ====== Save out processed data
  10772. //
  10773. // Save remapping data using "P4 edit -> save -> P4 add" approach
  10774. sprintf( pExt, ".vsi" );
  10775. CP4AutoEditAddFile _auto_edit_vsi( g_path );
  10776. if ( !WriteFileToDisk( g_path, NULL, bufMappingTable ) )
  10777. {
  10778. printf( "ERROR: Failed to save '%s'!\n", g_path );
  10779. return 1;
  10780. }
  10781. else if ( !g_quiet )
  10782. {
  10783. printf( "Generated .vsi stripping information.\n" );
  10784. }
  10785. return 0;
  10786. }
  10787. int CStudioMDLApp::Main_StripModel()
  10788. {
  10789. if ( !g_quiet )
  10790. {
  10791. printf( "Stripping binary model files...\n" );
  10792. }
  10793. if ( !mdllib )
  10794. {
  10795. printf( "ERROR: mdllib is not available!\n" );
  10796. return 1;
  10797. }
  10798. Q_FileBase( g_path, g_path, sizeof( g_path ) );
  10799. char *pExt = g_path + strlen( g_path );
  10800. *pExt = 0;
  10801. // Load up the files
  10802. CUtlBuffer bufMDL;
  10803. CUtlBuffer bufVVD;
  10804. CUtlBuffer bufVTX;
  10805. if ( !Load3ModelBuffers( bufMDL, bufVVD, bufVTX, g_path ) )
  10806. return 1;
  10807. //
  10808. // ====== Process file contents
  10809. //
  10810. CUtlBuffer bufMappingTable;
  10811. bool bResult = false;
  10812. {
  10813. LoggingSystem_SetChannelSpewLevelByName( "ModelLib", LS_MESSAGE );
  10814. IMdlStripInfo *pMdlStripInfo = NULL;
  10815. bResult =
  10816. mdllib->StripModelBuffers( bufMDL, bufVVD, bufVTX, &pMdlStripInfo ) &&
  10817. pMdlStripInfo->Serialize( bufMappingTable );
  10818. if ( pMdlStripInfo )
  10819. pMdlStripInfo->DeleteThis();
  10820. }
  10821. if ( !bResult )
  10822. {
  10823. printf( "ERROR: stripping failed!\n" );
  10824. return 1;
  10825. }
  10826. //
  10827. // ====== Save out processed data
  10828. //
  10829. // Save mdl
  10830. sprintf( pExt, ".mdl.strip" );
  10831. if ( !WriteFileToDisk( g_path, NULL, bufMDL ) )
  10832. {
  10833. printf( "ERROR: Failed to save '%s'!\n", g_path );
  10834. return 1;
  10835. }
  10836. // Save vvd
  10837. sprintf( pExt, ".vvd.strip" );
  10838. if ( !WriteFileToDisk( g_path, NULL, bufVVD ) )
  10839. {
  10840. printf( "ERROR: Failed to save '%s'!\n", g_path );
  10841. return 1;
  10842. }
  10843. // Save vtx
  10844. sprintf( pExt, ".vtx.strip" );
  10845. if ( !WriteFileToDisk( g_path, NULL, bufVTX ) )
  10846. {
  10847. printf( "ERROR: Failed to save '%s'!\n", g_path );
  10848. return 1;
  10849. }
  10850. // Save remapping data
  10851. sprintf( pExt, ".info.strip" );
  10852. if ( !WriteFileToDisk( g_path, NULL, bufMappingTable ) )
  10853. {
  10854. printf( "ERROR: Failed to save '%s'!\n", g_path );
  10855. return 1;
  10856. }
  10857. return 0;
  10858. }