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

2842 lines
90 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ================================== //
  2. //
  3. // Purpose:
  4. //
  5. //============================================================================================== //
  6. #include "pch_materialsystem.h"
  7. #include "ctexturecompositor.h"
  8. #include "materialsystem/itexture.h"
  9. #include "materialsystem/imaterialsystem.h"
  10. #include "materialsystem/combineoperations.h"
  11. #include "texturemanager.h"
  12. #define MATSYS_INTERNAL // Naughty!
  13. #include "cmaterialsystem.h"
  14. #include "tier0/memdbgon.h"
  15. #ifndef _WINDOWS
  16. #define sscanf_s sscanf
  17. #endif
  18. // If this is 0 or unset, we won't use the caching functionality.
  19. #define WITH_TEX_COMPOSITE_CACHE 1
  20. #ifdef STAGING_ONLY // Always should remain staging only.
  21. ConVar r_texcomp_dump( "r_texcomp_dump", "0", FCVAR_NONE, "Whether we should dump the textures to disk or not. 1: Save all; 2: Save Final; 3: Save Final with name suitable for scripting; 4: Save Final and skip saving workshop icons." );
  22. #endif
  23. const int cMaxSelectors = 16;
  24. // Ugh, this is annoying and matches TF's enums. That's lame. We should workaround this.
  25. enum { Neutral = 0, Red = 2, Blue = 3 };
  26. static int s_nDumpCount = 0;
  27. static CInterlockedInt s_nCompositeCount = 0;
  28. void ComputeTextureMatrixFromRectangle( VMatrix* pOutMat, const Vector2D& bl, const Vector2D& tl, const Vector2D& tr );
  29. bool HasCycle( CTextureCompositorTemplate* pStartTempl );
  30. CTextureCompositorTemplate* Advance( CTextureCompositorTemplate* pTmpl, int nSteps );
  31. void PrintMinimumCycle( CTextureCompositorTemplate* pStartTempl );
  32. // ------------------------------------------------------------------------------------------------
  33. // ------------------------------------------------------------------------------------------------
  34. // ------------------------------------------------------------------------------------------------
  35. struct CTCStageResult_t
  36. {
  37. ITexture* m_pTexture;
  38. ITexture* m_pRenderTarget;
  39. float m_fAdjustBlackPoint;
  40. float m_fAdjustWhitePoint;
  41. float m_fAdjustGamma;
  42. matrix3x4_t m_mUvAdjust;
  43. inline CTCStageResult_t()
  44. : m_pTexture(NULL)
  45. , m_pRenderTarget(NULL)
  46. , m_fAdjustBlackPoint(0.0f)
  47. , m_fAdjustWhitePoint(1.0f)
  48. , m_fAdjustGamma(1.0f)
  49. {
  50. SetIdentityMatrix( m_mUvAdjust );
  51. }
  52. inline void Cleanup( CTextureCompositor* _comp )
  53. {
  54. if ( m_pRenderTarget )
  55. _comp->ReleaseCompositorRenderTarget( m_pRenderTarget );
  56. m_pTexture = NULL;
  57. m_pRenderTarget = NULL;
  58. }
  59. };
  60. // ------------------------------------------------------------------------------------------------
  61. // ------------------------------------------------------------------------------------------------
  62. // ------------------------------------------------------------------------------------------------
  63. class CTCStage : public IAsyncTextureOperationReceiver
  64. {
  65. public:
  66. CTCStage();
  67. protected:
  68. // Called by Release()
  69. virtual ~CTCStage();
  70. public:
  71. // IAsyncTextureOperationReceiver
  72. virtual int AddRef() OVERRIDE;
  73. virtual int Release() OVERRIDE;
  74. virtual int GetRefCount() const OVERRIDE { return m_nReferenceCount; }
  75. virtual void OnAsyncCreateComplete( ITexture* pTex, void* pExtraArgs ) OVERRIDE { }
  76. virtual void OnAsyncFindComplete( ITexture* pTex, void* pExtraArgs ) OVERRIDE { }
  77. virtual void OnAsyncMapComplete( ITexture* pTex, void* pExtraArgs, void* pMemory, int pPitch ) OVERRIDE { }
  78. virtual void OnAsyncReadbackBegin( ITexture* pDst, ITexture* pSrc, void* pExtraArgs ) OVERRIDE { }
  79. // Our stuff.
  80. void Resolve( bool bFirstTime, CTextureCompositor* _comp );
  81. inline ECompositeResolveStatus GetResolveStatus() const { return m_ResolveStatus; }
  82. inline const CTCStageResult_t& GetResult() const { Assert( GetResolveStatus() == ECRS_Complete ); return m_Result; }
  83. bool HasTeamSpecifics() const;
  84. void ComputeRandomValues( int* pCurIndex, CUniformRandomStream* pRNGs, int nRNGCount );
  85. inline void SetFirstChild( CTCStage* _stage ) { m_pFirstChild = _stage; }
  86. inline void SetNextSibling( CTCStage* _stage ) { m_pNextSibling = _stage; }
  87. inline CTCStage* GetFirstChild() { return m_pFirstChild; }
  88. inline CTCStage* GetNextSibling() { return m_pNextSibling; }
  89. inline const CTCStage* GetFirstChild() const { return m_pFirstChild; }
  90. inline const CTCStage* GetNextSibling() const { return m_pNextSibling; }
  91. void AppendChildren( const CUtlVector< CTCStage* >& _children )
  92. {
  93. // Do these in reverse order, they will wind up in the right order
  94. FOR_EACH_VEC_BACK( _children, i )
  95. {
  96. CTCStage* childStage = _children[i];
  97. childStage->SetNextSibling( GetFirstChild() );
  98. SetFirstChild( childStage );
  99. }
  100. }
  101. void CleanupChildResults( CTextureCompositor* _comp );
  102. // Render a quad with _mat using _inputs to _destRT
  103. void Render( ITexture* _destRT, IMaterial* _mat, const CUtlVector<CTCStageResult_t>& _inputs, CTextureCompositor* _comp, bool bClear );
  104. void Cleanup( CTextureCompositor* _comp );
  105. // Does this stage target a render target or a texture?
  106. virtual bool DoesTargetRenderTarget() const = 0;
  107. inline void SetResult( const CTCStageResult_t& _result )
  108. {
  109. Assert( m_ResolveStatus != ECRS_Complete );
  110. m_Result = _result;
  111. m_ResolveStatus = ECRS_Complete;
  112. }
  113. protected:
  114. inline void SetResolveStatus( ECompositeResolveStatus _status )
  115. {
  116. m_ResolveStatus = _status;
  117. }
  118. // This function is called only once during the first ResolveTraversal, and is
  119. // for the compositor to request its textures. Textures should not be requested
  120. // before this or they can be held waaaay too long.
  121. virtual void RequestTextures() = 0;
  122. // This function will be called during Resolve traversal. At the point when this is called,
  123. // all of this node's children will have had their resolve completed. Our siblings will
  124. // not have resolved yet.
  125. virtual void ResolveThis( CTextureCompositor* _comp ) = 0;
  126. // This function is called during HasTeamSpecifics traversal.
  127. virtual bool HasTeamSpecificsThis() const = 0;
  128. virtual bool ComputeRandomValuesThis( CUniformRandomStream* pRNG ) = 0;
  129. private:
  130. CInterlockedInt m_nReferenceCount;
  131. CTCStage* m_pFirstChild;
  132. CTCStage* m_pNextSibling;
  133. CTCStageResult_t m_Result;
  134. ECompositeResolveStatus m_ResolveStatus;
  135. };
  136. // ------------------------------------------------------------------------------------------------
  137. // ------------------------------------------------------------------------------------------------
  138. // ------------------------------------------------------------------------------------------------
  139. typedef void ( *ParseSingleKV )( KeyValues* _kv, void* _dest );
  140. struct ParseTableEntry
  141. {
  142. const char* keyName;
  143. ParseSingleKV parseFunc;
  144. size_t structOffset;
  145. };
  146. // ------------------------------------------------------------------------------------------------
  147. struct Range
  148. {
  149. float low;
  150. float high;
  151. Range( )
  152. : low( 0 )
  153. , high( 0 )
  154. { }
  155. Range( float _l, float _h )
  156. : low( _l )
  157. , high( _h )
  158. { }
  159. };
  160. // ------------------------------------------------------------------------------------------------
  161. // ------------------------------------------------------------------------------------------------
  162. // ------------------------------------------------------------------------------------------------
  163. void ParseBoolFromKV( KeyValues* _kv, void* _pDest )
  164. {
  165. bool* realDest = ( bool* ) _pDest;
  166. ( *realDest ) = _kv->GetBool();
  167. }
  168. // ------------------------------------------------------------------------------------------------
  169. template<int N>
  170. void ParseIntVectorFromKV( KeyValues* _kv, void* _pDest )
  171. {
  172. CCopyableUtlVector<int>* realDest = ( CCopyableUtlVector<int>* ) _pDest;
  173. const int parsedValue = _kv->GetInt();
  174. if ( realDest->Size() < N )
  175. {
  176. realDest->AddToTail( parsedValue );
  177. }
  178. else
  179. {
  180. DevWarning( "Too many numbers (>%d), ignoring the value '%d'.\n", N, parsedValue );
  181. }
  182. }
  183. // ------------------------------------------------------------------------------------------------
  184. template< class T >
  185. CUtlString AsStringT( const T& _val )
  186. {
  187. #ifdef _WIN32
  188. // Not sure why linux is unhappy here. Error messages unhelpful. Thanks, GCC.
  189. static_assert( false, "Must add specialization for typename T" );
  190. #endif
  191. return CUtlString( "" );
  192. }
  193. // ------------------------------------------------------------------------------------------------
  194. template<>
  195. CUtlString AsStringT< int >( const int& _val )
  196. {
  197. char buffer[ 12 ];
  198. V_sprintf_safe( buffer, "%d", _val );
  199. return CUtlString( buffer );
  200. }
  201. // ------------------------------------------------------------------------------------------------
  202. template< class T >
  203. void ParseTFromKV( KeyValues* _kv, void* _pDest )
  204. {
  205. #ifdef _WIN32
  206. // Not sure why linux is unhappy here. Error messages unhelpful. Thanks, GCC.
  207. static_assert( false, "Must add specialization for typename T" );
  208. #endif
  209. }
  210. // ------------------------------------------------------------------------------------------------
  211. template<>
  212. void ParseTFromKV< int >( KeyValues* _kv, void* _pDest )
  213. {
  214. int* realDest = ( int* ) _pDest;
  215. ( *realDest ) = _kv->GetInt();
  216. }
  217. // ------------------------------------------------------------------------------------------------
  218. template<>
  219. void ParseTFromKV< Vector2D >( KeyValues* _kv, void* _pDest )
  220. {
  221. Vector2D* realDest = ( Vector2D* ) _pDest;
  222. Vector2D tmpDest;
  223. int count = sscanf_s( _kv->GetString(), "%f %f", &tmpDest.x, &tmpDest.y );
  224. if ( count != 2 )
  225. {
  226. Error( "Expected exactly two values, %d were provided.\n", count );
  227. return;
  228. }
  229. *realDest = tmpDest;
  230. }
  231. // ------------------------------------------------------------------------------------------------
  232. template< class T, int N = INT_MAX >
  233. void ParseVectorFromKV( KeyValues* _kv, void* _pDest )
  234. {
  235. CCopyableUtlVector< T >* realDest = ( CCopyableUtlVector< T >* ) _pDest;
  236. T parsedValue = T();
  237. ParseTFromKV<T>( _kv, &parsedValue );
  238. if ( realDest->Size() < N )
  239. {
  240. realDest->AddToTail( parsedValue );
  241. }
  242. else
  243. {
  244. DevWarning( "Too many entries (>%d), ignoring the value '%s'.\n", N, AsStringT( parsedValue ).Get() );
  245. }
  246. }
  247. // ------------------------------------------------------------------------------------------------
  248. void ParseRangeFromKV( KeyValues* _kv, void* _pDest )
  249. {
  250. Range* realDest = ( Range* ) _pDest;
  251. Range tmpDest;
  252. int count = sscanf_s( _kv->GetString(), "%f %f", &tmpDest.low, &tmpDest.high );
  253. switch (count)
  254. {
  255. case 1:
  256. // If we parse one, use the same value for low and high.
  257. ( *realDest ).low = tmpDest.low;
  258. ( *realDest ).high = tmpDest.low;
  259. break;
  260. case 2:
  261. // If we parse two, they're both correct.
  262. ( *realDest ).low = tmpDest.low;
  263. ( *realDest ).high = tmpDest.high;
  264. break;
  265. // error cases
  266. case EOF:
  267. case 0:
  268. default:
  269. Error( "Incorrect number of numbers while parsing, using defaults. This error message should be improved\n" );
  270. };
  271. }
  272. // ------------------------------------------------------------------------------------------------
  273. void ParseInverseRangeFromKV( KeyValues* _kv, void* _pDest )
  274. {
  275. const float kSubstValue = 0.00001;
  276. ParseRangeFromKV( _kv, _pDest );
  277. Range* realDest = ( Range* ) _pDest;
  278. if ( realDest->low != 0.0f )
  279. {
  280. ( *realDest ).low = 1.0f / realDest->low;
  281. }
  282. else
  283. {
  284. Error( "Specified 0.0 for low value, that is illegal in this field. Substituting %.5f\n", kSubstValue );
  285. ( *realDest ).low = kSubstValue;
  286. }
  287. if ( realDest->high != 0.0f )
  288. {
  289. ( *realDest ).high = 1.0f / realDest->high;
  290. }
  291. else
  292. {
  293. Error( "Specified 0.0 for high value, that is illegal in this field. Substituting %.5f\n", kSubstValue );
  294. ( *realDest ).high = kSubstValue;
  295. }
  296. }
  297. // ------------------------------------------------------------------------------------------------
  298. template < int Div >
  299. void ParseRangeThenDivideBy( KeyValues *_kv, void* _pDest )
  300. {
  301. static_assert( Div != 0, "Cannot specify a divisor of 0." );
  302. float fDiv = (float) Div;
  303. ParseRangeFromKV( _kv, _pDest );
  304. Range* realDest = ( Range* ) _pDest;
  305. ( *realDest ).low = ( *realDest ).low / fDiv;
  306. ( *realDest ).high = ( *realDest ).high / fDiv;
  307. }
  308. // ------------------------------------------------------------------------------------------------
  309. void ParseStringFromKV( KeyValues* _kv, void* _pDest )
  310. {
  311. CUtlString* realDest = ( CUtlString* ) _pDest;
  312. (*realDest) = _kv->GetString();
  313. }
  314. // ------------------------------------------------------------------------------------------------
  315. struct TextureStageParameters
  316. {
  317. CUtlString m_pTexFilename;
  318. CUtlString m_pTexRedFilename;
  319. CUtlString m_pTexBlueFilename;
  320. Range m_AdjustBlack;
  321. Range m_AdjustOffset;
  322. Range m_AdjustGamma;
  323. Range m_Rotation;
  324. Range m_TranslateU;
  325. Range m_TranslateV;
  326. Range m_ScaleUV;
  327. bool m_AllowFlipU;
  328. bool m_AllowFlipV;
  329. bool m_Evaluate;
  330. TextureStageParameters()
  331. : m_AdjustBlack( 0, 0 )
  332. , m_AdjustOffset( 1, 1 )
  333. , m_AdjustGamma( 1, 1 )
  334. , m_Rotation( 0 , 0 )
  335. , m_TranslateU( 0, 0 )
  336. , m_TranslateV( 0, 0 )
  337. , m_ScaleUV( 1, 1 )
  338. , m_AllowFlipU( false )
  339. , m_AllowFlipV( false )
  340. , m_Evaluate( true )
  341. { }
  342. };
  343. // ------------------------------------------------------------------------------------------------
  344. const ParseTableEntry cTextureStageParametersParseTable[] =
  345. {
  346. { "texture", ParseStringFromKV, offsetof( TextureStageParameters, m_pTexFilename ) },
  347. { "texture_red", ParseStringFromKV, offsetof( TextureStageParameters, m_pTexRedFilename ) },
  348. { "texture_blue", ParseStringFromKV, offsetof( TextureStageParameters, m_pTexBlueFilename ) },
  349. { "adjust_black", ParseRangeThenDivideBy<255>, offsetof( TextureStageParameters, m_AdjustBlack ) },
  350. { "adjust_offset", ParseRangeThenDivideBy<255>, offsetof( TextureStageParameters, m_AdjustOffset ) },
  351. { "adjust_gamma", ParseInverseRangeFromKV, offsetof( TextureStageParameters, m_AdjustGamma ) },
  352. { "rotation", ParseRangeFromKV, offsetof( TextureStageParameters, m_Rotation ) },
  353. { "translate_u", ParseRangeFromKV, offsetof( TextureStageParameters, m_TranslateU ) },
  354. { "translate_v", ParseRangeFromKV, offsetof( TextureStageParameters, m_TranslateV ) },
  355. { "scale_uv", ParseRangeFromKV, offsetof( TextureStageParameters, m_ScaleUV ) },
  356. { "flip_u", ParseBoolFromKV, offsetof( TextureStageParameters, m_AllowFlipU ) },
  357. { "flip_v", ParseBoolFromKV, offsetof( TextureStageParameters, m_AllowFlipV ) },
  358. { "evaluate?", ParseBoolFromKV, offsetof( TextureStageParameters, m_Evaluate ) },
  359. { 0, 0 }
  360. };
  361. // ------------------------------------------------------------------------------------------------
  362. // ------------------------------------------------------------------------------------------------
  363. // ------------------------------------------------------------------------------------------------
  364. class CTCTextureStage : public CTCStage
  365. {
  366. public:
  367. CTCTextureStage( const TextureStageParameters& _tsp, uint32 nTexCompositeCreateFlags )
  368. : m_Parameters( _tsp )
  369. , m_pTex( NULL )
  370. , m_pTexRed( NULL )
  371. , m_pTexBlue( NULL )
  372. {
  373. }
  374. virtual ~CTCTextureStage()
  375. {
  376. SafeRelease( &m_pTex );
  377. SafeRelease( &m_pTexBlue );
  378. SafeRelease( &m_pTexRed );
  379. }
  380. virtual void OnAsyncFindComplete( ITexture* pTex, void* pExtraArgs )
  381. {
  382. switch ( ( int ) pExtraArgs )
  383. {
  384. case Neutral:
  385. SafeAssign( &m_pTex, pTex );
  386. break;
  387. case Red:
  388. SafeAssign( &m_pTexRed, pTex );
  389. break;
  390. case Blue:
  391. SafeAssign( &m_pTexBlue, pTex );
  392. break;
  393. default:
  394. Assert( !"Unexpected value passed to OnAsyncFindComplete" );
  395. break;
  396. };
  397. }
  398. virtual bool DoesTargetRenderTarget() const { return false; }
  399. protected:
  400. bool AreTexturesLoaded() const
  401. {
  402. if ( !m_Parameters.m_pTexFilename.IsEmpty() && !m_pTex )
  403. return false;
  404. if ( !m_Parameters.m_pTexRedFilename.IsEmpty() && !m_pTexRed )
  405. return false;
  406. if ( !m_Parameters.m_pTexBlueFilename.IsEmpty() && !m_pTexBlue )
  407. return false;
  408. return true;
  409. }
  410. ITexture* GetTeamSpecificTexture( int nTeam )
  411. {
  412. if ( nTeam == Red && m_pTexRed )
  413. return m_pTexRed;
  414. if ( nTeam == Blue && m_pTexBlue )
  415. return m_pTexBlue;
  416. return m_pTex;
  417. }
  418. virtual void RequestTextures()
  419. {
  420. if ( !m_Parameters.m_pTexFilename.IsEmpty() )
  421. materials->AsyncFindTexture( m_Parameters.m_pTexFilename.Get(), TEXTURE_GROUP_RUNTIME_COMPOSITE, this, ( void* ) Neutral, false, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  422. if ( !m_Parameters.m_pTexRedFilename.IsEmpty() )
  423. materials->AsyncFindTexture( m_Parameters.m_pTexRedFilename.Get(), TEXTURE_GROUP_RUNTIME_COMPOSITE, this, ( void* ) Red, false, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  424. if ( !m_Parameters.m_pTexBlueFilename.IsEmpty() )
  425. materials->AsyncFindTexture( m_Parameters.m_pTexBlueFilename.Get(), TEXTURE_GROUP_RUNTIME_COMPOSITE, this, ( void* ) Blue, false, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  426. }
  427. virtual void ResolveThis( CTextureCompositor* _comp )
  428. {
  429. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  430. // We shouldn't have any children, we're going to ignore them anyways.
  431. Assert( GetFirstChild() == NULL );
  432. ECompositeResolveStatus resolveStatus = GetResolveStatus();
  433. // If we're done, we're done.
  434. if ( resolveStatus == ECRS_Complete || resolveStatus == ECRS_Error )
  435. return;
  436. if ( resolveStatus == ECRS_Scheduled )
  437. SetResolveStatus( ECRS_PendingTextureLoads );
  438. // Someone is misusing this node if this assert fires.
  439. Assert( GetResolveStatus() == ECRS_PendingTextureLoads );
  440. // When the texture has finished loading, this will be set to the texture we should use.
  441. if ( !AreTexturesLoaded() )
  442. return;
  443. if ( !m_pTex && !m_pTexRed && !m_pTexBlue )
  444. {
  445. _comp->Error( false, "Invalid texture_lookup node, must specify at least texture (or texture_red and texture_blue) or all of them.\n" );
  446. return;
  447. }
  448. if ( m_pTex && m_pTex->IsError() )
  449. {
  450. _comp->Error( false, "Failed to load texture '%s', this is non-recoverable.\n", m_Parameters.m_pTexFilename.Get() );
  451. return;
  452. }
  453. if ( m_pTexRed && m_pTexRed->IsError() )
  454. {
  455. _comp->Error( false, "Failed to load texture_red '%s', this is non-recoverable.\n", m_Parameters.m_pTexRedFilename.Get() );
  456. return;
  457. }
  458. if ( m_pTexBlue && m_pTexBlue->IsError() )
  459. {
  460. _comp->Error( false, "Failed to load texture_blue '%s', this is non-recoverable.\n", m_Parameters.m_pTexBlueFilename.Get() );
  461. return;
  462. }
  463. CTCStageResult_t res;
  464. res.m_pTexture = GetTeamSpecificTexture( _comp->GetTeamNumber() );
  465. res.m_fAdjustBlackPoint = m_fAdjustBlack;
  466. res.m_fAdjustWhitePoint = m_fAdjustWhite;
  467. res.m_fAdjustGamma = m_fAdjustGamma;
  468. // Store the matrix into the uv adjustment matrix
  469. m_mTextureAdjust.Set3x4( res.m_mUvAdjust );
  470. SetResult( res );
  471. CleanupChildResults( _comp );
  472. tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE, "Completed: %s", __FUNCTION__ );
  473. }
  474. virtual bool HasTeamSpecificsThis() const OVERRIDE
  475. {
  476. return !m_Parameters.m_pTexBlueFilename.IsEmpty();
  477. }
  478. virtual bool ComputeRandomValuesThis( CUniformRandomStream* pRNG ) OVERRIDE
  479. {
  480. // If you change the order of these random numbers being generated, or add new ones, you will
  481. // change the look of existing players' weapons! Don't do that.
  482. const bool shouldFlipU = m_Parameters.m_AllowFlipU ? pRNG->RandomInt( 0, 1 ) != 0 : false;
  483. const bool shouldFlipV = m_Parameters.m_AllowFlipV ? pRNG->RandomInt( 0, 1 ) != 0 : false;
  484. const float translateU = pRNG->RandomFloat( m_Parameters.m_TranslateU.low, m_Parameters.m_TranslateU.high );
  485. const float translateV = pRNG->RandomFloat( m_Parameters.m_TranslateV.low, m_Parameters.m_TranslateV.high );
  486. const float rotation = pRNG->RandomFloat( m_Parameters.m_Rotation.low, m_Parameters.m_Rotation.high );
  487. const float scaleUV = pRNG->RandomFloat( m_Parameters.m_ScaleUV.low, m_Parameters.m_ScaleUV.high );
  488. const float adjustBlack = pRNG->RandomFloat( m_Parameters.m_AdjustBlack.low, m_Parameters.m_AdjustBlack.high );
  489. const float adjustOffset = pRNG->RandomFloat( m_Parameters.m_AdjustOffset.low, m_Parameters.m_AdjustOffset.high );
  490. const float adjustGamma = pRNG->RandomFloat( m_Parameters.m_AdjustGamma.low, m_Parameters.m_AdjustGamma.high );
  491. const float adjustWhite = adjustBlack + adjustOffset;
  492. m_fAdjustBlack = adjustBlack;
  493. m_fAdjustWhite = adjustWhite;
  494. m_fAdjustGamma = adjustGamma;
  495. const float finalScaleU = scaleUV * ( shouldFlipU ? -1.0f : 1.0f );
  496. const float finalScaleV = scaleUV * ( shouldFlipV ? -1.0f : 1.0f );
  497. MatrixBuildRotateZ( m_mTextureAdjust, rotation );
  498. m_mTextureAdjust = m_mTextureAdjust.Scale( Vector( finalScaleU, finalScaleV, 1.0f ) );
  499. MatrixTranslate( m_mTextureAdjust, Vector( translateU, translateV, 0 ) );
  500. // Copy W into Z because we're doing a texture matrix.
  501. m_mTextureAdjust[ 0 ][ 2 ] = m_mTextureAdjust[ 0 ][ 3 ];
  502. m_mTextureAdjust[ 1 ][ 2 ] = m_mTextureAdjust[ 1 ][ 3 ];
  503. m_mTextureAdjust[ 2 ][ 2 ] = 1.0f;
  504. return true;
  505. }
  506. private:
  507. TextureStageParameters m_Parameters;
  508. ITexture* m_pTex;
  509. ITexture* m_pTexRed;
  510. ITexture* m_pTexBlue;
  511. // Random values here
  512. float m_fAdjustBlack;
  513. float m_fAdjustWhite;
  514. float m_fAdjustGamma;
  515. VMatrix m_mTextureAdjust;
  516. };
  517. // ------------------------------------------------------------------------------------------------
  518. // ------------------------------------------------------------------------------------------------
  519. // ------------------------------------------------------------------------------------------------
  520. // Keep in sync with CombineOperation
  521. const char* cCombineMaterialName[] =
  522. {
  523. "dev/CompositorMultiply",
  524. "dev/CompositorAdd",
  525. "dev/CompositorLerp",
  526. "dev/CompositorSelect",
  527. "\0 ECO_Legacy_Lerp_FirstPass", // Procedural; starting with \0 will skip precaching
  528. "\0 ECO_Legacy_Lerp_SecondPass", // Procedural; starting with \0 will skip precaching
  529. "dev/CompositorBlend",
  530. "\0 ECO_LastPrecacheMaterial", //
  531. "CompositorError",
  532. NULL
  533. };
  534. static_assert( ARRAYSIZE( cCombineMaterialName ) == ECO_COUNT + 1, "cCombineMaterialName and ECombineOperation are out of sync." );
  535. // ------------------------------------------------------------------------------------------------
  536. struct CombineStageParameters
  537. {
  538. ECombineOperation m_CombineOp;
  539. Range m_AdjustBlack;
  540. Range m_AdjustOffset;
  541. Range m_AdjustGamma;
  542. Range m_Rotation;
  543. Range m_TranslateU;
  544. Range m_TranslateV;
  545. Range m_ScaleUV;
  546. bool m_AllowFlipU;
  547. bool m_AllowFlipV;
  548. bool m_Evaluate;
  549. CombineStageParameters()
  550. : m_CombineOp( ECO_Error )
  551. , m_AdjustBlack( 0, 0 )
  552. , m_AdjustOffset( 1, 1 )
  553. , m_AdjustGamma( 1, 1 )
  554. , m_Rotation( 0 , 0 )
  555. , m_TranslateU( 0, 0 )
  556. , m_TranslateV( 0, 0 )
  557. , m_ScaleUV( 1, 1 )
  558. , m_AllowFlipU( false )
  559. , m_AllowFlipV( false )
  560. , m_Evaluate( true )
  561. { }
  562. };
  563. // ------------------------------------------------------------------------------------------------
  564. void ParseOperationFromKV( KeyValues* _kv, void* _pDest )
  565. {
  566. ECombineOperation* realDest = ( ECombineOperation* ) _pDest;
  567. const char* opStr = _kv->GetString();
  568. if ( V_stricmp( "multiply", opStr ) == 0 )
  569. (*realDest) = ECO_Multiply;
  570. else if ( V_stricmp( "add", opStr ) == 0 )
  571. (*realDest) = ECO_Add;
  572. else if ( V_stricmp( "lerp", opStr) == 0 )
  573. (*realDest) = ECO_Lerp;
  574. else
  575. (*realDest) = ECO_Error;
  576. }
  577. // ------------------------------------------------------------------------------------------------
  578. const ParseTableEntry cCombineStageParametersParseTable[] =
  579. {
  580. { "adjust_black", ParseRangeThenDivideBy<255>, offsetof( CombineStageParameters, m_AdjustBlack ) },
  581. { "adjust_offset", ParseRangeThenDivideBy<255>, offsetof( CombineStageParameters, m_AdjustOffset ) },
  582. { "adjust_gamma", ParseInverseRangeFromKV, offsetof( CombineStageParameters, m_AdjustGamma ) },
  583. { "rotation", ParseRangeFromKV, offsetof( CombineStageParameters, m_Rotation ) },
  584. { "translate_u", ParseRangeFromKV, offsetof( CombineStageParameters, m_TranslateU ) },
  585. { "translate_v", ParseRangeFromKV, offsetof( CombineStageParameters, m_TranslateV ) },
  586. { "scale_uv", ParseRangeFromKV, offsetof( CombineStageParameters, m_ScaleUV ) },
  587. { "flip_u", ParseBoolFromKV, offsetof( CombineStageParameters, m_AllowFlipU ) },
  588. { "flip_v", ParseBoolFromKV, offsetof( CombineStageParameters, m_AllowFlipV ) },
  589. { "evaluate?", ParseBoolFromKV, offsetof( CombineStageParameters, m_Evaluate ) },
  590. { 0, 0 }
  591. };
  592. // ------------------------------------------------------------------------------------------------
  593. // ------------------------------------------------------------------------------------------------
  594. // ------------------------------------------------------------------------------------------------
  595. class CTCCombineStage : public CTCStage
  596. {
  597. public:
  598. CTCCombineStage( const CombineStageParameters& _csp, uint32 nTexCompositeCreateFlags )
  599. : m_Parameters( _csp )
  600. , m_pMaterial( NULL )
  601. {
  602. Assert( m_Parameters.m_CombineOp >= 0 && m_Parameters.m_CombineOp < ECO_COUNT );
  603. SafeAssign( &m_pMaterial, materials->FindMaterial( cCombineMaterialName[ m_Parameters.m_CombineOp ], TEXTURE_GROUP_RUNTIME_COMPOSITE ) );
  604. }
  605. virtual ~CTCCombineStage()
  606. {
  607. SafeRelease( &m_pMaterial );
  608. }
  609. virtual bool DoesTargetRenderTarget() const { return true; }
  610. protected:
  611. virtual void RequestTextures() { /* No textures here */ }
  612. virtual void ResolveThis( CTextureCompositor* _comp )
  613. {
  614. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  615. ECompositeResolveStatus resolveStatus = GetResolveStatus();
  616. // If we're done, we're done.
  617. if ( resolveStatus == ECRS_Complete || resolveStatus == ECRS_Error )
  618. return;
  619. if ( resolveStatus == ECRS_Scheduled )
  620. SetResolveStatus( ECRS_PendingTextureLoads );
  621. // Someone is misusing this node if this assert fires.
  622. Assert( GetResolveStatus() == ECRS_PendingTextureLoads );
  623. for ( CTCStage* child = GetFirstChild(); child; child = child->GetNextSibling() )
  624. {
  625. // If any child isn't ready to go, we're not ready to go.
  626. if ( child->GetResolveStatus() != ECRS_Complete )
  627. return;
  628. }
  629. ITexture* pRenderTarget = _comp->AllocateCompositorRenderTarget();
  630. CUtlVector<CTCStageResult_t> results;
  631. uint childCount = 0;
  632. for ( CTCStage* child = GetFirstChild(); child; child = child->GetNextSibling() )
  633. {
  634. results.AddToTail( child->GetResult() );
  635. ++childCount;
  636. }
  637. // TODO: If there are more than 8 children, need to split them into multiple groups here. Skip it for now.
  638. Render( pRenderTarget, m_pMaterial, results, _comp, true );
  639. CTCStageResult_t res;
  640. res.m_pRenderTarget = pRenderTarget;
  641. res.m_fAdjustBlackPoint = m_fAdjustBlack;
  642. res.m_fAdjustWhitePoint = m_fAdjustWhite;
  643. res.m_fAdjustGamma = m_fAdjustGamma;
  644. SetResult( res );
  645. // As soon as we have scheduled the read of a child render target, we can release that
  646. // texture back to the pool for use by another stage. Everything is pipelined, so this just
  647. // works.
  648. CleanupChildResults( _comp );
  649. tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE, "Completed: %s", __FUNCTION__ );
  650. }
  651. virtual bool HasTeamSpecificsThis() const OVERRIDE{ return false; }
  652. virtual bool ComputeRandomValuesThis( CUniformRandomStream* pRNG ) OVERRIDE
  653. {
  654. const float adjustBlack = pRNG->RandomFloat( m_Parameters.m_AdjustBlack.low, m_Parameters.m_AdjustBlack.high );
  655. const float adjustOffset = pRNG->RandomFloat( m_Parameters.m_AdjustOffset.low, m_Parameters.m_AdjustOffset.high );
  656. const float adjustGamma = pRNG->RandomFloat( m_Parameters.m_AdjustGamma.low, m_Parameters.m_AdjustGamma.high );
  657. const float adjustWhite = adjustBlack + adjustOffset;
  658. m_fAdjustBlack = adjustBlack;
  659. m_fAdjustWhite = adjustWhite;
  660. m_fAdjustGamma = adjustGamma;
  661. return true;
  662. }
  663. private:
  664. CombineStageParameters m_Parameters;
  665. IMaterial* m_pMaterial;
  666. float m_fAdjustBlack;
  667. float m_fAdjustWhite;
  668. float m_fAdjustGamma;
  669. };
  670. // ------------------------------------------------------------------------------------------------
  671. struct SelectStageParameters
  672. {
  673. CUtlString m_pTexFilename;
  674. CCopyableUtlVector<int> m_Select;
  675. bool m_Evaluate;
  676. SelectStageParameters()
  677. : m_Evaluate( true )
  678. {
  679. }
  680. };
  681. // ------------------------------------------------------------------------------------------------
  682. const ParseTableEntry cSelectStageParametersParseTable[] =
  683. {
  684. { "groups", ParseStringFromKV, offsetof( SelectStageParameters, m_pTexFilename ) },
  685. { "select", ParseVectorFromKV< int, cMaxSelectors >, offsetof( SelectStageParameters, m_Select ) },
  686. { "evaluate?", ParseBoolFromKV, offsetof( SelectStageParameters, m_Evaluate ) },
  687. { 0, 0 }
  688. };
  689. // ------------------------------------------------------------------------------------------------
  690. // ------------------------------------------------------------------------------------------------
  691. // ------------------------------------------------------------------------------------------------
  692. class CTCSelectStage : public CTCStage
  693. {
  694. public:
  695. CTCSelectStage( const SelectStageParameters& _ssp, uint32 nTexCompositeCreateFlags )
  696. : m_Parameters( _ssp )
  697. , m_pMaterial( NULL )
  698. , m_pTex( NULL )
  699. {
  700. SafeAssign( &m_pMaterial, materials->FindMaterial( cCombineMaterialName[ ECO_Select ], TEXTURE_GROUP_RUNTIME_COMPOSITE ) );
  701. }
  702. virtual ~CTCSelectStage()
  703. {
  704. SafeRelease( &m_pMaterial );
  705. SafeRelease( &m_pTex );
  706. }
  707. virtual void OnAsyncFindComplete( ITexture* pTex, void* pExtraArgs ) { SafeAssign( &m_pTex, pTex ); }
  708. virtual bool DoesTargetRenderTarget() const { return true; }
  709. protected:
  710. virtual void RequestTextures()
  711. {
  712. materials->AsyncFindTexture( m_Parameters.m_pTexFilename.Get(), TEXTURE_GROUP_RUNTIME_COMPOSITE, this, NULL, false, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  713. }
  714. virtual void ResolveThis( CTextureCompositor* _comp )
  715. {
  716. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  717. // We shouldn't have any children, we're going to ignore them anyways.
  718. Assert( GetFirstChild() == NULL );
  719. ECompositeResolveStatus resolveStatus = GetResolveStatus();
  720. // If we're done, we're done.
  721. if ( resolveStatus == ECRS_Complete || resolveStatus == ECRS_Error )
  722. return;
  723. if ( resolveStatus == ECRS_Scheduled )
  724. SetResolveStatus( ECRS_PendingTextureLoads );
  725. // Someone is misusing this node if this assert fires.
  726. Assert( GetResolveStatus() == ECRS_PendingTextureLoads );
  727. // When the texture has finished loading, this will be set to the texture we should use.
  728. if ( m_pTex == NULL )
  729. return;
  730. if ( m_pTex->IsError() )
  731. {
  732. _comp->Error( false, "Failed to load texture %s, this is non-recoverable.\n", m_Parameters.m_pTexFilename.Get() );
  733. return;
  734. }
  735. ITexture* pRenderTarget = _comp->AllocateCompositorRenderTarget();
  736. char buffer[128];
  737. for ( int i = 0; i < cMaxSelectors; ++i )
  738. {
  739. bool bFound = false;
  740. V_snprintf( buffer, ARRAYSIZE( buffer ), "$selector%d", i );
  741. IMaterialVar* pVar = m_pMaterial->FindVar( buffer, &bFound );
  742. Assert(bFound);
  743. if ( i < m_Parameters.m_Select.Size() )
  744. pVar->SetIntValue( m_Parameters.m_Select[i] );
  745. else
  746. pVar->SetIntValue( 0 );
  747. }
  748. CTCStageResult_t inRes;
  749. inRes.m_pTexture = m_pTex;
  750. CUtlVector<CTCStageResult_t> fakeResults;
  751. fakeResults.AddToTail( inRes );
  752. Render( pRenderTarget, m_pMaterial, fakeResults, _comp, true );
  753. CTCStageResult_t outRes;
  754. outRes.m_pRenderTarget = pRenderTarget;
  755. SetResult( outRes );
  756. CleanupChildResults( _comp );
  757. tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE, "Completed: %s", __FUNCTION__ );
  758. }
  759. virtual bool HasTeamSpecificsThis() const OVERRIDE { return false; }
  760. virtual bool ComputeRandomValuesThis( CUniformRandomStream* pRNG ) OVERRIDE
  761. {
  762. // No RNG here.
  763. return false;
  764. }
  765. private:
  766. SelectStageParameters m_Parameters;
  767. IMaterial* m_pMaterial;
  768. ITexture* m_pTex;
  769. };
  770. // ------------------------------------------------------------------------------------------------
  771. // ------------------------------------------------------------------------------------------------
  772. // ------------------------------------------------------------------------------------------------
  773. struct Sticker_t
  774. {
  775. float m_fWeight; // Random likelihood this one is to be selected
  776. CUtlString m_baseFilename; // Name of the base file for the sticker (the albedo).
  777. CUtlString m_specFilename; // Name of the specular file for the sticker, or if blank we will assume it is baseFilename + _spec + baseExtension
  778. Sticker_t()
  779. : m_fWeight( 1.0 )
  780. { }
  781. };
  782. // ------------------------------------------------------------------------------------------------
  783. template<>
  784. void ParseTFromKV< Sticker_t >( KeyValues* _kv, void* _pDest )
  785. {
  786. Sticker_t* realDest = ( Sticker_t* ) _pDest;
  787. Sticker_t tmpDest;
  788. tmpDest.m_fWeight = _kv->GetFloat( "weight", 1.0 );
  789. tmpDest.m_baseFilename = _kv->GetString( "base" );
  790. KeyValues* pSpec = _kv->FindKey( "spec" );
  791. if ( pSpec )
  792. tmpDest.m_specFilename = pSpec->GetString();
  793. else
  794. {
  795. CUtlString specPath = tmpDest.m_baseFilename.StripExtension()
  796. + "_s"
  797. + tmpDest.m_baseFilename.GetExtension();
  798. tmpDest.m_specFilename = specPath;
  799. }
  800. *realDest = tmpDest;
  801. }
  802. // ------------------------------------------------------------------------------------------------
  803. template <>
  804. CUtlString AsStringT< Sticker_t >( const Sticker_t& _val )
  805. {
  806. char buffer[ 80 ];
  807. V_sprintf_safe( buffer, "[ weight %.2f; base \"%s\"; spec \"%s\" ]", _val.m_fWeight, _val.m_baseFilename.Get(), _val.m_specFilename.Get() );
  808. return CUtlString( buffer );
  809. }
  810. // ------------------------------------------------------------------------------------------------
  811. template< class T >
  812. struct Settable_t
  813. {
  814. T m_val;
  815. bool m_bSet;
  816. Settable_t()
  817. : m_val( T() )
  818. , m_bSet( false )
  819. { }
  820. };
  821. // ------------------------------------------------------------------------------------------------
  822. template < class T >
  823. void ParseSettable( KeyValues *_kv, void* _pDest )
  824. {
  825. Settable_t<T> *pSettable = ( Settable_t<T>* )_pDest;
  826. ParseTFromKV<T>( _kv, &pSettable->m_val );
  827. ( *pSettable ).m_bSet = true;
  828. }
  829. // ------------------------------------------------------------------------------------------------
  830. struct ApplyStickerStageParameters
  831. {
  832. CCopyableUtlVector< Sticker_t > m_possibleStickers;
  833. Settable_t< Vector2D > m_vDestBL;
  834. Settable_t< Vector2D > m_vDestTL;
  835. Settable_t< Vector2D > m_vDestTR;
  836. Range m_AdjustBlack;
  837. Range m_AdjustOffset;
  838. Range m_AdjustGamma;
  839. bool m_Evaluate;
  840. ApplyStickerStageParameters()
  841. : m_AdjustBlack( 0, 0 )
  842. , m_AdjustOffset( 1, 1 )
  843. , m_AdjustGamma( 1, 1 )
  844. , m_Evaluate( true )
  845. { }
  846. };
  847. // ------------------------------------------------------------------------------------------------
  848. const ParseTableEntry cApplyStickerStageParametersParseTable[] =
  849. {
  850. { "sticker", ParseVectorFromKV< Sticker_t >, offsetof( ApplyStickerStageParameters, m_possibleStickers ) },
  851. { "dest_bl", ParseSettable< Vector2D >, offsetof( ApplyStickerStageParameters, m_vDestBL ) },
  852. { "dest_tl", ParseSettable< Vector2D >, offsetof( ApplyStickerStageParameters, m_vDestTL ) },
  853. { "dest_tr", ParseSettable< Vector2D >, offsetof( ApplyStickerStageParameters, m_vDestTR ) },
  854. { "adjust_black", ParseRangeThenDivideBy< 255 >, offsetof( ApplyStickerStageParameters, m_AdjustBlack ) },
  855. { "adjust_offset", ParseRangeThenDivideBy< 255 >, offsetof( ApplyStickerStageParameters, m_AdjustOffset ) },
  856. { "adjust_gamma", ParseInverseRangeFromKV, offsetof( ApplyStickerStageParameters, m_AdjustGamma ) },
  857. { "evaluate?", ParseBoolFromKV, offsetof( ApplyStickerStageParameters, m_Evaluate ) },
  858. { 0, 0 }
  859. };
  860. // ------------------------------------------------------------------------------------------------
  861. // ------------------------------------------------------------------------------------------------
  862. // ------------------------------------------------------------------------------------------------
  863. class CTCApplyStickerStage : public CTCStage
  864. {
  865. enum { Albedo = 0, Specular = 1 };
  866. public:
  867. CTCApplyStickerStage( const ApplyStickerStageParameters& _assp, uint32 nTexCompositeCreateFlags )
  868. : m_Parameters( _assp )
  869. , m_pMaterial( NULL )
  870. , m_pTex( NULL )
  871. , m_pTexSpecular( NULL )
  872. , m_nChoice( 0 )
  873. {
  874. SafeAssign( &m_pMaterial, materials->FindMaterial( cCombineMaterialName[ ECO_Blend ], TEXTURE_GROUP_RUNTIME_COMPOSITE ) );
  875. }
  876. virtual ~CTCApplyStickerStage()
  877. {
  878. SafeRelease( &m_pTex );
  879. SafeRelease( &m_pTexSpecular );
  880. SafeRelease( &m_pMaterial );
  881. }
  882. virtual bool DoesTargetRenderTarget() const { return true; }
  883. protected:
  884. bool AreTexturesLoaded() const
  885. {
  886. if ( !m_Parameters.m_possibleStickers[ m_nChoice ].m_baseFilename.IsEmpty() && !m_pTex )
  887. return false;
  888. if ( !m_Parameters.m_possibleStickers[ m_nChoice ].m_specFilename.IsEmpty() && !m_pTexSpecular )
  889. return false;
  890. return true;
  891. }
  892. virtual void RequestTextures()
  893. {
  894. if ( !m_Parameters.m_possibleStickers[ m_nChoice ].m_baseFilename.IsEmpty() )
  895. materials->AsyncFindTexture( m_Parameters.m_possibleStickers[ m_nChoice ].m_baseFilename.Get(), TEXTURE_GROUP_RUNTIME_COMPOSITE, this, ( void* ) Albedo, false, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  896. if ( !m_Parameters.m_possibleStickers[ m_nChoice ].m_specFilename.IsEmpty() )
  897. materials->AsyncFindTexture( m_Parameters.m_possibleStickers[ m_nChoice ].m_specFilename.Get(), TEXTURE_GROUP_RUNTIME_COMPOSITE, this, ( void* ) Specular, false, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  898. }
  899. virtual void ResolveThis( CTextureCompositor* _comp )
  900. {
  901. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  902. ECompositeResolveStatus resolveStatus = GetResolveStatus();
  903. // If we're done, we're done.
  904. if ( resolveStatus == ECRS_Complete || resolveStatus == ECRS_Error )
  905. return;
  906. if ( resolveStatus == ECRS_Scheduled )
  907. SetResolveStatus( ECRS_PendingTextureLoads );
  908. // Someone is misusing this node if this assert fires.
  909. Assert( GetResolveStatus() == ECRS_PendingTextureLoads );
  910. CTCStage* pChild = GetFirstChild();
  911. if ( pChild != NULL && pChild->GetResolveStatus() != ECRS_Complete )
  912. return;
  913. if ( !AreTexturesLoaded() )
  914. return;
  915. // Ensure we only have zero or one direct children.
  916. Assert( !pChild || pChild->GetNextSibling() == NULL );
  917. // We expect exactly one or zero children. If we have a child, use its render target to render to, otherwise
  918. // Get one and use that.
  919. ITexture* pRenderTarget = _comp->AllocateCompositorRenderTarget();
  920. CUtlVector<CTCStageResult_t> results;
  921. // If we have a child, great! Use it. If not,
  922. if ( pChild )
  923. results.AddToTail( pChild->GetResult() );
  924. else
  925. {
  926. CTCStageResult_t fakeRes;
  927. fakeRes.m_pTexture = materials->FindTexture( "black", TEXTURE_GROUP_RUNTIME_COMPOSITE );
  928. }
  929. CTCStageResult_t baseTex, specTex;
  930. baseTex.m_pTexture = m_pTex;
  931. m_mTextureAdjust.Set3x4( baseTex.m_mUvAdjust );
  932. results.AddToTail( baseTex );
  933. specTex.m_pTexture = m_pTexSpecular;
  934. m_mTextureAdjust.Set3x4( specTex.m_mUvAdjust );
  935. results.AddToTail( specTex );
  936. Render( pRenderTarget, m_pMaterial, results, _comp, pChild == NULL );
  937. CTCStageResult_t res;
  938. res.m_pRenderTarget = pRenderTarget;
  939. res.m_fAdjustBlackPoint = m_fAdjustBlack;
  940. res.m_fAdjustWhitePoint = m_fAdjustWhite;
  941. res.m_fAdjustGamma = m_fAdjustGamma;
  942. SetResult( res );
  943. // As soon as we have scheduled the read of a child render target, we can release that
  944. // texture back to the pool for use by another stage. Everything is pipelined, so this just
  945. // works.
  946. CleanupChildResults( _comp );
  947. tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE, "Completed: %s", __FUNCTION__ );
  948. }
  949. virtual bool HasTeamSpecificsThis() const OVERRIDE{ return false; }
  950. virtual bool ComputeRandomValuesThis( CUniformRandomStream* pRNG ) OVERRIDE
  951. {
  952. float m_fTotalWeight = 0;
  953. FOR_EACH_VEC( m_Parameters.m_possibleStickers, i )
  954. {
  955. m_fTotalWeight += m_Parameters.m_possibleStickers[ i ].m_fWeight;
  956. }
  957. float fWeight = pRNG->RandomFloat( 0.0f, m_fTotalWeight );
  958. FOR_EACH_VEC( m_Parameters.m_possibleStickers, i )
  959. {
  960. const float thisWeight = m_Parameters.m_possibleStickers[ i ].m_fWeight;
  961. if ( fWeight < thisWeight )
  962. {
  963. m_nChoice = i;
  964. break;
  965. }
  966. else
  967. {
  968. fWeight -= thisWeight;
  969. }
  970. }
  971. const float adjustBlack = pRNG->RandomFloat( m_Parameters.m_AdjustBlack.low, m_Parameters.m_AdjustBlack.high );
  972. const float adjustOffset = pRNG->RandomFloat( m_Parameters.m_AdjustOffset.low, m_Parameters.m_AdjustOffset.high );
  973. const float adjustGamma = pRNG->RandomFloat( m_Parameters.m_AdjustGamma.low, m_Parameters.m_AdjustGamma.high );
  974. const float adjustWhite = adjustBlack + adjustOffset;
  975. m_fAdjustBlack = adjustBlack;
  976. m_fAdjustWhite = adjustWhite;
  977. m_fAdjustGamma = adjustGamma;
  978. ComputeTextureMatrixFromRectangle( &m_mTextureAdjust, m_Parameters.m_vDestBL.m_val, m_Parameters.m_vDestTL.m_val, m_Parameters.m_vDestTR.m_val );
  979. return true;
  980. }
  981. virtual void OnAsyncFindComplete( ITexture* pTex, void* pExtraArgs )
  982. {
  983. switch ( ( int ) pExtraArgs )
  984. {
  985. case Albedo:
  986. SafeAssign( &m_pTex, pTex );
  987. break;
  988. case Specular:
  989. // It's okay if this is the case, we just need to substitute with the black texture.
  990. if ( pTex->IsError() )
  991. {
  992. pTex = materials->FindTexture( "black", TEXTURE_GROUP_RUNTIME_COMPOSITE );
  993. }
  994. SafeAssign( &m_pTexSpecular, pTex );
  995. break;
  996. default:
  997. Assert( !"Unexpected value passed to OnAsyncFindComplete" );
  998. break;
  999. };
  1000. }
  1001. private:
  1002. ApplyStickerStageParameters m_Parameters;
  1003. IMaterial* m_pMaterial;
  1004. ITexture* m_pTex;
  1005. ITexture* m_pTexSpecular;
  1006. int m_nChoice;
  1007. float m_fAdjustBlack;
  1008. float m_fAdjustWhite;
  1009. float m_fAdjustGamma;
  1010. VMatrix m_mTextureAdjust;
  1011. };
  1012. // ------------------------------------------------------------------------------------------------
  1013. // ------------------------------------------------------------------------------------------------
  1014. // ------------------------------------------------------------------------------------------------
  1015. // This is a procedural stage we use to copy the results of a composite into a texture so we can
  1016. // release the render targets back to a pool to be used later.
  1017. class CTCCopyStage : public CTCStage
  1018. {
  1019. public:
  1020. CTCCopyStage()
  1021. : m_pTex( NULL )
  1022. {
  1023. }
  1024. ~CTCCopyStage()
  1025. {
  1026. SafeRelease( &m_pTex );
  1027. }
  1028. virtual void OnAsyncCreateComplete( ITexture* pTex, void* pExtraArgs )
  1029. {
  1030. SafeAssign( &m_pTex, pTex );
  1031. tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE, "Completed: %s", __FUNCTION__ );
  1032. }
  1033. virtual bool DoesTargetRenderTarget() const { return false; }
  1034. private:
  1035. virtual void RequestTextures() { /* No input textures */ }
  1036. virtual void ResolveThis( CTextureCompositor* _comp )
  1037. {
  1038. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1039. ECompositeResolveStatus resolveStatus = GetResolveStatus();
  1040. // If we're done, we're done.
  1041. if ( resolveStatus == ECRS_Complete || resolveStatus == ECRS_Error )
  1042. return;
  1043. if ( resolveStatus == ECRS_Scheduled )
  1044. SetResolveStatus( ECRS_PendingTextureLoads );
  1045. Assert( GetFirstChild() != NULL );
  1046. // Can't move forward until the child is done.
  1047. if ( GetFirstChild()->GetResolveStatus() != ECRS_Complete )
  1048. return;
  1049. // Compositing has completed!
  1050. if ( m_pTex )
  1051. {
  1052. if ( m_pTex->IsError() )
  1053. {
  1054. _comp->Error( false, "Error occurred copying render target to texture. This is fatal." );
  1055. return;
  1056. }
  1057. CTCStageResult_t res;
  1058. res.m_pTexture = m_pTex;
  1059. #ifdef STAGING_ONLY
  1060. if ( r_texcomp_dump.GetInt() == 2 )
  1061. {
  1062. char buffer[128];
  1063. V_snprintf( buffer, ARRAYSIZE(buffer), "composite_%s_result_%02d.tga", _comp->GetName().Get(), s_nDumpCount++ );
  1064. GetFirstChild()->GetResult().m_pRenderTarget->SaveToFile( buffer );
  1065. }
  1066. #endif
  1067. SetResult( res );
  1068. return;
  1069. }
  1070. if ( resolveStatus == ECRS_PendingComposites )
  1071. return;
  1072. ImageFormat fmt = IMAGE_FORMAT_DXT5_RUNTIME;
  1073. if ( _comp->GetCreateFlags() & TEX_COMPOSITE_CREATE_FLAGS_NO_COMPRESSION )
  1074. fmt = IMAGE_FORMAT_RGBA8888;
  1075. bool bGenMipmaps = !( _comp->GetCreateFlags() & TEX_COMPOSITE_CREATE_FLAGS_NO_MIPMAPS );
  1076. // We want to do this once only.
  1077. char buffer[_MAX_PATH];
  1078. _comp->GetTextureName( buffer, ARRAYSIZE( buffer ) );
  1079. int nCreateFlags = TEXTUREFLAGS_IMMEDIATE_CLEANUP
  1080. | TEXTUREFLAGS_TRILINEAR
  1081. | TEXTUREFLAGS_ANISOTROPIC;
  1082. #if defined( STAGING_ONLY )
  1083. #if WITH_TEX_COMPOSITE_CACHE
  1084. if ( r_texcomp_dump.GetInt() == 0 && ( _comp->GetCreateFlags() & TEX_COMPOSITE_CREATE_FLAGS_FORCE ) == 0 )
  1085. nCreateFlags = 0;
  1086. #endif
  1087. #endif
  1088. CMatRenderContextPtr pRenderContext( materials );
  1089. pRenderContext->AsyncCreateTextureFromRenderTarget( GetFirstChild()->GetResult().m_pRenderTarget, buffer, fmt, bGenMipmaps, nCreateFlags, this, NULL );
  1090. SetResolveStatus( ECRS_PendingComposites );
  1091. // Don't clean up here just yet, we'll get cleaned up when the composite is totally complete.
  1092. tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE, "Begun: %s", __FUNCTION__ );
  1093. }
  1094. virtual bool HasTeamSpecificsThis() const OVERRIDE { return false; }
  1095. virtual bool ComputeRandomValuesThis( CUniformRandomStream* pRNG ) OVERRIDE
  1096. {
  1097. // No RNG here.
  1098. return false;
  1099. }
  1100. ITexture* m_pTex;
  1101. CUtlString m_FinalTextureName;
  1102. uint32 m_nTexCompositeCreateFlags;
  1103. };
  1104. // ------------------------------------------------------------------------------------------------
  1105. // ------------------------------------------------------------------------------------------------
  1106. // ------------------------------------------------------------------------------------------------
  1107. CTextureCompositor::CTextureCompositor( int _width, int _height, int nTeam, const char* pCompositeName, uint64 nRandomSeed, uint32 nTexCompositeCreateFlags )
  1108. : m_nReferenceCount( 0 )
  1109. , m_nWidth( _width )
  1110. , m_nHeight( _height )
  1111. , m_nTeam( nTeam )
  1112. , m_nRandomSeed( nRandomSeed )
  1113. , m_pRootStage( NULL )
  1114. , m_ResolveStatus( ECRS_Idle )
  1115. , m_bError( false )
  1116. , m_bFatal( false )
  1117. , m_nRenderTargetsAllocated( 0 )
  1118. , m_CompositeName( pCompositeName )
  1119. , m_nTexCompositeCreateFlags( nTexCompositeCreateFlags )
  1120. , m_bHasTeamSpecifics( false )
  1121. , m_nCompositePaintKitId( 0 )
  1122. {
  1123. }
  1124. // ------------------------------------------------------------------------------------------------
  1125. CTextureCompositor::~CTextureCompositor()
  1126. {
  1127. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1128. Assert ( m_nReferenceCount == 0 );
  1129. // Have to clean up the stages before cleaning up the render target pool, because cleanup up
  1130. // stages will throw things back to the render target pool.
  1131. SafeRelease( &m_pRootStage );
  1132. FOR_EACH_VEC( m_RenderTargetPool, i )
  1133. {
  1134. RenderTarget_t& rt = m_RenderTargetPool[ i ];
  1135. SafeRelease( &rt.m_pRT );
  1136. }
  1137. }
  1138. // ------------------------------------------------------------------------------------------------
  1139. void CTextureCompositor::Restart()
  1140. {
  1141. Assert(!"TODO! Need to clone the root node, then cleanup the old root and start the new work.");
  1142. // CTCStage* clone = m_pRootStage->Clone();
  1143. SafeRelease( &m_pRootStage );
  1144. // m_pRootStage = clone;
  1145. m_ResolveStatus = ECRS_Scheduled;
  1146. // Kick it off again
  1147. m_pRootStage->Resolve( true, this );
  1148. m_ResolveStatus = ECRS_PendingTextureLoads;
  1149. }
  1150. // ------------------------------------------------------------------------------------------------
  1151. void CTextureCompositor::Shutdown()
  1152. {
  1153. // If this thing is a template, then it's a faker and doesn't have an m_pRootStage. This is
  1154. // only true during startup when we're just verifying that the templates look sane--later
  1155. // they should have real data.
  1156. if ( m_pRootStage )
  1157. m_pRootStage->Cleanup( this );
  1158. // These should match now.
  1159. Assert( m_nRenderTargetsAllocated == m_RenderTargetPool.Count() );
  1160. }
  1161. // ------------------------------------------------------------------------------------------------
  1162. int CTextureCompositor::AddRef()
  1163. {
  1164. return ++m_nReferenceCount;
  1165. }
  1166. // ------------------------------------------------------------------------------------------------
  1167. int CTextureCompositor::Release()
  1168. {
  1169. int retVal = --m_nReferenceCount;
  1170. Assert( retVal >= 0 );
  1171. if ( retVal == 0 )
  1172. {
  1173. Shutdown();
  1174. delete this;
  1175. }
  1176. return retVal;
  1177. }
  1178. // ------------------------------------------------------------------------------------------------
  1179. void CTextureCompositor::Update()
  1180. {
  1181. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1182. Assert( m_pRootStage );
  1183. if ( m_bError )
  1184. {
  1185. if ( !m_bFatal )
  1186. {
  1187. m_bError = false;
  1188. Restart();
  1189. }
  1190. else
  1191. m_ResolveStatus = ECRS_Error;
  1192. return;
  1193. }
  1194. if ( m_pRootStage->GetResolveStatus() != ECRS_Complete )
  1195. m_pRootStage->Resolve( false, this );
  1196. if ( m_pRootStage->GetResolveStatus() == ECRS_Complete )
  1197. {
  1198. #ifdef STAGING_ONLY
  1199. // One time, go ahead and dump out the texture if we're supposed to right here, at completion time.
  1200. if ( ( r_texcomp_dump.GetInt() == 3 || r_texcomp_dump.GetInt() == 4 ) && m_ResolveStatus != ECRS_Complete )
  1201. {
  1202. char filename[_MAX_PATH];
  1203. V_sprintf_safe( filename, "%s.tga", m_CompositeName.Get() );
  1204. m_pRootStage->GetResult().m_pTexture->SaveToFile( filename );
  1205. }
  1206. #endif
  1207. m_ResolveStatus = ECRS_Complete;
  1208. #ifdef RAD_TELEMETRY_ENABLED
  1209. char buffer[ 256 ];
  1210. GetTextureName( buffer, ARRAYSIZE( buffer ) );
  1211. tmEndTimeSpan( TELEMETRY_LEVEL0, m_nCompositePaintKitId, 0, "Composite: %s", tmDynamicString( TELEMETRY_LEVEL0, buffer ) );
  1212. #endif
  1213. }
  1214. }
  1215. // ------------------------------------------------------------------------------------------------
  1216. ITexture* CTextureCompositor::GetResultTexture() const
  1217. {
  1218. Assert( m_pRootStage && m_pRootStage->GetResolveStatus() == ECRS_Complete );
  1219. Assert( m_pRootStage->GetResult().m_pTexture );
  1220. return m_pRootStage->GetResult().m_pTexture;
  1221. }
  1222. // ------------------------------------------------------------------------------------------------
  1223. ECompositeResolveStatus CTextureCompositor::GetResolveStatus() const
  1224. {
  1225. return m_ResolveStatus;
  1226. }
  1227. // ------------------------------------------------------------------------------------------------
  1228. void CTextureCompositor::ScheduleResolve( )
  1229. {
  1230. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1231. Assert( m_pRootStage );
  1232. Assert( m_ResolveStatus == ECRS_Idle );
  1233. #if WITH_TEX_COMPOSITE_CACHE
  1234. if ( ( GetCreateFlags() & TEX_COMPOSITE_CREATE_FLAGS_FORCE ) == 0)
  1235. {
  1236. char buffer[ _MAX_PATH ];
  1237. GetTextureName( buffer, ARRAYSIZE( buffer ) );
  1238. // I think there's a race condition here, add a flag to FindTexture that says only if loaded, and bumps ref?
  1239. if ( materials->IsTextureLoaded( buffer ) )
  1240. {
  1241. ITexture* resTexture = materials->FindTexture( buffer, TEXTURE_GROUP_RUNTIME_COMPOSITE, false, 0 );
  1242. if ( resTexture && resTexture->IsError() == false )
  1243. {
  1244. m_pRootStage->OnAsyncCreateComplete( resTexture, NULL );
  1245. CTCStageResult_t res;
  1246. res.m_pTexture = resTexture;
  1247. m_pRootStage->SetResult( res );
  1248. m_ResolveStatus = ECRS_Complete;
  1249. return;
  1250. }
  1251. }
  1252. }
  1253. #endif
  1254. #ifdef RAD_TELEMETRY_ENABLED
  1255. m_nCompositePaintKitId = ++s_nCompositeCount;
  1256. char buffer[256];
  1257. GetTextureName( buffer, ARRAYSIZE( buffer ) );
  1258. tmBeginTimeSpan( TELEMETRY_LEVEL0, m_nCompositePaintKitId, 0, "Composite: %s", tmDynamicString( TELEMETRY_LEVEL0, buffer ) );
  1259. #endif
  1260. m_ResolveStatus = ECRS_Scheduled;
  1261. // Naughty.
  1262. extern CMaterialSystem g_MaterialSystem;
  1263. g_MaterialSystem.ScheduleTextureComposite( this );
  1264. }
  1265. // ------------------------------------------------------------------------------------------------
  1266. void CTextureCompositor::Resolve()
  1267. {
  1268. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1269. // We can actually get in multiply times for the same one because of the way EconItemView works.
  1270. // So if that's the case, bail.
  1271. if ( m_ResolveStatus != ECRS_Scheduled )
  1272. return;
  1273. m_pRootStage->Resolve( true, this );
  1274. // Update our resolve status
  1275. m_ResolveStatus = ECRS_PendingTextureLoads;
  1276. }
  1277. // ------------------------------------------------------------------------------------------------
  1278. void CTextureCompositor::Error( bool _retry, const char* _debugDevMsg, ... )
  1279. {
  1280. m_bError = true;
  1281. m_bFatal = !_retry;
  1282. va_list args;
  1283. va_start( args, _debugDevMsg );
  1284. WarningV( _debugDevMsg, args );
  1285. va_end( args );
  1286. }
  1287. // ------------------------------------------------------------------------------------------------
  1288. void CTextureCompositor::SetRootStage( CTCStage* rootStage )
  1289. {
  1290. SafeAssign( &m_pRootStage, rootStage );
  1291. // After we set a root, compute everyone's RNG values. Do this once, early, to ensure the values are stable.
  1292. uint32 seedhi = 0;
  1293. uint32 seedlo = 0;
  1294. GetSeed( &seedhi, &seedlo );
  1295. CUniformRandomStream streams[2];
  1296. streams[0].SetSeed( seedhi );
  1297. streams[1].SetSeed( seedlo );
  1298. int currentIndex = 0;
  1299. m_pRootStage->ComputeRandomValues( &currentIndex, streams, ARRAYSIZE( streams ) );
  1300. }
  1301. // ------------------------------------------------------------------------------------------------
  1302. // TODO: Need to accept format and depth status
  1303. ITexture* CTextureCompositor::AllocateCompositorRenderTarget( )
  1304. {
  1305. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1306. FOR_EACH_VEC( m_RenderTargetPool, i )
  1307. {
  1308. const RenderTarget_t& rt = m_RenderTargetPool[ i ];
  1309. if ( rt.m_nWidth == m_nWidth && rt.m_nHeight == m_nHeight )
  1310. {
  1311. ITexture* retVal = rt.m_pRT;
  1312. m_RenderTargetPool.Remove( i );
  1313. return retVal;
  1314. }
  1315. }
  1316. // Lie to the material system that we are asking for this allocation way back at the beginning of time.
  1317. // This used to matter to GPUs for perf, but hasn't in a long time.
  1318. materials->OverrideRenderTargetAllocation( true );
  1319. ITexture* retVal = materials->CreateNamedRenderTargetTextureEx( "", m_nWidth, m_nHeight, RT_SIZE_LITERAL_PICMIP, IMAGE_FORMAT_RGBA8888, MATERIAL_RT_DEPTH_NONE, TEXTUREFLAGS_IMMEDIATE_CLEANUP );
  1320. Assert( retVal );
  1321. materials->OverrideRenderTargetAllocation( false );
  1322. // Used to count how many we actually allocated so we can verify we cleaned them all up at
  1323. // shutdown
  1324. ++m_nRenderTargetsAllocated;
  1325. return retVal;
  1326. }
  1327. // ------------------------------------------------------------------------------------------------
  1328. void CTextureCompositor::ReleaseCompositorRenderTarget( ITexture* _tex )
  1329. {
  1330. Assert( _tex );
  1331. int w = _tex->GetMappingWidth();
  1332. int h = _tex->GetMappingHeight();
  1333. RenderTarget_t rt = { w, h, _tex };
  1334. m_RenderTargetPool.AddToTail( rt );
  1335. }
  1336. // ------------------------------------------------------------------------------------------------
  1337. void CTextureCompositor::GetTextureName( char* pOutBuffer, int nBufferLen ) const
  1338. {
  1339. uint32 seedhi = 0;
  1340. uint32 seedlo = 0;
  1341. GetSeed( &seedhi, &seedlo );
  1342. Assert( m_pRootStage != NULL );
  1343. if ( m_pRootStage->HasTeamSpecifics() )
  1344. V_snprintf( pOutBuffer, nBufferLen, "proc/texcomp/%s_flags%08x_seedhi%08x_seedlo%08x_team%d_w%d_h%d", GetName().Get(), GetCreateFlags(), seedhi, seedlo, m_nTeam, m_nWidth, m_nHeight );
  1345. else
  1346. V_snprintf( pOutBuffer, nBufferLen, "proc/texcomp/%s_flags%08x_seedhi%08x_seedlo%08x_w%d_h%d", GetName().Get(), GetCreateFlags(), seedhi, seedlo, m_nWidth, m_nHeight );
  1347. }
  1348. // ------------------------------------------------------------------------------------------------
  1349. void CTextureCompositor::GetSeed( uint32* pOutHi, uint32* pOutLo ) const
  1350. {
  1351. tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ );
  1352. Assert( pOutHi && pOutLo );
  1353. ( *pOutHi ) = 0;
  1354. ( *pOutLo ) = 0;
  1355. // This is most definitely not the most efficient way to do this.
  1356. for ( int i = 0; i < 32; ++i )
  1357. {
  1358. ( *pOutHi ) |= (uint32)( ( m_nRandomSeed & ( uint64( 1 ) << ( ( 2 * i ) + 0 ) ) ) >> i );
  1359. ( *pOutLo ) |= (uint32)( ( m_nRandomSeed & ( uint64( 1 ) << ( ( 2 * i ) + 1 ) ) ) >> ( i + 1 ) );
  1360. }
  1361. }
  1362. // ------------------------------------------------------------------------------------------------
  1363. // ------------------------------------------------------------------------------------------------
  1364. // ------------------------------------------------------------------------------------------------
  1365. CTCStage::CTCStage()
  1366. : m_nReferenceCount( 1 ) // This is 1 because the common case is to assign these as children, and we don't want to play with refs there.
  1367. , m_pFirstChild( NULL )
  1368. , m_pNextSibling( NULL )
  1369. , m_ResolveStatus( ECRS_Idle )
  1370. { }
  1371. // ------------------------------------------------------------------------------------------------
  1372. CTCStage::~CTCStage()
  1373. {
  1374. Assert ( m_nReferenceCount == 0 );
  1375. SafeRelease( &m_pFirstChild );
  1376. SafeRelease( &m_pNextSibling );
  1377. }
  1378. // ------------------------------------------------------------------------------------------------
  1379. int CTCStage::AddRef()
  1380. {
  1381. return ++m_nReferenceCount;
  1382. }
  1383. // ------------------------------------------------------------------------------------------------
  1384. int CTCStage::Release()
  1385. {
  1386. int retVal = --m_nReferenceCount;
  1387. if ( retVal == 0 )
  1388. delete this;
  1389. return retVal;
  1390. }
  1391. // ------------------------------------------------------------------------------------------------
  1392. void CTCStage::Resolve( bool bFirstTime, CTextureCompositor* _comp )
  1393. {
  1394. if ( m_pFirstChild )
  1395. m_pFirstChild->Resolve( bFirstTime, _comp );
  1396. // Update our status, which may be updated below. Only do this the first time through.
  1397. if ( bFirstTime )
  1398. {
  1399. m_ResolveStatus = ECRS_Scheduled;
  1400. // Request textures here. We used to request in the constructor, but it caused us
  1401. // to potentially hold all paintkitted textures for all time. That's bad for Mac,
  1402. // where we are super memory constrained.
  1403. RequestTextures();
  1404. }
  1405. ResolveThis( _comp );
  1406. if ( m_pNextSibling )
  1407. m_pNextSibling->Resolve( bFirstTime, _comp );
  1408. }
  1409. // ------------------------------------------------------------------------------------------------
  1410. bool CTCStage::HasTeamSpecifics( ) const
  1411. {
  1412. if ( m_pFirstChild && m_pFirstChild->HasTeamSpecifics() )
  1413. return true;
  1414. if ( HasTeamSpecificsThis() )
  1415. return true;
  1416. return m_pNextSibling && m_pNextSibling->HasTeamSpecifics();
  1417. }
  1418. // ------------------------------------------------------------------------------------------------
  1419. void CTCStage::ComputeRandomValues( int* pCurIndex, CUniformRandomStream* pRNGs, int nRNGCount )
  1420. {
  1421. Assert( pCurIndex != NULL );
  1422. Assert( pRNGs != NULL );
  1423. Assert( nRNGCount != 0 );
  1424. // We do a depth-first traversal here, but we hit ourselves first.
  1425. if ( ComputeRandomValuesThis( &pRNGs[*pCurIndex] ) )
  1426. {
  1427. // Switch which RNG the next person will use.
  1428. ( *pCurIndex ) = ( ( *pCurIndex ) + 1 ) % nRNGCount;
  1429. }
  1430. if ( m_pFirstChild )
  1431. m_pFirstChild->ComputeRandomValues( pCurIndex, pRNGs, nRNGCount );
  1432. if ( m_pNextSibling )
  1433. m_pNextSibling->ComputeRandomValues( pCurIndex, pRNGs, nRNGCount );
  1434. }
  1435. // ------------------------------------------------------------------------------------------------
  1436. void CTCStage::CleanupChildResults( CTextureCompositor* _comp )
  1437. {
  1438. // This does not recurse. We call it as we move through the tree to clean up our
  1439. // first-generation children.
  1440. for ( CTCStage* child = GetFirstChild(); child; child = child->GetNextSibling() )
  1441. {
  1442. child->m_Result.Cleanup( _comp );
  1443. child->m_Result = CTCStageResult_t();
  1444. }
  1445. }
  1446. // ------------------------------------------------------------------------------------------------
  1447. void CTCStage::Render( ITexture* _destRT, IMaterial* _mat, const CUtlVector<CTCStageResult_t>& _inputs, CTextureCompositor* _comp, bool bClear )
  1448. {
  1449. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1450. CUtlVector< IMaterialVar* > varsToClean;
  1451. bool bFound = false;
  1452. char buffer[128];
  1453. FOR_EACH_VEC( _inputs, i )
  1454. {
  1455. const CTCStageResult_t& stageParams = _inputs[ i ];
  1456. Assert( stageParams.m_pTexture || stageParams.m_pRenderTarget );
  1457. ITexture* inTex = stageParams.m_pTexture
  1458. ? stageParams.m_pTexture
  1459. : stageParams.m_pRenderTarget;
  1460. V_snprintf( buffer, ARRAYSIZE( buffer ), "$srctexture%d", i );
  1461. // Set the texture
  1462. IMaterialVar* var = _mat->FindVar( buffer, &bFound );
  1463. Assert( bFound );
  1464. var->SetTextureValue( inTex );
  1465. varsToClean.AddToTail( var );
  1466. // And the levels parameters
  1467. V_snprintf( buffer, ARRAYSIZE(buffer), "$texadjustlevels%d", i );
  1468. var = _mat->FindVar( buffer, &bFound );
  1469. Assert(bFound);
  1470. var->SetVecValue( stageParams.m_fAdjustBlackPoint, stageParams.m_fAdjustWhitePoint, stageParams.m_fAdjustGamma );
  1471. // And the expected transform
  1472. V_snprintf( buffer, ARRAYSIZE(buffer), "$textransform%d", i );
  1473. var = _mat->FindVar( buffer, &bFound );
  1474. Assert(bFound);
  1475. var->SetMatrixValue( stageParams.m_mUvAdjust );
  1476. }
  1477. IMaterialVar* var = _mat->FindVar( "$textureinputcount", &bFound );
  1478. Assert( bFound );
  1479. var->SetIntValue( _inputs.Count() );
  1480. CMatRenderContextPtr pRenderContext( materials );
  1481. int w = _destRT->GetActualWidth();
  1482. int h = _destRT->GetActualHeight();
  1483. pRenderContext->PushRenderTargetAndViewport( _destRT, 0, 0, w, h );
  1484. if ( bClear )
  1485. {
  1486. pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
  1487. pRenderContext->ClearBuffers( true, false, false );
  1488. }
  1489. // Perform the render!
  1490. pRenderContext->DrawScreenSpaceQuad( _mat );
  1491. #ifdef STAGING_ONLY
  1492. if (r_texcomp_dump.GetInt() == 1)
  1493. {
  1494. FOR_EACH_VEC(_inputs, i)
  1495. {
  1496. if (_inputs[i].m_pTexture)
  1497. {
  1498. V_snprintf(buffer, ARRAYSIZE(buffer), "composite_%s_input_%02d_in%01d_%08x.tga", _comp->GetName().Get(), s_nDumpCount, i, (int) this);
  1499. _inputs[i].m_pTexture->SaveToFile(buffer);
  1500. }
  1501. }
  1502. V_snprintf(buffer, ARRAYSIZE(buffer), "composite_%s_result_%02d_%08x.tga", _comp->GetName().Get(), s_nDumpCount++, (int) this);
  1503. _destRT->SaveToFile(buffer);
  1504. }
  1505. #endif
  1506. // Restore previous state
  1507. pRenderContext->PopRenderTargetAndViewport();
  1508. // After rendering, clean up the leftover texture references or they will be there for a long
  1509. // time.
  1510. FOR_EACH_VEC( varsToClean, i )
  1511. {
  1512. varsToClean[ i ]->SetUndefined();
  1513. }
  1514. }
  1515. // ------------------------------------------------------------------------------------------------
  1516. void CTCStage::Cleanup( CTextureCompositor* _comp )
  1517. {
  1518. if ( m_pFirstChild )
  1519. m_pFirstChild->Cleanup( _comp );
  1520. m_Result.Cleanup( _comp );
  1521. if ( m_pNextSibling )
  1522. m_pNextSibling->Cleanup( _comp );
  1523. }
  1524. // ------------------------------------------------------------------------------------------------
  1525. // ------------------------------------------------------------------------------------------------
  1526. // ------------------------------------------------------------------------------------------------
  1527. typedef bool ( *TBuildNodeFromKVFunc )( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags );
  1528. bool TexStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags );
  1529. template<int Type> bool CombineStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags );
  1530. bool SelectStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags );
  1531. bool ApplyStickerStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags );
  1532. struct NodeDefinitionEntry
  1533. {
  1534. const char* keyName;
  1535. TBuildNodeFromKVFunc buildFunc;
  1536. };
  1537. NodeDefinitionEntry cNodeParseTable[] =
  1538. {
  1539. { "texture_lookup", TexStageFromKV },
  1540. { "combine_add", CombineStageFromKV<ECO_Add> },
  1541. { "combine_lerp", CombineStageFromKV<ECO_Lerp> },
  1542. { "combine_multiply", CombineStageFromKV<ECO_Multiply> },
  1543. { "select", SelectStageFromKV },
  1544. { "apply_sticker", ApplyStickerStageFromKV },
  1545. { 0, 0 }
  1546. };
  1547. // ------------------------------------------------------------------------------------------------
  1548. template<typename S>
  1549. void ParseIntoStruct( S* _outStruct, CUtlVector< KeyValues *>* _leftovers, KeyValues* _kv, uint32 nTexCompositeCreateFlags, const ParseTableEntry* _entries )
  1550. {
  1551. Assert( _leftovers );
  1552. const char* keyName = _kv->GetName();
  1553. keyName;
  1554. FOR_EACH_SUBKEY( _kv, thisKey )
  1555. {
  1556. bool parsed = false;
  1557. for ( int e = 0; _entries[e].keyName; ++e )
  1558. {
  1559. if ( V_stricmp( _entries[e].keyName, thisKey->GetName() ) == 0 )
  1560. {
  1561. // If we're instancing, go ahead and run the parse function. If we're just doing template verification
  1562. // then the right hand side may still have variables that need to be expanded, so just verify that the
  1563. // left hand side is sane.
  1564. if ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) == 0 )
  1565. {
  1566. void* pDest = ((unsigned char*)_outStruct) + _entries[e].structOffset;
  1567. _entries[e].parseFunc( thisKey, pDest );
  1568. }
  1569. parsed = true;
  1570. break;
  1571. }
  1572. }
  1573. if ( !parsed )
  1574. {
  1575. ( *_leftovers ).AddToTail( thisKey );
  1576. }
  1577. }
  1578. }
  1579. // ------------------------------------------------------------------------------------------------
  1580. bool ParseNodes( CUtlVector< CTCStage* >* _outStages, const CUtlVector< KeyValues *>& _kvs, uint32 nTexCompositeCreateFlags )
  1581. {
  1582. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1583. bool anyFails = false;
  1584. FOR_EACH_VEC( _kvs, thisKey )
  1585. {
  1586. KeyValues *thisKV = _kvs[ thisKey ];
  1587. bool parsed = false;
  1588. for ( int e = 0; cNodeParseTable[ e ].keyName; ++e )
  1589. {
  1590. if ( V_stricmp( cNodeParseTable[ e ].keyName, thisKV->GetName() ) == 0 )
  1591. {
  1592. CTCStage* pNewStage = NULL;
  1593. if ( !cNodeParseTable[ e ].buildFunc( &pNewStage, thisKV->GetName(), thisKV, nTexCompositeCreateFlags ) )
  1594. anyFails = true;
  1595. (*_outStages).AddToTail( pNewStage );
  1596. parsed = true;
  1597. break;
  1598. }
  1599. }
  1600. if (!parsed)
  1601. {
  1602. DevWarning( "Compositor Error: Unexpected key '%s' while parsing definition.\n", thisKV->GetName() );
  1603. anyFails = true;
  1604. }
  1605. }
  1606. return !anyFails;
  1607. }
  1608. // ------------------------------------------------------------------------------------------------
  1609. bool TexStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags )
  1610. {
  1611. Assert( ppOutStage != NULL );
  1612. TextureStageParameters tsp;
  1613. CUtlVector< KeyValues* > leftovers;
  1614. CUtlVector< CTCStage* > childNodes;
  1615. ParseIntoStruct( &tsp, &leftovers, _kv, nTexCompositeCreateFlags, cTextureStageParametersParseTable );
  1616. if ( !ParseNodes( &childNodes, leftovers, nTexCompositeCreateFlags ) )
  1617. return false;
  1618. if ( !( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) )
  1619. {
  1620. ( *ppOutStage ) = new CTCTextureStage( tsp, nTexCompositeCreateFlags );
  1621. ( *ppOutStage )->AppendChildren( childNodes );
  1622. }
  1623. return true;
  1624. }
  1625. // ------------------------------------------------------------------------------------------------
  1626. template <int Type>
  1627. bool CombineStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags )
  1628. {
  1629. Assert( ppOutStage != NULL );
  1630. static_assert( Type >= 0 && Type < ECO_Error, "Invalid type, you need to update the enum." );
  1631. CombineStageParameters csp;
  1632. csp.m_CombineOp = (ECombineOperation) Type;
  1633. CUtlVector< KeyValues* > leftovers;
  1634. CUtlVector< CTCStage* > childNodes;
  1635. ParseIntoStruct( &csp, &leftovers, _kv, nTexCompositeCreateFlags, cCombineStageParametersParseTable );
  1636. if ( !ParseNodes( &childNodes, leftovers, nTexCompositeCreateFlags ) )
  1637. return false;
  1638. if ( !( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) )
  1639. {
  1640. ( *ppOutStage ) = new CTCCombineStage( csp, nTexCompositeCreateFlags );
  1641. ( *ppOutStage )->AppendChildren( childNodes );
  1642. }
  1643. return true;
  1644. }
  1645. // ------------------------------------------------------------------------------------------------
  1646. bool SelectStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags )
  1647. {
  1648. Assert( ppOutStage != NULL );
  1649. SelectStageParameters ssp;
  1650. CUtlVector< KeyValues* > leftovers;
  1651. CUtlVector< CTCStage* > childNodes;
  1652. ParseIntoStruct( &ssp, &leftovers, _kv, nTexCompositeCreateFlags, cSelectStageParametersParseTable );
  1653. if ( !ParseNodes( &childNodes, leftovers, nTexCompositeCreateFlags ) )
  1654. return false;
  1655. if ( !( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) )
  1656. {
  1657. ( *ppOutStage ) = new CTCSelectStage( ssp, nTexCompositeCreateFlags );
  1658. ( *ppOutStage )->AppendChildren( childNodes );
  1659. }
  1660. return true;
  1661. }
  1662. // ------------------------------------------------------------------------------------------------
  1663. bool ApplyStickerStageFromKV( CTCStage** ppOutStage, const char* _key, KeyValues* _kv, uint32 nTexCompositeCreateFlags )
  1664. {
  1665. Assert( ppOutStage != NULL );
  1666. ApplyStickerStageParameters assp;
  1667. CUtlVector< KeyValues* > leftovers;
  1668. CUtlVector< CTCStage* > childNodes;
  1669. ParseIntoStruct( &assp, &leftovers, _kv, nTexCompositeCreateFlags, cApplyStickerStageParametersParseTable );
  1670. if ( !ParseNodes( &childNodes, leftovers, nTexCompositeCreateFlags ) )
  1671. return false;
  1672. // These stages can have exactly one child.
  1673. if ( childNodes.Count() > 1 )
  1674. return false;
  1675. int setCount = 0;
  1676. if ( assp.m_vDestBL.m_bSet ) ++setCount;
  1677. if ( assp.m_vDestTL.m_bSet ) ++setCount;
  1678. if ( assp.m_vDestTR.m_bSet ) ++setCount;
  1679. if ( setCount != 3 )
  1680. return false;
  1681. if ( !( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) )
  1682. {
  1683. ( *ppOutStage ) = new CTCApplyStickerStage( assp, nTexCompositeCreateFlags );
  1684. ( *ppOutStage )->AppendChildren( childNodes );
  1685. }
  1686. return true;
  1687. }
  1688. // ------------------------------------------------------------------------------------------------
  1689. const char *GetCombinedMaterialName( ECombineOperation eMaterial )
  1690. {
  1691. Assert( eMaterial >= ECO_FirstPrecacheMaterial && eMaterial < ECO_COUNT );
  1692. return cCombineMaterialName[eMaterial];
  1693. }
  1694. // ------------------------------------------------------------------------------------------------
  1695. KeyValues* ResolveTemplate( const char* pRootName, KeyValues* pValues, uint32 nTexCompositeCreateFlags, bool *pInOutAllocdNew )
  1696. {
  1697. Assert( pRootName != NULL && pValues != NULL && pInOutAllocdNew != NULL );
  1698. const char* pTemplateName = NULL;
  1699. bool bImplementsTemplate = false;
  1700. bool bHasOtherNodes = false;
  1701. // First, figure out if the tree is sensible.
  1702. FOR_EACH_SUBKEY( pValues, pChild )
  1703. {
  1704. const char* pChildName = pChild->GetName();
  1705. if ( V_stricmp( pChildName, "implements" ) == 0 )
  1706. {
  1707. if ( bImplementsTemplate )
  1708. {
  1709. Warning( "ERROR[%s]: implements field can only appear once, seen a second time as 'implements \"%s\"\n", pRootName, pChild->GetString() );
  1710. return NULL;
  1711. }
  1712. bImplementsTemplate = true;
  1713. pTemplateName = pChild->GetString();
  1714. }
  1715. else if ( pChildName && pChildName[0] != '$' )
  1716. {
  1717. bHasOtherNodes = true;
  1718. }
  1719. }
  1720. if ( bImplementsTemplate && bHasOtherNodes )
  1721. {
  1722. Warning( "ERROR[%s]: if using 'implements', can only have variable definitions--other fields not allowed.\n", pRootName );
  1723. return NULL;
  1724. }
  1725. // If we're not doing templates, we're all finished.
  1726. if ( !bImplementsTemplate )
  1727. return pValues;
  1728. KeyValues* pNewKV = NULL;
  1729. if ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) == 0 )
  1730. {
  1731. CTextureCompositorTemplate* pTmpl = TextureManager()->FindTextureCompositorTemplate( pTemplateName );
  1732. if ( !pTmpl )
  1733. {
  1734. Warning( "ERROR[%s]: Couldn't find template named '%s'.\n", pRootName, pTemplateName );
  1735. return NULL;
  1736. }
  1737. Assert( pTmpl->GetKV() );
  1738. // If the verify flag isn't set, we're instancing the template so do all the logic.
  1739. if ( pTmpl->ImplementsTemplate() )
  1740. {
  1741. pNewKV = ResolveTemplate( pRootName, pTmpl->GetKV(), nTexCompositeCreateFlags, pInOutAllocdNew );
  1742. }
  1743. else
  1744. {
  1745. // The root-most template will allocate the memory for all of us.
  1746. pNewKV = pTmpl->GetKV()->MakeCopy();
  1747. pNewKV->SetName( pRootName );
  1748. ( *pInOutAllocdNew ) = true;
  1749. }
  1750. }
  1751. else
  1752. {
  1753. // Just return the original KV back to the caller, who just wants a success code here.
  1754. return pValues;
  1755. }
  1756. // Now, copy any child var definitions from pValues into pNewKV. Because of the recursive call stack,
  1757. // this has the net effect that more concrete templates will write their values later than more remote templates.
  1758. FOR_EACH_SUBKEY( pValues, pChild )
  1759. {
  1760. const char* pChildName = pChild->GetName();
  1761. if ( pChildName && pChildName[0] == '$' )
  1762. {
  1763. pNewKV->AddSubKey( pChild->MakeCopy() );
  1764. }
  1765. }
  1766. // Success!
  1767. return pNewKV;
  1768. }
  1769. // ------------------------------------------------------------------------------------------------
  1770. typedef CUtlDict< const char* > VariableDefs_t;
  1771. KeyValues* ExtractVariableDefinitions( VariableDefs_t* pOutVarDefs, const char* pRootName, KeyValues* pKeyValues )
  1772. {
  1773. Assert( pOutVarDefs );
  1774. FOR_EACH_SUBKEY( pKeyValues, pChild )
  1775. {
  1776. const char* pChildName = pChild->GetName();
  1777. if ( pChildName[0] == '$' )
  1778. {
  1779. if ( pChild->GetFirstTrueSubKey() )
  1780. {
  1781. Warning( "ERROR[%s]: All variable definitions must be simple strings, '%s' was a full subtree.\n", pRootName, pChildName );
  1782. return NULL;
  1783. }
  1784. int ndx = ( *pOutVarDefs ).Find( pChildName + 1 );
  1785. if ( pOutVarDefs->IsValidIndex( ndx ) )
  1786. ( *pOutVarDefs )[ ndx ] = pChild->GetString();
  1787. else
  1788. ( *pOutVarDefs ).Insert( pChildName + 1, pChild->GetString() );
  1789. }
  1790. }
  1791. return pKeyValues;
  1792. }
  1793. // ------------------------------------------------------------------------------------------------
  1794. CUtlString GetErrorTrail( CUtlVector< const char* >& errorStack )
  1795. {
  1796. if ( errorStack.Count() == 0 )
  1797. return CUtlString( "" );
  1798. const int stackLength = errorStack.Count();
  1799. const int stackLengthMinusOne = stackLength - 1;
  1800. const char* cStageSep = " -> ";
  1801. const int cStageSepStrLen = V_strlen( cStageSep );
  1802. int totalStrLength = 0;
  1803. for ( int i = 0; i < stackLength; ++i )
  1804. {
  1805. totalStrLength += V_strlen( errorStack[ i ] );
  1806. }
  1807. totalStrLength += stackLengthMinusOne * cStageSepStrLen;
  1808. CUtlString retStr;
  1809. retStr.SetLength( totalStrLength );
  1810. char* pDstOrig = retStr.GetForModify(); pDstOrig;
  1811. char* pDst = retStr.GetForModify();
  1812. int destPos = 0;
  1813. for ( int i = 0; i < stackLength; ++i )
  1814. {
  1815. // Copy the string
  1816. const char* pSrc = errorStack[ i ];
  1817. while ( ( *pDst++ = *pSrc++ ) != 0 )
  1818. ++destPos;
  1819. --pDst;
  1820. if ( i < stackLengthMinusOne )
  1821. {
  1822. // Now copy our separator
  1823. pSrc = cStageSep;
  1824. while ( ( *pDst++ = *pSrc++ ) != 0 )
  1825. ++destPos;
  1826. --pDst;
  1827. }
  1828. }
  1829. Assert( destPos == totalStrLength );
  1830. Assert( pDst - retStr.Get() == totalStrLength );
  1831. // SetLength above already included the +1 to length for the null terminator.
  1832. *pDst = '\0';
  1833. return retStr;
  1834. }
  1835. // ------------------------------------------------------------------------------------------------
  1836. enum ParseMode
  1837. {
  1838. Copy,
  1839. DetermineStringForReplace,
  1840. };
  1841. // ------------------------------------------------------------------------------------------------
  1842. // Returns the number of characters written into pOutBuffer or -1 if there was an error.
  1843. int SubstituteVarsRecursive( char* pOutBuffer, int* pOutSubsts, CUtlVector< const char* >& errorStack, const char* pStr, uint32 nTexCompositeCreateFlags, const VariableDefs_t& varDefs )
  1844. {
  1845. ParseMode mode = Copy;
  1846. char* pCurVariable = NULL;
  1847. char* pDst = pOutBuffer;
  1848. int srcPos = 0;
  1849. int dstPos = 0;
  1850. while ( pStr[ srcPos ] != 0 )
  1851. {
  1852. const char* srcC = pStr + srcPos;
  1853. switch ( mode )
  1854. {
  1855. case Copy:
  1856. if ( srcC[ 0 ] == '$' && srcC[ 1 ] == '[' )
  1857. {
  1858. mode = DetermineStringForReplace;
  1859. srcPos += 2;
  1860. pCurVariable = const_cast< char* >( pStr + srcPos );
  1861. continue;
  1862. }
  1863. else if ( pOutBuffer )
  1864. {
  1865. pDst[ dstPos++ ] = pStr[ srcPos++ ];
  1866. }
  1867. else
  1868. {
  1869. ++dstPos;
  1870. ++srcPos;
  1871. }
  1872. break;
  1873. case DetermineStringForReplace:
  1874. if ( srcC[ 0 ] == ']' )
  1875. {
  1876. // Make a modification so we can just do the lookup from this buffer.
  1877. pCurVariable[ srcC - pCurVariable ] = 0;
  1878. // Lookup our substitution value.
  1879. int ndx = varDefs.Find( pCurVariable );
  1880. const char* pSubstText = NULL;
  1881. if ( ndx != varDefs.InvalidIndex() )
  1882. {
  1883. pSubstText = varDefs[ ndx ];
  1884. }
  1885. else if ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) != 0 )
  1886. {
  1887. pSubstText = ""; // It's fine to run into these when verifying the template only.
  1888. }
  1889. else
  1890. {
  1891. Warning( "ERROR[%s]: Couldn't find variable named $%s that was requested to be substituted.\n", ( const char* ) GetErrorTrail( errorStack ), pCurVariable );
  1892. // Restore the string first.
  1893. pCurVariable[ srcC - pCurVariable ] = ']';
  1894. return -1;
  1895. }
  1896. // Put it back.
  1897. pCurVariable[ srcC - pCurVariable ] = ']';
  1898. int charsWritten = SubstituteVarsRecursive( pOutBuffer ? &pDst[ dstPos ] : NULL, pOutSubsts, errorStack, pSubstText, nTexCompositeCreateFlags, varDefs );
  1899. if ( charsWritten < 0 )
  1900. return -1;
  1901. ++( *pOutSubsts );
  1902. dstPos += charsWritten;
  1903. ++srcPos;
  1904. mode = Copy;
  1905. }
  1906. else
  1907. {
  1908. ++srcPos;
  1909. }
  1910. break;
  1911. }
  1912. }
  1913. if ( mode == DetermineStringForReplace )
  1914. {
  1915. Warning( "ERROR[%s]: Variable $[%s missing closing bracket ].\n", ( const char* ) GetErrorTrail( errorStack ), pCurVariable );
  1916. return -1;
  1917. }
  1918. return dstPos;
  1919. }
  1920. // ------------------------------------------------------------------------------------------------
  1921. // Returns true if successful, false otherwise.
  1922. bool SubstituteVars( CUtlString* pOutStr, int* pOutSubsts, CUtlVector< const char* >& errorStack, const char* pStr, uint32 nTexCompositeCreateFlags, const VariableDefs_t& varDefs )
  1923. {
  1924. Assert( pOutStr != NULL && pOutSubsts != NULL && pStr != NULL );
  1925. ( *pOutSubsts ) = 0;
  1926. // Even though this involves a traversal, we're saving a malloc by walking this thing once looking for the start token.
  1927. const char* pFirstRepl = V_strstr( pStr, "$[" );
  1928. // No substitutions, so bail out now.
  1929. if ( pFirstRepl == NULL )
  1930. {
  1931. ( *pOutStr ) = pStr;
  1932. return true;
  1933. }
  1934. // We could do this as we go, but we're trying to avoid re-mallocing memory repeatedly in here so process once
  1935. // to find out what the size is.
  1936. int expectedLen = SubstituteVarsRecursive( NULL, pOutSubsts, errorStack, pStr, nTexCompositeCreateFlags, varDefs );
  1937. if ( expectedLen < 0 )
  1938. return false;
  1939. // We don't need to actually write the string, and we shouldn't. If we're just verifying, exit now with success.
  1940. if ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) != 0 )
  1941. return true;
  1942. CUtlString& outStr = ( *pOutStr );
  1943. outStr.SetLength( expectedLen ); // SetLength does +1 to the length for us.
  1944. int finalLen = SubstituteVarsRecursive( outStr.GetForModify(), pOutSubsts, errorStack, pStr, nTexCompositeCreateFlags, varDefs );
  1945. if ( finalLen < 0 )
  1946. return false;
  1947. // Otherwise things have gone horribly wrong.
  1948. Assert( outStr.Length() == expectedLen );
  1949. Assert( expectedLen == finalLen );
  1950. // Success!
  1951. return true;
  1952. }
  1953. // ------------------------------------------------------------------------------------------------
  1954. bool ResolveAllVariablesRecursive( CUtlVector< const char* >& errorStack, const VariableDefs_t& varDefs, KeyValues* pKeyValues, uint32 nTexCompositeCreateFlags, CUtlString& tmpStr )
  1955. {
  1956. // hope for the best
  1957. bool success = true;
  1958. FOR_EACH_SUBKEY( pKeyValues, pChild )
  1959. {
  1960. if ( pChild->GetName()[ 0 ] == '$' )
  1961. continue;
  1962. errorStack.AddToTail( pChild->GetName() );
  1963. if ( pChild->GetFirstSubKey() )
  1964. {
  1965. if ( !ResolveAllVariablesRecursive( errorStack, varDefs, pChild, nTexCompositeCreateFlags, tmpStr ) )
  1966. success = false;
  1967. }
  1968. else
  1969. {
  1970. int nSubsts = 0;
  1971. if ( !SubstituteVars( &tmpStr, &nSubsts, errorStack, pChild->GetString(), nTexCompositeCreateFlags, varDefs ) )
  1972. success = false;
  1973. // Did we do any substitutions?
  1974. if ( nSubsts > 0 && ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) == 0 ) )
  1975. pChild->SetStringValue( tmpStr );
  1976. }
  1977. errorStack.RemoveMultipleFromTail( 1 );
  1978. }
  1979. return success;
  1980. }
  1981. // ------------------------------------------------------------------------------------------------
  1982. KeyValues* ResolveAllVariables( const char* pRootName, const VariableDefs_t& varDefs, KeyValues* pKeyValues, uint32 nTexCompositeCreateFlags, bool *pInOutAllocdNew )
  1983. {
  1984. KeyValuesAD kvad_onError( ( KeyValues* ) nullptr );
  1985. // Let's just assume first that if we have any vars, we will need to substitute them.
  1986. // But if we're just verifying the template, no need.
  1987. if ( !( *pInOutAllocdNew ) && varDefs.Count() > 0 && ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) == 0 ) )
  1988. {
  1989. pKeyValues = pKeyValues->MakeCopy();
  1990. kvad_onError.Assign( pKeyValues );
  1991. ( *pInOutAllocdNew ) = true;
  1992. }
  1993. CUtlString str;
  1994. CUtlVector< const char* > errorStack;
  1995. errorStack.AddToHead( pRootName );
  1996. if ( !ResolveAllVariablesRecursive( errorStack, varDefs, pKeyValues, nTexCompositeCreateFlags, str ) )
  1997. return NULL;
  1998. kvad_onError.Assign( NULL );
  1999. return pKeyValues;
  2000. }
  2001. // ------------------------------------------------------------------------------------------------
  2002. // Perform all template expansion and variable substitution here. What should be output
  2003. // should look like v1.0 paintkits without templates or variables. Return NULL
  2004. // if var substitution fails or if we can't resolve a template or something
  2005. // (after outputting a meaningful error message, of course).
  2006. KeyValues* ParseTopLevelIntoKV( const char* pRootName, KeyValues* pValues, uint32 nTexCompositeCreateFlags, bool *pOutAllocdNew )
  2007. {
  2008. Assert( pRootName != NULL );
  2009. Assert( pOutAllocdNew != NULL );
  2010. if ( !pValues )
  2011. return NULL;
  2012. bool bRequiresCleanup = false;
  2013. KeyValues* pExpandedKV = NULL;
  2014. KeyValuesAD autoCleanup_pExpandedKV( pExpandedKV );
  2015. VariableDefs_t varDefs;
  2016. pExpandedKV = ResolveTemplate( pRootName, pValues, nTexCompositeCreateFlags, &bRequiresCleanup );
  2017. if ( pExpandedKV == NULL )
  2018. return NULL;
  2019. if ( bRequiresCleanup )
  2020. {
  2021. Assert( autoCleanup_pExpandedKV == nullptr || autoCleanup_pExpandedKV == pExpandedKV );
  2022. autoCleanup_pExpandedKV.Assign( pExpandedKV );
  2023. }
  2024. pExpandedKV = ExtractVariableDefinitions( &varDefs, pRootName, pExpandedKV );
  2025. if ( pExpandedKV == NULL )
  2026. return NULL;
  2027. // Only resolve the variables if we're instantiating. During verification time, we'll
  2028. // just check that the keys are sensible and we can skip this.
  2029. pExpandedKV = ResolveAllVariables( pRootName, varDefs, pExpandedKV, nTexCompositeCreateFlags, &bRequiresCleanup);
  2030. if ( pExpandedKV == NULL )
  2031. return NULL;
  2032. Assert( bRequiresCleanup || varDefs.Count() == 0 || ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) != 0 ) );
  2033. varDefs.RemoveAll(); // These won't be valid after we cleanup the tree to remove variable definitions.
  2034. if ( bRequiresCleanup )
  2035. {
  2036. KeyValues* pChild = pExpandedKV->GetFirstSubKey();
  2037. while ( pChild )
  2038. {
  2039. const char* pChildName = pChild->GetName();
  2040. if ( pChildName[ 0 ] == '$' )
  2041. {
  2042. KeyValues* pNext = pChild->GetNextKey();
  2043. pExpandedKV->RemoveSubKey( pChild );
  2044. pChild->deleteThis();
  2045. pChild = pNext;
  2046. }
  2047. else
  2048. pChild = pChild->GetNextKey();
  2049. }
  2050. }
  2051. // We don't need to clean up the KeyValues we created, so clear the AD.
  2052. autoCleanup_pExpandedKV.Assign( NULL );
  2053. ( *pOutAllocdNew ) = bRequiresCleanup;
  2054. return pExpandedKV;
  2055. }
  2056. // ------------------------------------------------------------------------------------------------
  2057. bool HasTemplateOrVariables( const char** ppOutTemplateName, KeyValues* pKV)
  2058. {
  2059. Assert( ppOutTemplateName );
  2060. bool retVal = false;
  2061. ( *ppOutTemplateName ) = NULL;
  2062. FOR_EACH_SUBKEY( pKV, pChild )
  2063. {
  2064. const char* pName = pChild->GetName();
  2065. if ( V_stricmp( pName, "implements" ) == 0 )
  2066. {
  2067. ( *ppOutTemplateName ) = pChild->GetString();
  2068. retVal = true;
  2069. }
  2070. if ( pName[ 0 ] == '$' )
  2071. retVal = true;
  2072. }
  2073. return retVal;
  2074. }
  2075. // ------------------------------------------------------------------------------------------------
  2076. CTextureCompositor* CreateTextureCompositor( int _w, int _h, const char* pCompositeName, int nTeamNum, uint64 nRandomSeed, KeyValues* _stageDesc, uint32 nTexCompositeCreateFlags )
  2077. {
  2078. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  2079. #ifdef STAGING_ONLY
  2080. if ( r_texcomp_dump.GetInt() == 3 || r_texcomp_dump.GetInt() == 4 )
  2081. {
  2082. // Skip compression because it breaks saving render targets out
  2083. // Also don't pollute the cache (or use it)
  2084. nTexCompositeCreateFlags |= ( TEX_COMPOSITE_CREATE_FLAGS_NO_COMPRESSION | TEX_COMPOSITE_CREATE_FLAGS_FORCE );
  2085. }
  2086. #endif
  2087. CUtlVector< CTCStage* > vecStage;
  2088. CUtlVector< KeyValues* > kvs;
  2089. KeyValuesAD kvAutoCleanup( (KeyValues*) nullptr );
  2090. bool bRequiresCleanup = false;
  2091. if ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_LOG_NODES_ONLY ) != 0 )
  2092. {
  2093. DevMsg( 0, "%s\n{\n", pCompositeName );
  2094. KeyValuesDumpAsDevMsg( _stageDesc, 1, 0 );
  2095. DevMsg( 0, "}\n" );
  2096. }
  2097. _stageDesc = ParseTopLevelIntoKV( pCompositeName, _stageDesc, nTexCompositeCreateFlags, &bRequiresCleanup );
  2098. if ( !_stageDesc )
  2099. {
  2100. if ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_LOG_NODES_ONLY ) != 0 )
  2101. Msg( "ERROR[%s]: Failed to create compositor, errors above.\n", pCompositeName );
  2102. return NULL;
  2103. }
  2104. // Set ourselves up for future cleanup.
  2105. if ( bRequiresCleanup )
  2106. kvAutoCleanup.Assign( _stageDesc );
  2107. if ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_LOG_NODES_ONLY )
  2108. {
  2109. if ( bRequiresCleanup )
  2110. {
  2111. DevMsg( 0, "With expansion:\n%s\n{\n", pCompositeName );
  2112. KeyValuesDumpAsDevMsg( _stageDesc, 1, 0 );
  2113. DevMsg( 0, "}\n" );
  2114. }
  2115. return NULL;
  2116. }
  2117. const char* pTemplateName = NULL;
  2118. // If we're just doing a template verification, and we still have keys or values that look like template stuff, bail out now.
  2119. if ( HasTemplateOrVariables( &pTemplateName, _stageDesc ) && ( ( nTexCompositeCreateFlags & TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY ) != 0 ) )
  2120. {
  2121. CTextureCompositor* pComp = new CTextureCompositor( _w, _h, nTeamNum, pCompositeName, nRandomSeed, nTexCompositeCreateFlags );
  2122. if ( pTemplateName )
  2123. pComp->SetTemplate( pTemplateName );
  2124. return pComp;
  2125. }
  2126. KeyValues* kv = _stageDesc->GetFirstTrueSubKey();
  2127. if ( !kv )
  2128. return NULL;
  2129. kvs.AddToTail( kv );
  2130. if ( !ParseNodes( &vecStage, kvs, nTexCompositeCreateFlags ) )
  2131. {
  2132. FOR_EACH_VEC( vecStage, i )
  2133. {
  2134. SafeRelease( &vecStage[ i ] );
  2135. }
  2136. return NULL;
  2137. }
  2138. // Should only get 1 here.
  2139. Assert( vecStage.Count() == 1 );
  2140. CTCStage* rootStage = vecStage[ 0 ];
  2141. // Need to add a copy as the new root.
  2142. CTCStage* copyStage = new CTCCopyStage;
  2143. copyStage->SetFirstChild( rootStage );
  2144. rootStage = copyStage;
  2145. CTextureCompositor* texCompositor = new CTextureCompositor( _w, _h, nTeamNum, pCompositeName, nRandomSeed, nTexCompositeCreateFlags );
  2146. if ( pTemplateName )
  2147. texCompositor->SetTemplate( pTemplateName );
  2148. texCompositor->SetRootStage( rootStage );
  2149. SafeRelease( &rootStage );
  2150. return texCompositor;
  2151. }
  2152. // ------------------------------------------------------------------------------------------------
  2153. CTextureCompositorTemplate* CTextureCompositorTemplate::Create( const char* pName, KeyValues* pTmplDesc )
  2154. {
  2155. if ( !pName || !pTmplDesc )
  2156. return NULL;
  2157. CTextureCompositor* texCompositor = CreateTextureCompositor( 1, 1, pName, 2, 0, pTmplDesc, TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY | TEX_COMPOSITE_CREATE_FLAGS_VERIFY_TEMPLATE_ONLY );
  2158. if ( texCompositor )
  2159. {
  2160. CTextureCompositorTemplate* pTemplate = new CTextureCompositorTemplate( pName, pTmplDesc );
  2161. if ( texCompositor->UsesTemplate() )
  2162. {
  2163. pTemplate->SetImplementsName( texCompositor->GetTemplateName() );
  2164. }
  2165. // Bump then release the ref.
  2166. texCompositor->AddRef();
  2167. texCompositor->Release();
  2168. return pTemplate;
  2169. }
  2170. return NULL;
  2171. }
  2172. // ------------------------------------------------------------------------------------------------
  2173. CTextureCompositorTemplate::~CTextureCompositorTemplate()
  2174. {
  2175. // We don't own the KV we were created with--don't delete it.
  2176. }
  2177. // ------------------------------------------------------------------------------------------------
  2178. bool CTextureCompositorTemplate::ResolveDependencies() const
  2179. {
  2180. // If we don't reference another template, then our verification was validated at construction
  2181. // time.
  2182. if ( m_ImplementsName.IsEmpty() )
  2183. return true;
  2184. CTextureCompositorTemplate* pImplementsTmpl = TextureManager()->FindTextureCompositorTemplate( m_ImplementsName );
  2185. // If we couldn't find our child, then we are not okay.
  2186. if ( pImplementsTmpl == NULL )
  2187. {
  2188. Warning( "ERROR[paintkit_template %s]: Couldn't find template '%s' which we claim to implement.\n", (const char*) m_Name, (const char*)m_ImplementsName );
  2189. return false;
  2190. }
  2191. return true;
  2192. }
  2193. // ------------------------------------------------------------------------------------------------
  2194. bool CTextureCompositorTemplate::HasDependencyCycles()
  2195. {
  2196. // Uses Floyd's algorithm to determine if there's a cycle.
  2197. TM_ZONE_DEFAULT( TELEMETRY_LEVEL1 );
  2198. if ( HasCycle( this ) )
  2199. {
  2200. // Print the cycle. This also marks the nodes as having been tested for cycles.
  2201. PrintMinimumCycle( this );
  2202. return true;
  2203. }
  2204. else
  2205. {
  2206. // Mark everything in this lineage as having been tested for cycles.
  2207. CTextureCompositorTemplate* pTmpl = this;
  2208. while ( pTmpl != NULL )
  2209. {
  2210. if ( pTmpl->HasCheckedForCycles() )
  2211. break;
  2212. pTmpl->SetCheckedForCycles( true );
  2213. pTmpl = Advance( pTmpl, 1 );
  2214. }
  2215. }
  2216. return false;
  2217. }
  2218. // ------------------------------------------------------------------------------------------------
  2219. // ------------------------------------------------------------------------------------------------
  2220. // ------------------------------------------------------------------------------------------------
  2221. void ComputeTextureMatrixFromRectangle( VMatrix* pOutMat, const Vector2D& bl, const Vector2D& tl, const Vector2D& tr )
  2222. {
  2223. Assert( pOutMat != NULL );
  2224. Vector2D leftEdge = bl - tl;
  2225. Vector2D topEdge = tr - tl;
  2226. Vector2D topEdgePerpLeft( -topEdge.y, topEdge.x );
  2227. float magLeftEdge = leftEdge.Length();
  2228. float magTopEdge = topEdge.Length();
  2229. float xScalar = ( topEdgePerpLeft.Dot( leftEdge ) > 0 ) ? 1 : -1;
  2230. // Simplification of acos( ( A . L ) / ( mag( A ) * mag( L ) )
  2231. // Because A is ( 0, 1), which means A . L is just L.y
  2232. // and mag( A ) * mag( L ) is just mag( L )
  2233. float rotationD = RAD2DEG( acos( leftEdge.y / magLeftEdge ) )
  2234. * ( leftEdge.x < 0 ? 1 : -1 );
  2235. VMatrix tmpMat;
  2236. tmpMat.Identity();
  2237. MatrixTranslate( tmpMat, Vector( tl.x, tl.y, 0 ) );
  2238. MatrixRotate( tmpMat, Vector( 0, 0, 1 ), rotationD );
  2239. tmpMat = tmpMat.Scale( Vector( xScalar * magTopEdge, magLeftEdge, 1.0f ) );
  2240. MatrixInverseGeneral( tmpMat, *pOutMat );
  2241. // Copy W into Z because this is a 2-D matrix.
  2242. ( *pOutMat )[ 0 ][ 2 ] = ( *pOutMat )[ 0 ][ 3 ];
  2243. ( *pOutMat )[ 1 ][ 2 ] = ( *pOutMat )[ 1 ][ 3 ];
  2244. ( *pOutMat )[ 2 ][ 2 ] = 1.0f;
  2245. }
  2246. // ------------------------------------------------------------------------------------------------
  2247. // ------------------------------------------------------------------------------------------------
  2248. // ------------------------------------------------------------------------------------------------
  2249. CTextureCompositorTemplate* Advance( CTextureCompositorTemplate* pTmpl, int nSteps )
  2250. {
  2251. Assert( pTmpl != NULL );
  2252. for ( int i = 0; i < nSteps; ++i )
  2253. {
  2254. if ( pTmpl->ImplementsTemplate() )
  2255. {
  2256. pTmpl = TextureManager()->FindTextureCompositorTemplate( pTmpl->GetImplementsName() );
  2257. }
  2258. else
  2259. return NULL;
  2260. }
  2261. return pTmpl;
  2262. }
  2263. // ------------------------------------------------------------------------------------------------
  2264. bool HasCycle( CTextureCompositorTemplate* pStartTempl )
  2265. {
  2266. Assert( pStartTempl != NULL );
  2267. CTextureCompositorTemplate* pTortoise = pStartTempl;
  2268. CTextureCompositorTemplate* pHare = Advance( pStartTempl, 1 );
  2269. while ( pHare != NULL )
  2270. {
  2271. Assert( pTortoise != NULL ); // pTortoise should never be NULL unless pHare already is.
  2272. if ( pTortoise == pHare )
  2273. return true;
  2274. // There may still actually be a cycle here, but we've already reported it if so,
  2275. // so go ahead and bail out and say "no cycle found."
  2276. if ( pTortoise->HasCheckedForCycles() || pHare->HasCheckedForCycles() )
  2277. return false;
  2278. pTortoise = Advance( pTortoise, 1 );
  2279. pHare = Advance( pHare, 1 );
  2280. }
  2281. return false;
  2282. }
  2283. // ------------------------------------------------------------------------------------------------
  2284. void PrintMinimumCycle( CTextureCompositorTemplate* pTmpl )
  2285. {
  2286. TM_ZONE_DEFAULT( TELEMETRY_LEVEL1 );
  2287. const char* pFirstNodeName = pTmpl->GetName();
  2288. // Also mark the nodes as having been cycle-tested to save execution of retesting the same templates.
  2289. // Finding a minimum cycle is O( n log n ) using a map, but we only do this when there's an error.
  2290. CUtlMap< CTextureCompositorTemplate*, int > cycles( DefLessFunc( CTextureCompositorTemplate* ) );
  2291. CUtlLinkedList< const char* > cycleBuilder;
  2292. while ( pTmpl != NULL)
  2293. {
  2294. // Add before we bail so that the first looping element is in the list twice.
  2295. cycleBuilder.AddToTail( pTmpl->GetName() );
  2296. if ( cycles.IsValidIndex( cycles.Find( pTmpl ) ) )
  2297. break;
  2298. pTmpl->SetCheckedForCycles( true );
  2299. cycles.Insert( pTmpl );
  2300. pTmpl = Advance( pTmpl, 1 );
  2301. }
  2302. // If this hits, we didn't actually have a cycle. What?
  2303. Assert( pTmpl );
  2304. Warning( "ERROR[paintkit_template %s]: Detected cycle in paintkit template dependency chain: ", pFirstNodeName );
  2305. FOR_EACH_LL( cycleBuilder, i )
  2306. {
  2307. Warning( "%s -> ", cycleBuilder[ i ] );
  2308. }
  2309. Warning( "...\n" );
  2310. }