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.

500 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "GlobalFunctions.h"
  9. #include "fgdlib/HelperInfo.h"
  10. #include "MapAnimator.h"
  11. #include "MapDoc.h"
  12. #include "MapEntity.h"
  13. #include "MapWorld.h"
  14. #include "KeyFrame/KeyFrame.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include <tier0/memdbgon.h>
  17. IMPLEMENT_MAPCLASS( CMapAnimator );
  18. //-----------------------------------------------------------------------------
  19. // Purpose: Factory function. Used for creating a CMapKeyFrame from a set
  20. // of string parameters from the FGD file.
  21. // Input : *pInfo - Pointer to helper info class which gives us information
  22. // about how to create the class.
  23. // Output : Returns a pointer to the class, NULL if an error occurs.
  24. //-----------------------------------------------------------------------------
  25. CMapClass *CMapAnimator::CreateMapAnimator(CHelperInfo *pHelperInfo, CMapEntity *pParent)
  26. {
  27. return(new CMapAnimator);
  28. }
  29. //-----------------------------------------------------------------------------
  30. // Purpose:
  31. //-----------------------------------------------------------------------------
  32. CMapAnimator::CMapAnimator()
  33. {
  34. m_CoordFrame.Identity();
  35. m_bCurrentlyAnimating = false;
  36. m_pCurrentKeyFrame = this;
  37. m_iPositionInterpolator = m_iRotationInterpolator = m_iTimeModifier = 0;
  38. m_nKeysChanged = 0;
  39. }
  40. //-----------------------------------------------------------------------------
  41. // Purpose:
  42. //-----------------------------------------------------------------------------
  43. CMapAnimator::~CMapAnimator()
  44. {
  45. }
  46. //-----------------------------------------------------------------------------
  47. // Purpose:
  48. // Output : CMapClass *
  49. //-----------------------------------------------------------------------------
  50. CMapClass *CMapAnimator::Copy(bool bUpdateDependencies)
  51. {
  52. CMapAnimator *pNew = new CMapAnimator;
  53. pNew->CopyFrom(this, bUpdateDependencies);
  54. return pNew;
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose:
  58. // Input : *pObj -
  59. // Output : CMapClass
  60. //-----------------------------------------------------------------------------
  61. CMapClass *CMapAnimator::CopyFrom(CMapClass *pObj, bool bUpdateDependencies)
  62. {
  63. CMapKeyFrame::CopyFrom(pObj, bUpdateDependencies);
  64. CMapAnimator *pFrom = dynamic_cast<CMapAnimator*>( pObj );
  65. Assert( pFrom != NULL );
  66. memcpy( m_CoordFrame.Base(), pFrom->m_CoordFrame.Base(), sizeof(m_CoordFrame) );
  67. m_bCurrentlyAnimating = false;
  68. m_pCurrentKeyFrame = NULL; // keyframe it's currently at
  69. m_iTimeModifier = pFrom->m_iTimeModifier;
  70. m_iPositionInterpolator = pFrom->m_iPositionInterpolator;
  71. m_iRotationInterpolator = pFrom->m_iRotationInterpolator;
  72. return this;
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Purpose: Returns a coordinate frame to render in, if the entity is animating
  76. // Input : matrix -
  77. // Output : returns true if a new matrix is returned, false if it is invalid
  78. //-----------------------------------------------------------------------------
  79. bool CMapAnimator::GetTransformMatrix( VMatrix& matrix )
  80. {
  81. // are we currently animating?
  82. if ( m_bCurrentlyAnimating )
  83. {
  84. matrix = m_CoordFrame;
  85. return true;
  86. }
  87. return false;
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Purpose: Notifies that the entity this is attached to has had a key change
  91. // Input : key -
  92. // value -
  93. //-----------------------------------------------------------------------------
  94. void CMapAnimator::OnParentKeyChanged( const char* key, const char* value )
  95. {
  96. if ( !stricmp(key, "TimeModifier") )
  97. {
  98. m_iTimeModifier = atoi( value );
  99. }
  100. else if ( !stricmp(key, "PositionInterpolator") )
  101. {
  102. m_iPositionInterpolator = atoi( value );
  103. // HACK: Force everything in the path to update. Better to follow our path and update only it.
  104. UpdateAllDependencies(this);
  105. }
  106. else if ( !stricmp(key, "RotationInterpolator") )
  107. {
  108. m_iRotationInterpolator = atoi( value );
  109. }
  110. m_nKeysChanged++;
  111. CMapKeyFrame::OnParentKeyChanged( key, value );
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose: Gets the current and previous keyframes for a given time.
  115. // Input : time - time into sequence
  116. // pKeyFrame - receives current keyframe pointer
  117. // pPrevKeyFrame - receives previous keyframe pointer
  118. // Output : time remaining after the thing has reached this key
  119. //-----------------------------------------------------------------------------
  120. float CMapAnimator::GetKeyFramesAtTime( float time, CMapKeyFrame *&pKeyFrame, CMapKeyFrame *&pPrevKeyFrame )
  121. {
  122. pKeyFrame = this;
  123. pPrevKeyFrame = this;
  124. float outTime = time;
  125. while ( pKeyFrame )
  126. {
  127. if ( pKeyFrame->MoveTime() > outTime )
  128. {
  129. break;
  130. }
  131. // make sure this anim has enough time
  132. if ( pKeyFrame->MoveTime() < 0.01f )
  133. {
  134. outTime = 0.0f;
  135. break;
  136. }
  137. outTime -= pKeyFrame->MoveTime();
  138. pPrevKeyFrame = pKeyFrame;
  139. pKeyFrame = pKeyFrame->NextKeyFrame();
  140. }
  141. return outTime;
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose: creates a new keyframe at the specified time
  145. // Input : time -
  146. // Output : Returns true on success, false on failure.
  147. //-----------------------------------------------------------------------------
  148. CMapEntity *CMapAnimator::CreateNewKeyFrame( float time )
  149. {
  150. // work out where we are in the animation
  151. CMapKeyFrame *key;
  152. CMapKeyFrame *pPrevKey;
  153. float partialTime = GetKeyFramesAtTime( time, key, pPrevKey );
  154. CMapEntity *pCurrentEnt = dynamic_cast<CMapEntity*>( key->m_pParent );
  155. // check to see if we're direction on a key frame
  156. Vector posOffset( 0, 0, 0 );
  157. if ( partialTime == 0 )
  158. {
  159. // create this new key frame slightly after the current one, and offset
  160. posOffset[0] = 64;
  161. }
  162. // get our orientation and position at this time
  163. Vector vOrigin;
  164. QAngle angles;
  165. Quaternion qAngles;
  166. GetAnimationAtTime( key, pPrevKey, partialTime, vOrigin, qAngles, m_iPositionInterpolator, m_iRotationInterpolator );
  167. QuaternionAngles( qAngles, angles );
  168. // create the new map entity
  169. CMapEntity *pNewEntity = new CMapEntity;
  170. Vector newPos;
  171. VectorAdd( vOrigin, posOffset, newPos );
  172. pNewEntity->SetPlaceholder( TRUE );
  173. pNewEntity->SetOrigin( newPos );
  174. pNewEntity->SetClass( "keyframe_track" );
  175. char buf[128];
  176. sprintf( buf, "%f %f %f", angles[0], angles[1], angles[2] );
  177. pNewEntity->SetKeyValue( "angles", buf );
  178. // link it into the keyframe list
  179. // take over this existing next keyframe pointer
  180. const char *nextKeyName = pCurrentEnt->GetKeyValue( "NextKey" );
  181. if ( nextKeyName )
  182. {
  183. pNewEntity->SetKeyValue( "NextKey", nextKeyName );
  184. }
  185. // create a new unique name for this ent
  186. char newName[128];
  187. const char *oldName = pCurrentEnt->GetKeyValue( "targetname" );
  188. if ( !oldName || oldName[0] == 0 )
  189. oldName = "keyframe";
  190. CMapWorld *pWorld = GetWorldObject( this );
  191. if ( pWorld )
  192. {
  193. pWorld->GenerateNewTargetname( oldName, newName, sizeof( newName ), true, NULL );
  194. pNewEntity->SetKeyValue( "targetname", newName );
  195. // point the current entity at the newly created one
  196. pCurrentEnt->SetKeyValue( "NextKey", newName );
  197. // copy any relevant values
  198. const char *keyValue = pCurrentEnt->GetKeyValue( "parentname" );
  199. if ( keyValue )
  200. pNewEntity->SetKeyValue( "parentname", keyValue );
  201. keyValue = pCurrentEnt->GetKeyValue( "MoveSpeed" );
  202. if ( keyValue )
  203. pNewEntity->SetKeyValue( "MoveSpeed", keyValue );
  204. }
  205. return(pNewEntity);
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose: stops CMapKeyframe from doing it's auto-connect behavior when cloning
  209. // Input : pClone -
  210. //-----------------------------------------------------------------------------
  211. void CMapAnimator::OnClone( CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList )
  212. {
  213. CMapClass::OnClone( pClone, pWorld, OriginalList, NewList );
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Purpose: calculates the position of an animating object at a given time in
  217. // it's animation sequence
  218. // Input : animTime -
  219. // *newOrigin -
  220. // *newAngles -
  221. //-----------------------------------------------------------------------------
  222. void CMapAnimator::GetAnimationAtTime( float animTime, Vector& newOrigin, Quaternion &newAngles )
  223. {
  224. // setup the animation, given the time
  225. // get our new position & orientation
  226. float newTime, totalAnimTime = GetRemainingTime();
  227. animTime /= totalAnimTime;
  228. // don't use time modifier until we work out what we're going to do with it
  229. //Motion_CalculateModifiedTime( animTime, m_iTimeModifier, &newTime );
  230. newTime = animTime;
  231. // find out where we are in the keyframe sequence based on the time
  232. CMapKeyFrame *pPrevKeyFrame;
  233. float posTime = GetKeyFramesAtTime( newTime * totalAnimTime, m_pCurrentKeyFrame, pPrevKeyFrame );
  234. // find the position from that keyframe
  235. GetAnimationAtTime( m_pCurrentKeyFrame, pPrevKeyFrame, posTime, newOrigin, newAngles, m_iPositionInterpolator, m_iRotationInterpolator );
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose: calculates the position of the animating object between two keyframes
  239. // Input : currentKey -
  240. // pPrevKey -
  241. // partialTime -
  242. // newOrigin -
  243. // newAngles -
  244. // posInterpolator -
  245. // rotInterpolator -
  246. //-----------------------------------------------------------------------------
  247. void CMapAnimator::GetAnimationAtTime( CMapKeyFrame *currentKey, CMapKeyFrame *pPrevKey, float partialTime, Vector& newOrigin, Quaternion &newAngles, int posInterpolator, int rotInterpolator )
  248. {
  249. // calculate the proportion of time to be spent on this keyframe
  250. float animTime;
  251. if ( currentKey->MoveTime() < 0.01 )
  252. {
  253. animTime = 1.0f;
  254. }
  255. else
  256. {
  257. animTime = partialTime / currentKey->MoveTime();
  258. }
  259. Assert( animTime >= 0.0f && animTime <= 1.0f );
  260. IPositionInterpolator *pInterp = currentKey->SetupPositionInterpolator( posInterpolator );
  261. // setup interpolation keyframes
  262. Vector keyOrigin;
  263. Quaternion keyAngles;
  264. pPrevKey->GetOrigin( keyOrigin );
  265. pPrevKey->GetQuatAngles( keyAngles );
  266. pInterp->SetKeyPosition( -1, keyOrigin );
  267. Motion_SetKeyAngles ( -1, keyAngles );
  268. currentKey->GetOrigin( keyOrigin );
  269. currentKey->GetQuatAngles( keyAngles );
  270. pInterp->SetKeyPosition( 0, keyOrigin );
  271. Motion_SetKeyAngles ( 0, keyAngles );
  272. currentKey->NextKeyFrame()->GetOrigin( keyOrigin );
  273. currentKey->NextKeyFrame()->GetQuatAngles( keyAngles );
  274. pInterp->SetKeyPosition( 1, keyOrigin );
  275. Motion_SetKeyAngles ( 1, keyAngles );
  276. currentKey->NextKeyFrame()->NextKeyFrame()->GetOrigin( keyOrigin );
  277. currentKey->NextKeyFrame()->NextKeyFrame()->GetQuatAngles( keyAngles );
  278. pInterp->SetKeyPosition( 2, keyOrigin );
  279. Motion_SetKeyAngles ( 2, keyAngles );
  280. // get our new interpolated position
  281. // HACK HACK - Hey Brian, look here!!!!
  282. Vector hackOrigin;
  283. pInterp->InterpolatePosition( animTime, hackOrigin );
  284. newOrigin[0] = hackOrigin[0];
  285. newOrigin[1] = hackOrigin[1];
  286. newOrigin[2] = hackOrigin[2];
  287. Motion_InterpolateRotation( animTime, rotInterpolator, newAngles );
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose: Builds the animation transformation matrix for the entity if it's animating
  291. //-----------------------------------------------------------------------------
  292. void CMapAnimator::UpdateAnimation( float animTime )
  293. {
  294. // only animate if the doc is animating and we're selected
  295. if ( !CMapDoc::GetActiveMapDoc()->IsAnimating() || !m_pParent->IsSelected() )
  296. {
  297. // we're not animating
  298. m_bCurrentlyAnimating = false;
  299. return;
  300. }
  301. m_bCurrentlyAnimating = true;
  302. Vector newOrigin;
  303. Quaternion newAngles;
  304. GetAnimationAtTime( animTime, newOrigin, newAngles );
  305. VMatrix mat, tmpMat;
  306. Vector ourOrigin;
  307. GetOrigin( ourOrigin );
  308. // build us a matrix
  309. // T(newOrigin)R(angle)T(-ourOrigin)
  310. m_CoordFrame.Identity() ;
  311. // transform back to the origin
  312. for ( int i = 0; i < 3; i++ )
  313. {
  314. m_CoordFrame[i][3] = -ourOrigin[i];
  315. }
  316. // Apply interpolated Rotation
  317. mat.Identity();
  318. QuaternionMatrix( newAngles, const_cast< matrix3x4_t & > ( mat.As3x4() ) );
  319. m_CoordFrame = m_CoordFrame * mat;
  320. // transform back to our new position
  321. mat.Identity();
  322. for ( int i = 0; i < 3; i++ )
  323. {
  324. mat[i][3] = newOrigin[i];
  325. }
  326. m_CoordFrame = m_CoordFrame * mat;
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose: Rebuilds the line path between keyframe entities
  330. // samples the interpolator function to get an approximation of the curve
  331. //-----------------------------------------------------------------------------
  332. void CMapAnimator::RebuildPath( void )
  333. {
  334. CMapWorld *pWorld = GetWorldObject( this );
  335. if ( !pWorld )
  336. {
  337. // Sometimes the object isn't linked back into the world yet when RebuildPath()
  338. // is called... we will be get called again when needed, but may cause an incorrect
  339. // (linear-only) path to get drawn temporarily.
  340. return;
  341. }
  342. //
  343. // Build the path forward from the head. Keep a list of nodes we've visited to
  344. // use in detecting circularities.
  345. //
  346. CMapObjectList VisitedList;
  347. CMapKeyFrame *pCurKey = this;
  348. while ( pCurKey != NULL )
  349. {
  350. VisitedList.AddToTail( pCurKey );
  351. //
  352. // Attach ourselves as this keyframe's animator.
  353. //
  354. pCurKey->SetAnimator( this );
  355. //
  356. // Get the entity parent of this keyframe so we can query keyvalues.
  357. //
  358. CMapEntity *pCurEnt = dynamic_cast<CMapEntity *>( pCurKey->GetParent() );
  359. if ( !pCurEnt )
  360. {
  361. return;
  362. }
  363. //
  364. // Find the next keyframe in the path.
  365. //
  366. CMapEntity *pNextEnt = pWorld->FindEntityByName( pCurEnt->GetKeyValue( "NextKey" ) );
  367. CMapKeyFrame *pNextKey = NULL;
  368. if ( pNextEnt )
  369. {
  370. pNextKey = pNextEnt->GetChildOfType( ( CMapKeyFrame * )NULL );
  371. pCurKey->SetNextKeyFrame(pNextKey);
  372. }
  373. else
  374. {
  375. pCurKey->SetNextKeyFrame( NULL );
  376. }
  377. pCurKey = pNextKey;
  378. //
  379. // If we detect a circularity, stop.
  380. //
  381. if ( VisitedList.Find( pCurKey ) != -1 )
  382. {
  383. break;
  384. }
  385. }
  386. //
  387. // Now traverse the path again building the spline points, once again checking
  388. // the visited list for circularities.
  389. //
  390. VisitedList.RemoveAll();
  391. pCurKey = this;
  392. CMapKeyFrame *pPrevKey = this;
  393. while ( pCurKey != NULL )
  394. {
  395. VisitedList.AddToTail( pCurKey );
  396. pCurKey->BuildPathSegment(pPrevKey);
  397. pPrevKey = pCurKey;
  398. pCurKey = pCurKey->m_pNextKeyFrame;
  399. //
  400. // If we detect a circularity, stop.
  401. //
  402. if ( VisitedList.Find( pCurKey ) != -1 )
  403. {
  404. break;
  405. }
  406. }
  407. }