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.

545 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Controls the pose parameters of a model
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "point_posecontroller.h"
  8. #ifndef CLIENT_DLL
  9. //-----------------------------------------------------------------------------
  10. // SERVER CLASS
  11. //-----------------------------------------------------------------------------
  12. #include "baseanimating.h"
  13. #include "props.h"
  14. #define MAX_POSE_INTERPOLATION_TIME 10.0f
  15. #define MAX_POSE_CYCLE_FREQUENCY 10.0f
  16. #define MAX_POSE_FMOD_RATE 10.0f
  17. #define MAX_POSE_FMOD_AMPLITUDE 10.0f
  18. LINK_ENTITY_TO_CLASS( point_posecontroller, CPoseController );
  19. BEGIN_DATADESC( CPoseController )
  20. DEFINE_AUTO_ARRAY( m_hProps, FIELD_EHANDLE ),
  21. DEFINE_AUTO_ARRAY( m_chPoseIndex, FIELD_CHARACTER ),
  22. DEFINE_FIELD( m_bDisablePropLookup, FIELD_BOOLEAN ),
  23. DEFINE_FIELD( m_bPoseValueParity, FIELD_BOOLEAN ),
  24. // Keys
  25. DEFINE_KEYFIELD( m_iszPropName, FIELD_STRING, "PropName" ),
  26. DEFINE_KEYFIELD( m_iszPoseParameterName, FIELD_STRING, "PoseParameterName" ),
  27. DEFINE_KEYFIELD( m_fPoseValue, FIELD_FLOAT, "PoseValue" ),
  28. DEFINE_KEYFIELD( m_fInterpolationTime, FIELD_FLOAT, "InterpolationTime" ),
  29. DEFINE_KEYFIELD( m_bInterpolationWrap, FIELD_BOOLEAN, "InterpolationWrap" ),
  30. DEFINE_KEYFIELD( m_fCycleFrequency, FIELD_FLOAT, "CycleFrequency" ),
  31. DEFINE_KEYFIELD( m_nFModType, FIELD_INTEGER, "FModType" ),
  32. DEFINE_KEYFIELD( m_fFModTimeOffset, FIELD_FLOAT, "FModTimeOffset" ),
  33. DEFINE_KEYFIELD( m_fFModRate, FIELD_FLOAT, "FModRate" ),
  34. DEFINE_KEYFIELD( m_fFModAmplitude, FIELD_FLOAT, "FModAmplitude" ),
  35. // Functions
  36. DEFINE_FUNCTION( Think ),
  37. // Inputs
  38. DEFINE_INPUTFUNC( FIELD_STRING, "SetPoseParameterName", InputSetPoseParameterName ),
  39. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPoseValue", InputSetPoseValue ),
  40. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetInterpolationTime", InputSetInterpolationTime ),
  41. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetCycleFrequency", InputSetCycleFrequency ),
  42. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFModType", InputSetFModType ),
  43. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFModTimeOffset", InputSetFModTimeOffset ),
  44. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFModRate", InputSetFModRate ),
  45. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFModAmplitude", InputSetFModAmplitude ),
  46. DEFINE_INPUTFUNC( FIELD_FLOAT, "RandomizeFMod", InputRandomizeFMod ),
  47. DEFINE_INPUTFUNC( FIELD_VOID, "GetFMod", InputGetFMod ),
  48. END_DATADESC()
  49. IMPLEMENT_SERVERCLASS_ST(CPoseController, DT_PoseController)
  50. SendPropArray3( SENDINFO_ARRAY3(m_hProps), SendPropEHandle( SENDINFO_ARRAY(m_hProps) ) ),
  51. SendPropArray3( SENDINFO_ARRAY3(m_chPoseIndex), SendPropInt( SENDINFO_ARRAY(m_chPoseIndex), 5, SPROP_UNSIGNED ) ), // bits sent must be enough to represent MAXSTUDIOPOSEPARAM
  52. SendPropBool( SENDINFO(m_bPoseValueParity) ),
  53. SendPropFloat( SENDINFO(m_fPoseValue), 11, 0, 0.0f, 1.0f ),
  54. SendPropFloat( SENDINFO(m_fInterpolationTime), 11, 0, 0.0f, MAX_POSE_INTERPOLATION_TIME ),
  55. SendPropBool( SENDINFO(m_bInterpolationWrap) ),
  56. SendPropFloat( SENDINFO(m_fCycleFrequency), 11, 0, -MAX_POSE_CYCLE_FREQUENCY, MAX_POSE_CYCLE_FREQUENCY ),
  57. SendPropInt( SENDINFO(m_nFModType), 3, SPROP_UNSIGNED ),
  58. SendPropFloat( SENDINFO(m_fFModTimeOffset), 11, 0, -1.0f, 1.0f ),
  59. SendPropFloat( SENDINFO(m_fFModRate), 11, 0, -MAX_POSE_FMOD_RATE, MAX_POSE_FMOD_RATE ),
  60. SendPropFloat( SENDINFO(m_fFModAmplitude), 11, 0, 0.0f, MAX_POSE_FMOD_AMPLITUDE ),
  61. END_SEND_TABLE()
  62. void CPoseController::Spawn( void )
  63. {
  64. BaseClass::Spawn();
  65. // Talk to the client class when data changes
  66. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  67. // Think to refresh the list of models
  68. SetThink( &CPoseController::Think );
  69. SetNextThink( gpGlobals->curtime + 1.0 );
  70. }
  71. void CPoseController::Think( void )
  72. {
  73. if ( !m_bDisablePropLookup )
  74. {
  75. // Refresh the list of models
  76. BuildPropList();
  77. SetCurrentPose( m_fPoseValue );
  78. m_bDisablePropLookup = true;
  79. SetNextThink( gpGlobals->curtime + 1.0 );
  80. }
  81. }
  82. void CPoseController::BuildPropList( void )
  83. {
  84. int iPropNum = 0;
  85. CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, m_iszPropName );
  86. while ( pEnt && iPropNum < MAX_POSE_CONTROLLED_PROPS )
  87. {
  88. CBaseAnimating *pProp = dynamic_cast<CBaseAnimating*>( pEnt );
  89. if ( pProp )
  90. {
  91. CDynamicProp *pDynamicProp = dynamic_cast<CDynamicProp*>( pProp );
  92. if ( pDynamicProp )
  93. pDynamicProp->PropSetSequence( 0 );
  94. if ( m_hProps[ iPropNum ] != pProp )
  95. {
  96. // Only set new handles (to avoid network spam)
  97. m_hProps.Set( iPropNum, pProp );
  98. }
  99. // Update the pose parameter index
  100. SetPoseIndex( iPropNum, pProp->LookupPoseParameter( m_iszPoseParameterName.ToCStr() ) );
  101. ++iPropNum;
  102. }
  103. // Get the next entity with specified targetname
  104. pEnt = gEntList.FindEntityByName( pEnt, m_iszPropName );
  105. }
  106. // Nullify the remaining handles
  107. while ( iPropNum < MAX_POSE_CONTROLLED_PROPS )
  108. {
  109. if ( m_hProps[ iPropNum ] != NULL )
  110. m_hProps.Set( iPropNum, NULL );
  111. ++iPropNum;
  112. }
  113. SetNextThink( gpGlobals->curtime + 1.0 );
  114. }
  115. void CPoseController::BuildPoseIndexList( void )
  116. {
  117. for ( int iPropNum = 0; iPropNum < MAX_POSE_CONTROLLED_PROPS; ++iPropNum )
  118. {
  119. CBaseAnimating *pProp = dynamic_cast<CBaseAnimating*>( m_hProps[ iPropNum ].Get() );
  120. if ( pProp )
  121. {
  122. // Update the pose parameter index
  123. SetPoseIndex( iPropNum, pProp->LookupPoseParameter( m_iszPoseParameterName.ToCStr() ) );
  124. }
  125. }
  126. }
  127. void CPoseController::SetPoseIndex( int i, int iValue )
  128. {
  129. if ( iValue == -1 )
  130. {
  131. // Using this as invalid lets us network less bits
  132. iValue = MAXSTUDIOPOSEPARAM;
  133. }
  134. if ( m_chPoseIndex[ i ] != iValue )
  135. {
  136. // Only set a new index (to avoid network spam)
  137. m_chPoseIndex.Set( i, iValue );
  138. }
  139. }
  140. float CPoseController::GetPoseValue( void )
  141. {
  142. return m_fPoseValue;
  143. }
  144. void CPoseController::SetProp( CBaseAnimating *pProp )
  145. {
  146. // Control a prop directly by pointer
  147. if ( m_hProps[ 0 ] != pProp )
  148. {
  149. // Only set new handles (to avoid network spam)
  150. m_hProps.Set( 0, pProp );
  151. }
  152. // Update the pose parameter index
  153. SetPoseIndex( 0, pProp->LookupPoseParameter( m_iszPoseParameterName.ToCStr() ) );
  154. // Nullify the remaining handles
  155. for ( int iPropNum = 1; iPropNum < MAX_POSE_CONTROLLED_PROPS; ++iPropNum )
  156. {
  157. if ( m_hProps[ iPropNum ] != NULL )
  158. m_hProps.Set( iPropNum, NULL );
  159. }
  160. m_bDisablePropLookup = false;
  161. }
  162. void CPoseController::SetPropName( const char *pName )
  163. {
  164. m_iszPropName = MAKE_STRING( pName );
  165. BuildPropList();
  166. }
  167. void CPoseController::SetPoseParameterName( const char *pName )
  168. {
  169. m_iszPoseParameterName = MAKE_STRING( pName );
  170. BuildPoseIndexList();
  171. }
  172. void CPoseController::SetPoseValue( float fValue )
  173. {
  174. m_fPoseValue = clamp( fValue, 0.0f, 1.0f );
  175. // Force the client to set the current pose
  176. m_bPoseValueParity = !m_bPoseValueParity;
  177. SetCurrentPose( m_fPoseValue );
  178. }
  179. void CPoseController::SetInterpolationTime( float fValue )
  180. {
  181. m_fInterpolationTime = clamp( fValue, 0.0f, MAX_POSE_INTERPOLATION_TIME );
  182. }
  183. void CPoseController::SetInterpolationWrap( bool bWrap )
  184. {
  185. m_bInterpolationWrap = bWrap;
  186. }
  187. void CPoseController::SetCycleFrequency( float fValue )
  188. {
  189. m_fCycleFrequency = clamp( fValue, -MAX_POSE_CYCLE_FREQUENCY, MAX_POSE_CYCLE_FREQUENCY );
  190. }
  191. void CPoseController::SetFModType( int nType )
  192. {
  193. if ( nType < 0 || nType >= POSECONTROLLER_FMODTYPE_TOTAL )
  194. return;
  195. m_nFModType = static_cast<PoseController_FModType_t>(nType);
  196. }
  197. void CPoseController::SetFModTimeOffset( float fValue )
  198. {
  199. m_fFModTimeOffset = clamp( fValue, -1.0f, 1.0f );
  200. }
  201. void CPoseController::SetFModRate( float fValue )
  202. {
  203. m_fFModRate = clamp( fValue, -MAX_POSE_FMOD_RATE, MAX_POSE_FMOD_RATE );
  204. }
  205. void CPoseController::SetFModAmplitude( float fValue )
  206. {
  207. m_fFModAmplitude = clamp( fValue, 0.0f, MAX_POSE_FMOD_AMPLITUDE );
  208. }
  209. void CPoseController::RandomizeFMod( float fExtremeness )
  210. {
  211. fExtremeness = clamp( fExtremeness, 0.0f, 1.0f );
  212. SetFModType( RandomInt( 1, POSECONTROLLER_FMODTYPE_TOTAL - 1 ) );
  213. SetFModTimeOffset( RandomFloat( -1.0, 1.0f ) );
  214. SetFModRate( RandomFloat( fExtremeness * -MAX_POSE_FMOD_RATE, fExtremeness * MAX_POSE_FMOD_RATE ) );
  215. SetFModAmplitude( RandomFloat( 0.0f, fExtremeness * MAX_POSE_FMOD_AMPLITUDE ) );
  216. }
  217. void CPoseController::InputSetPoseParameterName( inputdata_t &inputdata )
  218. {
  219. SetPoseParameterName( inputdata.value.String() );
  220. }
  221. void CPoseController::InputSetPoseValue( inputdata_t &inputdata )
  222. {
  223. SetPoseValue( inputdata.value.Float() );
  224. }
  225. void CPoseController::InputSetInterpolationTime( inputdata_t &inputdata )
  226. {
  227. SetInterpolationTime( inputdata.value.Float() );
  228. }
  229. void CPoseController::InputSetCycleFrequency( inputdata_t &inputdata )
  230. {
  231. SetCycleFrequency( inputdata.value.Float() );
  232. }
  233. void CPoseController::InputSetFModType( inputdata_t &inputdata )
  234. {
  235. SetFModType( inputdata.value.Int() );
  236. }
  237. void CPoseController::InputSetFModTimeOffset( inputdata_t &inputdata )
  238. {
  239. SetFModTimeOffset( inputdata.value.Float() );
  240. }
  241. void CPoseController::InputSetFModRate( inputdata_t &inputdata )
  242. {
  243. SetFModRate( inputdata.value.Float() );
  244. }
  245. void CPoseController::InputSetFModAmplitude( inputdata_t &inputdata )
  246. {
  247. SetFModAmplitude( inputdata.value.Float() );
  248. }
  249. void CPoseController::InputRandomizeFMod( inputdata_t &inputdata )
  250. {
  251. RandomizeFMod( inputdata.value.Float() );
  252. }
  253. void CPoseController::InputGetFMod( inputdata_t &inputdata )
  254. {
  255. DevMsg( "FMod values for pose controller %s\nTYPE: %i\nTIME OFFSET: %f\nRATE: %f\nAMPLITUDE: %f\n",
  256. STRING( GetEntityName() ),
  257. m_nFModType.Get(),
  258. m_fFModTimeOffset.Get(),
  259. m_fFModRate.Get(),
  260. m_fFModAmplitude.Get() );
  261. }
  262. #else //#ifndef CLIENT_DLL
  263. //-----------------------------------------------------------------------------
  264. // CLIENT CLASS
  265. //-----------------------------------------------------------------------------
  266. IMPLEMENT_CLIENTCLASS_DT( C_PoseController, DT_PoseController, CPoseController )
  267. RecvPropArray3( RECVINFO_ARRAY(m_hProps), RecvPropEHandle( RECVINFO(m_hProps[0]) ) ),
  268. RecvPropArray3( RECVINFO_ARRAY(m_chPoseIndex), RecvPropInt( RECVINFO(m_chPoseIndex[0]) ) ),
  269. RecvPropBool( RECVINFO(m_bPoseValueParity) ),
  270. RecvPropFloat( RECVINFO(m_fPoseValue) ),
  271. RecvPropFloat( RECVINFO(m_fInterpolationTime) ),
  272. RecvPropBool( RECVINFO(m_bInterpolationWrap) ),
  273. RecvPropFloat( RECVINFO(m_fCycleFrequency) ),
  274. RecvPropInt( RECVINFO(m_nFModType) ),
  275. RecvPropFloat( RECVINFO(m_fFModTimeOffset) ),
  276. RecvPropFloat( RECVINFO(m_fFModRate) ),
  277. RecvPropFloat( RECVINFO(m_fFModAmplitude) ),
  278. END_RECV_TABLE()
  279. void C_PoseController::Spawn( void )
  280. {
  281. SetThink( &C_PoseController::ClientThink );
  282. SetNextClientThink( CLIENT_THINK_ALWAYS );
  283. m_fCurrentFMod = 0.0f;
  284. m_PoseTransitionValue.Init( 0.0f, 0.0f, 0.0f );
  285. BaseClass::Spawn();
  286. }
  287. void C_PoseController::OnDataChanged( DataUpdateType_t updateType )
  288. {
  289. BaseClass::OnDataChanged( updateType );
  290. if ( updateType == DATA_UPDATE_CREATED )
  291. {
  292. // Start thinking (Baseclass stops it)
  293. SetNextClientThink( CLIENT_THINK_ALWAYS );
  294. m_bOldPoseValueParity = m_bPoseValueParity;
  295. m_fCurrentPoseValue = m_fPoseValue;
  296. SetCurrentPose( m_fCurrentPoseValue );
  297. }
  298. if ( m_bOldPoseValueParity != m_bPoseValueParity )
  299. {
  300. // If the pose value was set directly set the actual pose value
  301. float fClientPoseValue = m_fCurrentPoseValue + m_PoseTransitionValue.Interp( gpGlobals->curtime );
  302. if ( fClientPoseValue < 0.0f )
  303. fClientPoseValue += 1.0f;
  304. else if ( fClientPoseValue > 1.0f )
  305. fClientPoseValue -= 1.0f;
  306. float fInterpForward = fClientPoseValue - m_fPoseValue;
  307. if ( m_bInterpolationWrap )
  308. {
  309. float fInterpBackward = ( fClientPoseValue + ( ( fClientPoseValue < 0.5f ) ? ( 1.0f ) : ( -1.0f ) ) ) - m_fPoseValue;
  310. m_PoseTransitionValue.Init( ( ( fabsf( fInterpForward ) < fabsf( fInterpBackward ) ) ? ( fInterpForward ) : ( fInterpBackward ) ), 0.0f, m_fInterpolationTime );
  311. }
  312. else
  313. {
  314. m_PoseTransitionValue.Init( fInterpForward, 0.0f, m_fInterpolationTime );
  315. }
  316. m_bOldPoseValueParity = m_bPoseValueParity;
  317. m_fCurrentPoseValue = m_fPoseValue;
  318. }
  319. }
  320. void C_PoseController::ClientThink( void )
  321. {
  322. UpdateModulation();
  323. UpdatePoseCycle( m_fCycleFrequency + m_fCurrentFMod );
  324. }
  325. void C_PoseController::UpdateModulation( void )
  326. {
  327. switch ( m_nFModType )
  328. {
  329. case POSECONTROLLER_FMODTYPE_NONE:
  330. {
  331. // No modulation
  332. m_fCurrentFMod = 0.0f;
  333. break;
  334. }
  335. case POSECONTROLLER_FMODTYPE_SINE:
  336. {
  337. float fCycleTime = m_fFModRate * ( gpGlobals->curtime + m_fFModTimeOffset );
  338. m_fCurrentFMod = m_fFModAmplitude * sinf( fCycleTime * ( 2.0f * M_PI ) );
  339. break;
  340. }
  341. case POSECONTROLLER_FMODTYPE_SQUARE:
  342. {
  343. float fCycleTime = fabsf( m_fFModRate * 2.0f * ( gpGlobals->curtime + m_fFModTimeOffset ) );
  344. // Separate the current time into integer and decimal
  345. int iIntegerPortion = static_cast<int>( fCycleTime );
  346. // Find if it's going up or down
  347. if ( ( iIntegerPortion % 2 ) == 0 )
  348. m_fCurrentFMod = m_fFModAmplitude;
  349. else
  350. m_fCurrentFMod = -m_fFModAmplitude;
  351. break;
  352. }
  353. case POSECONTROLLER_FMODTYPE_TRIANGLE:
  354. {
  355. float fCycleTime = fabsf( m_fFModRate * 4.0f * ( gpGlobals->curtime + m_fFModTimeOffset ) );
  356. // Separate the current time into integer and decimal
  357. int iIntegerPortion = static_cast<int>( fCycleTime );
  358. float fDecimalPortion = fCycleTime - static_cast<float>( iIntegerPortion );
  359. // Find if it's going up from 0, down from 1, down from 0, or up from -1
  360. switch ( iIntegerPortion % 4 )
  361. {
  362. case 0:
  363. m_fCurrentFMod = fDecimalPortion * m_fFModAmplitude;
  364. break;
  365. case 1:
  366. m_fCurrentFMod = ( 1.0f - fDecimalPortion ) * m_fFModAmplitude;
  367. break;
  368. case 2:
  369. m_fCurrentFMod = -fDecimalPortion * m_fFModAmplitude;
  370. break;
  371. case 3:
  372. m_fCurrentFMod = ( -1.0f + fDecimalPortion ) * m_fFModAmplitude;
  373. break;
  374. }
  375. break;
  376. }
  377. case POSECONTROLLER_FMODTYPE_SAWTOOTH:
  378. {
  379. float fCycleTime = fabsf( m_fFModRate * 2.0f * ( gpGlobals->curtime + m_fFModTimeOffset ) );
  380. // Separate the current time into integer and decimal
  381. int iIntegerPortion = static_cast<int>( fCycleTime );
  382. float fDecimalPortion = fCycleTime - static_cast<float>( iIntegerPortion );
  383. // Find if it's going up from 0 or up from -1
  384. if ( ( iIntegerPortion % 2 ) == 0 )
  385. m_fCurrentFMod = fDecimalPortion * m_fFModAmplitude;
  386. else
  387. m_fCurrentFMod = ( -1.0f + fDecimalPortion ) * m_fFModAmplitude;
  388. break;
  389. }
  390. case POSECONTROLLER_FMODTYPE_NOISE:
  391. {
  392. // Randomly increase or decrease by the rate
  393. if ( RandomInt( 0, 1 ) == 0 )
  394. m_fCurrentFMod += m_fFModRate * gpGlobals->frametime;
  395. else
  396. m_fCurrentFMod -= m_fFModRate * gpGlobals->frametime;
  397. m_fCurrentFMod = clamp( m_fCurrentFMod, -m_fFModAmplitude, m_fFModAmplitude );
  398. break;
  399. }
  400. }
  401. }
  402. void C_PoseController::UpdatePoseCycle( float fCycleAmount )
  403. {
  404. m_fCurrentPoseValue += fCycleAmount * gpGlobals->frametime;
  405. float fNewPoseValue = m_fCurrentPoseValue + m_PoseTransitionValue.Interp( gpGlobals->curtime );
  406. if ( fNewPoseValue < 0.0f )
  407. fNewPoseValue += 1.0f;
  408. else if ( fNewPoseValue > 1.0f )
  409. fNewPoseValue -= 1.0f;
  410. SetCurrentPose( fNewPoseValue );
  411. }
  412. #define CPoseController C_PoseController
  413. #define CBaseAnimating C_BaseAnimating
  414. #endif //#ifndef CLIENT_DLL
  415. void CPoseController::SetCurrentPose( float fCurrentPoseValue )
  416. {
  417. for ( int iPropNum = 0; iPropNum < MAX_POSE_CONTROLLED_PROPS; ++iPropNum )
  418. {
  419. // Control each model's pose parameter
  420. CBaseAnimating *pProp = dynamic_cast<CBaseAnimating*>( m_hProps[ iPropNum ].Get() );
  421. if ( pProp )
  422. {
  423. float fPoseValueMin;
  424. float fPoseValueMax;
  425. // Map to the pose parameter's range
  426. pProp->GetPoseParameterRange( m_chPoseIndex[ iPropNum ], fPoseValueMin, fPoseValueMax );
  427. pProp->SetPoseParameter( m_chPoseIndex[ iPropNum ], fPoseValueMin + fCurrentPoseValue * ( fPoseValueMax - fPoseValueMin ) );
  428. }
  429. }
  430. }