Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2140 lines
78 KiB

  1. //====== Copyright � 1996-2009, Valve Corporation, All rights reserved. =======
  2. //
  3. // Implementation of CDmAnimUtils, a set of animation related utilities
  4. // which work on DmeDag and other DmElement derived objects.
  5. //
  6. //=============================================================================
  7. #include "dmeutils/dmanimutils.h"
  8. #include "movieobjects/dmetransformcontrol.h"
  9. #include "movieobjects/dmegamemodel.h"
  10. #include "movieobjects/dmetimeselection.h"
  11. #include "movieobjects/dmetrack.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //-------------------------------------------------------------------------------------------------
  15. // Create an infinite time selection
  16. //-------------------------------------------------------------------------------------------------
  17. CDmeTimeSelection *CDmAnimUtils::CreateInfiniteTimeSelection()
  18. {
  19. CDmeTimeSelection *pTimeSelection = CreateElement< CDmeTimeSelection >( "AnimUtilsTimeSelection", DMFILEID_INVALID );
  20. pTimeSelection->SetInfinite( 0 );
  21. pTimeSelection->SetInfinite( 1 );
  22. return pTimeSelection;
  23. }
  24. //-------------------------------------------------------------------------------------------------
  25. // Create an absolute time selection with the specified key times
  26. //-------------------------------------------------------------------------------------------------
  27. CDmeTimeSelection *CDmAnimUtils::CreateTimeSelection( DmeTime_t leftFalloff, DmeTime_t leftHold, DmeTime_t rightHold, DmeTime_t rightFalloff )
  28. {
  29. CDmeTimeSelection *pTimeSelection = CreateElement< CDmeTimeSelection >( "AnimUtilsTimeSelection", DMFILEID_INVALID );
  30. pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_LEFT_FALLOFF, leftFalloff );
  31. pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_LEFT_HOLD, leftHold );
  32. pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_RIGHT_HOLD, rightHold );
  33. pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_RIGHT_FALLOFF, rightFalloff );
  34. return pTimeSelection;
  35. }
  36. //-------------------------------------------------------------------------------------------------
  37. // Create a CDmeDag instance with the specified name with the specified position and orientation
  38. //-------------------------------------------------------------------------------------------------
  39. CDmeDag *CDmAnimUtils::CreateDag( char const *pchHandleName, const Vector &position, const Quaternion &orientation, CDmeDag *pParent )
  40. {
  41. DmFileId_t fileId = DMFILEID_INVALID;
  42. if ( pParent )
  43. {
  44. fileId = pParent->GetFileId();
  45. }
  46. // Create the dag element
  47. CDmeDag *pDag = CreateElement< CDmeDag >( pchHandleName, fileId );
  48. if ( pDag )
  49. {
  50. // Add the handle to the scene
  51. if ( pParent )
  52. {
  53. pParent->AddChild( pDag );
  54. }
  55. // Position the handle
  56. pDag->SetAbsPosition( position );
  57. pDag->SetAbsOrientation( orientation );
  58. }
  59. return pDag;
  60. }
  61. //-------------------------------------------------------------------------------------------------
  62. // Get the average position of the provided dag nodes in the specified space
  63. //-------------------------------------------------------------------------------------------------
  64. Vector CDmAnimUtils::GetDagPosition( const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReferenceDag )
  65. {
  66. Vector position = vec3_origin;
  67. Quaternion orientation = quat_identity;
  68. GetDagPositionOrienation( position, orientation, dagList, space, pReferenceDag );
  69. return position;
  70. }
  71. //-------------------------------------------------------------------------------------------------
  72. // Get the average Euler rotation of the provided dag nodes in the specified space
  73. //-------------------------------------------------------------------------------------------------
  74. Vector CDmAnimUtils::GetDagRotation( const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReferenceDag )
  75. {
  76. Vector position = vec3_origin;
  77. Quaternion orientation = quat_identity;
  78. GetDagPositionOrienation( position, orientation, dagList, space, pReferenceDag );
  79. // Convert the quaternion to Euler rotation
  80. RadianEuler eulerRotation( orientation );
  81. Vector rotation = vec3_origin;
  82. rotation.x = RAD2DEG( eulerRotation.x );
  83. rotation.y = RAD2DEG( eulerRotation.y );
  84. rotation.z = RAD2DEG( eulerRotation.z );
  85. return rotation;
  86. }
  87. //-------------------------------------------------------------------------------------------------
  88. // Get the average orientation (quaternion) of the provided dag nodes in the specified space
  89. //-------------------------------------------------------------------------------------------------
  90. Quaternion CDmAnimUtils::GetDagOrientation( const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReferenceDag )
  91. {
  92. Vector position = vec3_origin;
  93. Quaternion orientation = quat_identity;
  94. GetDagPositionOrienation( position, orientation, dagList, space, pReferenceDag );
  95. return orientation;
  96. }
  97. //-------------------------------------------------------------------------------------------------
  98. // Purpose: Get the average position and orientation of the provided dag nodes in the specified
  99. // space
  100. //-------------------------------------------------------------------------------------------------
  101. void CDmAnimUtils::GetDagPositionOrienation( Vector &position, Quaternion &orienation, const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReference )
  102. {
  103. int nDagNodes = dagList.Count();
  104. position = vec3_origin;
  105. orienation = quat_identity;
  106. // Return the origin if there are no nodes provided
  107. if ( nDagNodes < 1 )
  108. return;
  109. // Sum the positions of all of the provided dag nodes
  110. // and add the orientation of each node to the list.
  111. CUtlVector< Quaternion > orientationList;
  112. orientationList.EnsureCapacity( nDagNodes );
  113. float flDagCount = 0;
  114. const CDmeDag *pPrimaryDag = NULL;
  115. Vector positionSum = vec3_origin;
  116. for ( int iNode = 0; iNode < nDagNodes; ++iNode )
  117. {
  118. const CDmeDag *pDagNode = dagList[ iNode ];
  119. if ( pDagNode == NULL )
  120. continue;
  121. // Get the world space position of the dag
  122. matrix3x4_t absTransform;
  123. Vector worldPos;
  124. Quaternion worldOrientation;
  125. pDagNode->GetAbsTransform( absTransform );
  126. MatrixAngles( absTransform, worldOrientation, worldPos );
  127. positionSum += worldPos;
  128. orientationList.AddToTail( worldOrientation );
  129. flDagCount += 1.0f;
  130. pPrimaryDag = pDagNode;
  131. }
  132. // Stop if there were no valid dag nodes
  133. if ( pPrimaryDag == NULL )
  134. return;
  135. // Calculate the average position in world space
  136. Vector positionAvg = positionSum / flDagCount;
  137. // Calculate the average orienation in world space
  138. Quaternion orienationAvg;
  139. QuaternionAverageExponential( orienationAvg, orientationList.Count(), orientationList.Base() );
  140. // Find the dag node whose space the position is to be returned in
  141. const CDmeDag *pSpaceDag = NULL;
  142. switch ( space )
  143. {
  144. case TS_LOCAL_SPACE:
  145. pSpaceDag = pPrimaryDag->GetParent();
  146. break;
  147. case TS_OBJECT_SPACE:
  148. pSpaceDag = pPrimaryDag;
  149. break;
  150. case TS_REFERENCE_SPACE:
  151. pSpaceDag = pReference;
  152. break;
  153. default:
  154. break;
  155. }
  156. // Convert the average world space position to the space of the specified dag node
  157. if ( pSpaceDag )
  158. {
  159. matrix3x4_t worldToLocal;
  160. matrix3x4_t localToWorld;
  161. pSpaceDag->GetAbsTransform( localToWorld );
  162. MatrixInvert( localToWorld, worldToLocal );
  163. VectorTransform( positionAvg, worldToLocal, position );
  164. float rotationAngle;
  165. Vector wsRotationAxis;
  166. Vector lsRotationAxis;
  167. QuaternionAxisAngle( orienationAvg, wsRotationAxis, rotationAngle );
  168. VectorRotate( wsRotationAxis, worldToLocal, lsRotationAxis );
  169. AxisAngleQuaternion( lsRotationAxis, rotationAngle, orienation );
  170. }
  171. else
  172. {
  173. position = positionAvg;
  174. orienation = orienationAvg;
  175. }
  176. }
  177. //-------------------------------------------------------------------------------------------------
  178. // Move the provided dag nodes in the specified space
  179. //-------------------------------------------------------------------------------------------------
  180. void CDmAnimUtils::MoveDagNodes( const Vector &offset, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, TransformSpace_t space, CDmeDag *pReferenceDag )
  181. {
  182. TransformDagNodes( offset, quat_identity, dagList, bRelative, space, pReferenceDag, true, false );
  183. }
  184. //-------------------------------------------------------------------------------------------------
  185. // Move the provided dag nodes in the specified space and apply the operation to the logs
  186. // associated with the dag
  187. //-------------------------------------------------------------------------------------------------
  188. void CDmAnimUtils::MoveDagNodes( const Vector &offset, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, const CDmeTimeSelection *pTimeSelection, bool bOffsetOverTime, TransformSpace_t space, CDmeDag *pReferenceDag )
  189. {
  190. TransformDagNodes( offset, quat_identity, dagList, bRelative, pTimeSelection, bOffsetOverTime, space, pReferenceDag, true, false );
  191. }
  192. //-------------------------------------------------------------------------------------------------
  193. // Rotate the provided dag nodes in the specified space
  194. //-------------------------------------------------------------------------------------------------
  195. void CDmAnimUtils::RotateDagNodes( const Vector &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, TransformSpace_t space, CDmeDag* pReferenceDag )
  196. {
  197. // Rotate the selected dag nodes in the specified space
  198. RadianEuler eulerRotation( DEG2RAD( rotation.x ), DEG2RAD( rotation.y ), DEG2RAD( rotation.z ) );
  199. Quaternion qRotation( eulerRotation );
  200. TransformDagNodes( vec3_origin, qRotation, dagList, bRelative, space, pReferenceDag, false, true );
  201. }
  202. //-------------------------------------------------------------------------------------------------
  203. // Rotate the provided dag nodes in the specified space and apply the operation to the logs
  204. // associated with the dag
  205. //-------------------------------------------------------------------------------------------------
  206. void CDmAnimUtils::RotateDagNodes( const Vector &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, const CDmeTimeSelection *pTimeSelection, bool bOffsetOverTime, TransformSpace_t space, CDmeDag* pReferenceDag )
  207. {
  208. // Rotate the selected dag nodes in the specified space
  209. RadianEuler eulerRotation( DEG2RAD( rotation.x ), DEG2RAD( rotation.y ), DEG2RAD( rotation.z ) );
  210. Quaternion qRotation( eulerRotation );
  211. TransformDagNodes( vec3_origin, qRotation, dagList, bRelative, pTimeSelection, bOffsetOverTime, space, pReferenceDag, false, true );
  212. }
  213. //-------------------------------------------------------------------------------------------------
  214. // Translate an rotate the provided dag nodes in the specified space
  215. //-------------------------------------------------------------------------------------------------
  216. void CDmAnimUtils::TransformDagNodes( const Vector &offset, const Quaternion &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, TransformSpace_t space, CDmeDag* pReferenceDag, bool bPosition, bool bRotation )
  217. {
  218. float rotationAngle;
  219. Vector rotationAxis;
  220. matrix3x4_t orienationMat;
  221. QuaternionAxisAngle( rotation, rotationAxis, rotationAngle );
  222. QuaternionMatrix( rotation, orienationMat );
  223. int nDagNodes = dagList.Count();
  224. for ( int iNode = 0; iNode < nDagNodes; ++iNode )
  225. {
  226. const CDmeDag *pDagNode = dagList[ iNode ];
  227. if ( pDagNode == NULL )
  228. continue;
  229. // The value stored in the dag node's transform is in local space ( space of the parent ),
  230. // we must convert the offset from the space it is provided in to a value in local space.
  231. Vector lsOffset;
  232. Vector lsRotationAxis;
  233. matrix3x4_t lsOrientationMat;
  234. matrix3x4_t objectToLocal;
  235. pDagNode->GetLocalMatrix( objectToLocal );
  236. if ( space == TS_LOCAL_SPACE )
  237. {
  238. // The offset and rotation are provided in local space, nothing needs to be done
  239. lsOffset = offset;
  240. lsRotationAxis = rotationAxis;
  241. lsOrientationMat = orienationMat;
  242. }
  243. else if ( space == TS_OBJECT_SPACE )
  244. {
  245. // The offset and rotation are provided in object space, it is simply transformed
  246. // by the object's transform to get it into space of the object's parent.
  247. VectorTransform( offset, objectToLocal, lsOffset );
  248. VectorRotate( rotationAxis, objectToLocal, lsRotationAxis );
  249. ConcatTransforms( orienationMat, objectToLocal, lsOrientationMat);
  250. }
  251. else
  252. {
  253. // Calculate the offset to be applied in world space
  254. Vector wsOffset = offset;
  255. Vector wsRotationAxis = rotationAxis;
  256. matrix3x4_t wsOrientationMat = orienationMat;
  257. if ( pReferenceDag && ( space == TS_REFERENCE_SPACE ) )
  258. {
  259. matrix3x4_t referenceToWorld;
  260. pReferenceDag->GetAbsTransform( referenceToWorld );
  261. VectorTransform( offset, referenceToWorld, wsOffset );
  262. VectorRotate( rotationAxis, referenceToWorld, wsRotationAxis );
  263. ConcatTransforms( orienationMat, referenceToWorld, wsOrientationMat );
  264. }
  265. // Calculate the offset to be applied to the dag's local space
  266. CDmeDag *pParent = pDagNode->GetParent();
  267. if ( pParent )
  268. {
  269. matrix3x4_t localToWorld;
  270. matrix3x4_t worldToLocal;
  271. pParent->GetAbsTransform( localToWorld );
  272. MatrixInvert( localToWorld, worldToLocal );
  273. VectorRotate( wsRotationAxis, worldToLocal, lsRotationAxis );
  274. MatrixMultiply( wsOrientationMat, worldToLocal, lsOrientationMat );
  275. if ( bRelative )
  276. {
  277. VectorRotate( wsOffset, worldToLocal, lsOffset );
  278. }
  279. else
  280. {
  281. VectorTransform( wsOffset, worldToLocal, lsOffset );
  282. }
  283. }
  284. else
  285. {
  286. lsOffset = wsOffset;
  287. lsRotationAxis = wsRotationAxis;
  288. lsOrientationMat = wsOrientationMat;
  289. }
  290. }
  291. // Now that we have the offset and rotation in local space, they may be applied to the
  292. // local transform. They may be applied as an absolute position and orientation, or a
  293. // relative offset and rotation.
  294. Quaternion lsRotation;
  295. AxisAngleQuaternion( lsRotationAxis, rotationAngle, lsRotation );
  296. Vector lsPosition = lsOffset;
  297. Quaternion lsOrientation = quat_identity;
  298. if ( bRelative )
  299. {
  300. Vector lsOrigin;
  301. MatrixAngles( objectToLocal, lsOrientation, lsOrigin );
  302. QuaternionMult( lsRotation, lsOrientation, lsOrientation );
  303. lsPosition = lsOrigin + lsOffset;
  304. }
  305. else
  306. {
  307. MatrixQuaternion( lsOrientationMat, lsOrientation );
  308. }
  309. CDmeTransform *pTransform = pDagNode->GetTransform();
  310. if ( pTransform )
  311. {
  312. if ( bPosition )
  313. {
  314. pTransform->SetPosition( lsPosition );
  315. }
  316. if ( bRotation )
  317. {
  318. pTransform->SetOrientation( lsOrientation );
  319. }
  320. }
  321. // Find the position and orientation controls for the transform of the dag node.
  322. CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
  323. // Assign the new local space position and orienation to the control
  324. if ( pTransformControl )
  325. {
  326. if ( bPosition )
  327. {
  328. pTransformControl->SetPosition( lsPosition );
  329. }
  330. if ( bRotation)
  331. {
  332. pTransformControl->SetOrientation( lsOrientation );
  333. }
  334. }
  335. }
  336. }
  337. //-------------------------------------------------------------------------------------------------
  338. // Perform both a translation and a rotation of the specified dag nodes and apply the operation to
  339. // the logs associated with the dag
  340. //-------------------------------------------------------------------------------------------------
  341. void CDmAnimUtils::TransformDagNodes( const Vector &offset, const Quaternion &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, const CDmeTimeSelection *pTimeSelection, bool bOffsetOverTime, TransformSpace_t space, CDmeDag* pReferenceDag, bool bPosition, bool bRotation )
  342. {
  343. TransformDagNodes( offset, rotation, dagList, bRelative, space, pReferenceDag, bPosition, bRotation );
  344. if ( pTimeSelection )
  345. {
  346. // Now apply the operation through time
  347. DmeLog_TimeSelection_t logTimeSelection;
  348. logTimeSelection.m_bAttachedMode = pTimeSelection->IsRelative();
  349. logTimeSelection.m_flThreshold = pTimeSelection->GetThreshold();
  350. logTimeSelection.m_nFalloffInterpolatorTypes[ 0 ] = pTimeSelection->GetFalloffInterpolatorType( 0 );
  351. logTimeSelection.m_nFalloffInterpolatorTypes[ 1 ] = pTimeSelection->GetFalloffInterpolatorType( 1 );
  352. logTimeSelection.m_nResampleInterval = pTimeSelection->GetResampleInterval();
  353. logTimeSelection.m_bInfinite[ 0 ] = pTimeSelection->IsInfinite( 0 );
  354. logTimeSelection.m_bInfinite[ 1 ] = pTimeSelection->IsInfinite( 1 );
  355. if ( logTimeSelection.m_bInfinite[ 0 ] )
  356. {
  357. logTimeSelection.m_nTimes[ TS_LEFT_FALLOFF ] = DMETIME_MAXTIME;
  358. logTimeSelection.m_nTimes[ TS_LEFT_HOLD ] = DMETIME_MAXTIME;
  359. }
  360. else
  361. {
  362. logTimeSelection.m_nTimes[ TS_LEFT_FALLOFF ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_LEFT_FALLOFF );
  363. logTimeSelection.m_nTimes[ TS_LEFT_HOLD ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_LEFT_HOLD );
  364. }
  365. if ( logTimeSelection.m_bInfinite[ 1 ] )
  366. {
  367. logTimeSelection.m_nTimes[ TS_RIGHT_FALLOFF ] = DMETIME_MINTIME;
  368. logTimeSelection.m_nTimes[ TS_RIGHT_HOLD ] = DMETIME_MINTIME;
  369. }
  370. else
  371. {
  372. logTimeSelection.m_nTimes[ TS_RIGHT_HOLD ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_RIGHT_HOLD );
  373. logTimeSelection.m_nTimes[ TS_RIGHT_FALLOFF ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_RIGHT_FALLOFF );
  374. }
  375. // Apply the results to the channels of the selected dag nodes
  376. if ( bOffsetOverTime )
  377. {
  378. logTimeSelection.m_TransformWriteMode = TRANSFORM_WRITE_MODE_OFFSET;
  379. logTimeSelection.SetRecordingMode( RECORD_ATTRIBUTESLIDER );
  380. }
  381. OperateDagChannels( dagList, CM_RECORD, logTimeSelection, NULL, NULL );
  382. }
  383. }
  384. //-------------------------------------------------------------------------------------------------
  385. // Set the current position and orientation as the defaults for the specified dag nodes.
  386. //-------------------------------------------------------------------------------------------------
  387. void CDmAnimUtils::SetDagTransformDefaults( const CUtlVector< CDmeDagPtr > &dagList, bool bPosition, bool bOrientation )
  388. {
  389. if ( ( bPosition == false ) && ( bOrientation == false ) )
  390. return;
  391. int nDagNodes = dagList.Count();
  392. for ( int iDagNode = 0; iDagNode < nDagNodes; ++iDagNode )
  393. {
  394. const CDmeDag *pDagNode = dagList[ iDagNode ];
  395. if ( pDagNode == NULL )
  396. continue;
  397. CDmeTransform *pTransform = pDagNode->GetTransform();
  398. if ( pTransform == NULL )
  399. continue;
  400. CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
  401. if ( pTransformControl )
  402. {
  403. if ( bPosition )
  404. {
  405. const Vector &position = pTransform->GetPosition();
  406. pTransformControl->SetDefaultPosition( position );
  407. }
  408. if ( bOrientation )
  409. {
  410. const Quaternion &orientation = pTransform->GetOrientation();
  411. pTransformControl->SetDefaultOrientation( orientation );
  412. }
  413. }
  414. }
  415. }
  416. //-------------------------------------------------------------------------------------------------
  417. // Set all of the controls targeting bones in the animation set to the reference pose
  418. // position and orientation
  419. //-------------------------------------------------------------------------------------------------
  420. void CDmAnimUtils::SetReferencePose( CDmeGameModel *pGameModel, const CUtlVector< CDmeDag* > &dagList )
  421. {
  422. if ( pGameModel == NULL )
  423. return;
  424. int nDagNodes = dagList.Count();
  425. for ( int iDagNode = 0; iDagNode < nDagNodes; ++iDagNode )
  426. {
  427. const CDmeDag *pDagNode = dagList[ iDagNode ];
  428. if ( pDagNode == NULL )
  429. continue;
  430. CDmeTransform *pTransform = pDagNode->GetTransform();
  431. if ( pTransform == NULL )
  432. continue;
  433. // Find the bone corresponding to the control
  434. int boneIndex = pGameModel->FindBone( pTransform );
  435. // Skip the dag if there is no corresponding bone unless it is the root
  436. if ( ( boneIndex < 0 ) && ( pDagNode != pGameModel ) )
  437. continue;
  438. CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
  439. if ( pTransformControl )
  440. {
  441. Vector position = vec3_origin;
  442. pGameModel->GetBoneDefaultPosition( boneIndex, position );
  443. pTransformControl->SetPosition( position );
  444. Quaternion orientation = quat_identity;
  445. pGameModel->GetBoneDefaultOrientation( boneIndex, orientation );
  446. pTransformControl->SetOrientation( orientation );
  447. }
  448. }
  449. }
  450. //-------------------------------------------------------------------------------------------------
  451. // Update the default values for the position and orientation controls of the specified dag so they
  452. // maintain their world space position
  453. //-------------------------------------------------------------------------------------------------
  454. void CDmAnimUtils::UpdateDefaultsForNewParent( CDmeDag *pDagNode, CDmeDag *pParentDag )
  455. {
  456. // Calculate the transform needed to convert from the
  457. // space of the current parent to the new parent.
  458. matrix3x4_t worldToNewParent;
  459. matrix3x4_t defaultWorldToNewParent;
  460. if ( pParentDag )
  461. {
  462. matrix3x4_t parentToWorld;
  463. pParentDag->GetAbsTransform( parentToWorld );
  464. MatrixInvert( parentToWorld, worldToNewParent );
  465. matrix3x4_t defaultParentToWorld;
  466. GetDefaultAbsTransform( pParentDag, defaultParentToWorld );
  467. MatrixInvert( defaultParentToWorld, defaultWorldToNewParent );
  468. }
  469. else
  470. {
  471. SetIdentityMatrix( worldToNewParent );
  472. SetIdentityMatrix( defaultWorldToNewParent );
  473. }
  474. matrix3x4_t oldToNewParentMat;
  475. matrix3x4_t defaultOldToNewParentMat;
  476. CDmeDag *pOldParent = pDagNode->GetParent();
  477. if ( pOldParent )
  478. {
  479. matrix3x4_t oldParentToWorld;
  480. pOldParent->GetAbsTransform( oldParentToWorld );
  481. ConcatTransforms( worldToNewParent, oldParentToWorld, oldToNewParentMat );
  482. matrix3x4_t defaultOldParentToWorld;
  483. GetDefaultAbsTransform( pOldParent, defaultOldParentToWorld);
  484. ConcatTransforms( defaultWorldToNewParent, defaultOldParentToWorld, defaultOldToNewParentMat );
  485. }
  486. else
  487. {
  488. oldToNewParentMat = worldToNewParent;
  489. defaultOldToNewParentMat = defaultWorldToNewParent;
  490. }
  491. CDmeTransformControl *pTransformControl = pDagNode->FindTransformControl();
  492. if ( pTransformControl )
  493. {
  494. // Move the position value into the space of the new parent
  495. Vector position = pTransformControl->GetPosition();
  496. Vector newPosition;
  497. VectorTransform( position, oldToNewParentMat, newPosition );
  498. pTransformControl->SetPosition( newPosition );
  499. // Move the default position into the space of new parent
  500. if ( pTransformControl->HasDefaultPosition() )
  501. {
  502. Vector newPosition;
  503. Vector position = pTransformControl->GetDefaultPosition();
  504. VectorTransform( position, defaultOldToNewParentMat, newPosition );
  505. pTransformControl->SetDefaultPosition( newPosition );
  506. }
  507. // Move the orientation value into the space of the new parent
  508. Quaternion orientation = pTransformControl->GetOrientation();
  509. Quaternion newOrientation;
  510. matrix3x4_t oldOrientMat, newOrientMat;
  511. QuaternionMatrix( orientation, oldOrientMat );
  512. ConcatTransforms( oldToNewParentMat, oldOrientMat, newOrientMat );
  513. MatrixQuaternion( newOrientMat, newOrientation );
  514. pTransformControl->SetOrientation( newOrientation );
  515. // Move the default orientation into the space of the new parent
  516. if ( pTransformControl->HasDefaultOrientation() )
  517. {
  518. Quaternion orientation = pTransformControl->GetDefaultOrientation();
  519. Quaternion newOrientation;
  520. matrix3x4_t oldOrientMat, newOrientMat;
  521. QuaternionMatrix( orientation, oldOrientMat );
  522. ConcatTransforms( defaultOldToNewParentMat, oldOrientMat, newOrientMat );
  523. MatrixQuaternion( newOrientMat, newOrientation );
  524. pTransformControl->SetDefaultOrientation( newOrientation );
  525. }
  526. }
  527. }
  528. //-------------------------------------------------------------------------------------------------
  529. // Purpose: Re-parent the specified dag node from its current parent to the specified dag node.
  530. //-------------------------------------------------------------------------------------------------
  531. void CDmAnimUtils::ReParentDagNode( CDmeDag *pDagNode, CDmeDag *pParentDag, bool bMaintainWorldPos, ReParentLogMode_t logMode )
  532. {
  533. if ( pDagNode == NULL )
  534. return;
  535. CDmeTransform *pTransform = pDagNode->GetTransform();
  536. if ( bMaintainWorldPos && pTransform )
  537. {
  538. // Modify the default values of the controls on the dag so that they maintain
  539. // their world space position when the specified dag node is made the new parent.
  540. UpdateDefaultsForNewParent( pDagNode, pParentDag );
  541. // Get the current world space matrix of the dag
  542. matrix3x4_t currentWorldMat;
  543. pDagNode->GetAbsTransform( currentWorldMat );
  544. // Get the current world space matrix of the new parent
  545. matrix3x4_t parentWorldMat;
  546. if ( pParentDag )
  547. {
  548. pParentDag->GetAbsTransform( parentWorldMat );
  549. }
  550. else
  551. {
  552. SetIdentityMatrix( parentWorldMat );
  553. }
  554. matrix3x4_t invParentMat;
  555. MatrixInvert( parentWorldMat, invParentMat );
  556. // If an update of the logs is requested re-generate the local logs of the dag so that the same
  557. // world space location is maintained once the dag is a child of the specified parent.
  558. if ( logMode == REPARENT_LOGS_MAINTAIN_WORLD )
  559. {
  560. // Determine if the dag has existing position and orientation samples
  561. CDmeChannel *pPosChannel = FindDagTransformChannel( pDagNode, TRANSFORM_POSITION );
  562. CDmeChannel *pRotChannel = FindDagTransformChannel( pDagNode, TRANSFORM_ORIENTATION );
  563. // Generate the log samples in the space of the new parent
  564. GenerateLogSamples( pDagNode, pParentDag, pPosChannel != NULL, pRotChannel != NULL, NULL );
  565. }
  566. else if ( logMode == REPARENT_LOGS_OFFSET_LOCAL )
  567. {
  568. // Compute the transform from the space defined by the
  569. // current parent to the space defined by the new parent.
  570. matrix3x4_t currentParentMat;
  571. pDagNode->GetParentWorldMatrix( currentParentMat );
  572. matrix3x4_t oldToNewParentMat;
  573. ConcatTransforms( invParentMat, currentParentMat, oldToNewParentMat );
  574. // Apply the transform to all the samples of the position channel of the dag
  575. CDmeChannel *pPosChannel = FindDagTransformChannel( pDagNode, TRANSFORM_POSITION );
  576. if ( pPosChannel )
  577. {
  578. CDmeTypedLogLayer< Vector > *pPositionLog = CastElement< CDmeTypedLogLayer< Vector > >( pPosChannel->GetLog() );
  579. RotatePositionLog( pPositionLog, oldToNewParentMat );
  580. }
  581. // Apply the the transform to all the samples of the orientation channel of the dag
  582. CDmeChannel *pRotChannel = FindDagTransformChannel( pDagNode, TRANSFORM_ORIENTATION );
  583. if ( pRotChannel )
  584. {
  585. CDmeTypedLogLayer< Quaternion > *pRotationLog = CastElement< CDmeTypedLogLayer< Quaternion > >( pRotChannel->GetLog() );
  586. RotateOrientationLog( pRotationLog, oldToNewParentMat, true );
  587. }
  588. }
  589. // Compute the new local matrix of the transform which will maintain the world space position of the dag
  590. matrix3x4_t newLocalMat;
  591. ConcatTransforms( invParentMat, currentWorldMat, newLocalMat );
  592. pTransform->SetTransform( newLocalMat );
  593. }
  594. // Remove the child from all parents
  595. CUtlVector< CDmeDag* > parents;
  596. FindAncestorsReferencingElement( pDagNode, parents );
  597. int nParents = parents.Count();
  598. for ( int i = 0; i < nParents; ++i )
  599. {
  600. parents[ i ]->RemoveChild( pDagNode );
  601. }
  602. // Add the child to the specified parent
  603. if ( pParentDag )
  604. {
  605. pParentDag->AddChild( pDagNode );
  606. }
  607. if ( logMode == REPARENT_LOGS_OVERWRITE )
  608. {
  609. SetLogsToCurrentTransform( pDagNode );
  610. }
  611. }
  612. //-------------------------------------------------------------------------------------------------
  613. // Set the temporary override parent of a dag, maintaining its world space position and orientation
  614. // animation
  615. //-------------------------------------------------------------------------------------------------
  616. void CDmAnimUtils::SetOverrideParent( CDmeDag *pDagNode, const CDmeDag *pOverrideParent, bool bPosition, bool bRotation )
  617. {
  618. if ( pDagNode == NULL )
  619. return;
  620. CDmeTransform *pTransform = pDagNode->GetTransform();
  621. if ( pTransform == NULL )
  622. return;
  623. // Do not allow the override parent to be set to a dag that is a child of the specified dag.
  624. if ( pDagNode->IsAncestorOfDag( pOverrideParent ) || ( pDagNode == pOverrideParent ) )
  625. return;
  626. // If both position and rotation are false treat it the same as if pOverrideParent is NULL.
  627. if ( !bPosition && !bRotation )
  628. {
  629. pOverrideParent = NULL;
  630. }
  631. // Get the shot and movie that the specified dag node belongs to for use in time conversions
  632. CDmeClip *pShot = NULL;
  633. CDmeClip *pMovie = NULL;
  634. FindShotAndMovieForDag( pDagNode, pShot, pMovie );
  635. // Find the channels which will be needed to evaluate
  636. // the position of the child and the parent dag.
  637. CUtlVector< CDmeOperator* > operatorList;
  638. pDagNode->FindRelevantOperators( operatorList );
  639. if ( pOverrideParent )
  640. {
  641. pOverrideParent->FindRelevantOperators( operatorList );
  642. }
  643. // Build a list of all of the key times for the operators
  644. CUtlVector< DmeTime_t > keyTimes;
  645. CompileKeyTimeList( operatorList, keyTimes, NULL, pShot, pMovie );
  646. // Get the world space transform of the dag at all the key times
  647. CUtlVector< matrix3x4_t > worldSpaceTransforms;
  648. GenerateDagWorldTransformList( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
  649. // Get the current world space matrix of the dag
  650. matrix3x4_t currentWorldMat;
  651. pDagNode->GetAbsTransform( currentWorldMat );
  652. // Now set the temporary override parent, note if NULL
  653. // is specified the override will simply be removed.
  654. pDagNode->SetOverrideParent( pOverrideParent, bPosition, bRotation );
  655. // Update the logs of the dag node so that it matches
  656. // its previous world space transform at all times
  657. SetDagWorldSpaceTransforms( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
  658. // Compute the new local matrix of the transform which
  659. // will maintain the world space position of the dag
  660. pDagNode->SetAbsTransform( currentWorldMat );
  661. }
  662. //-------------------------------------------------------------------------------------------------
  663. // Enable or disable the override parent functionality on a dag, if different than the current
  664. // state the logs will be updated such the dag world space position and orientation are maintained.
  665. //-------------------------------------------------------------------------------------------------
  666. void CDmAnimUtils::ToggleOverrideParent( CDmeDag *pDagNode, bool bEnable )
  667. {
  668. if ( pDagNode == NULL )
  669. return;
  670. CDmeTransform *pTransform = pDagNode->GetTransform();
  671. if ( pTransform == NULL )
  672. return;
  673. // If the enable state matches the requested state nothing needs to be done.
  674. if ( pDagNode->IsOverrideParentEnabled() == bEnable )
  675. return;
  676. // Get the current override parent regardless of the override state
  677. const CDmeDag *pOverrideParent = pDagNode->GetOverrideParent( true );
  678. if ( pOverrideParent == NULL )
  679. return;
  680. // Get the shot and movie that the specified dag node belongs to for use in time conversions
  681. CDmeClip *pShot = NULL;
  682. CDmeClip *pMovie = NULL;
  683. FindShotAndMovieForDag( pDagNode, pShot, pMovie );
  684. // Find the channels which will be needed to evaluate
  685. // the position of the child and the parent dag.
  686. CUtlVector< CDmeOperator* > operatorList;
  687. pDagNode->FindRelevantOperators( operatorList );
  688. if ( pOverrideParent )
  689. {
  690. pOverrideParent->FindRelevantOperators( operatorList );
  691. }
  692. // Build a list of all of the key times for the operators
  693. CUtlVector< DmeTime_t > keyTimes;
  694. CompileKeyTimeList( operatorList, keyTimes, NULL, pShot, pMovie );
  695. // Get the world space transform of the dag at all the key times
  696. CUtlVector< matrix3x4_t > worldSpaceTransforms;
  697. GenerateDagWorldTransformList( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
  698. // Get the current world space matrix of the dag
  699. matrix3x4_t currentWorldMat;
  700. pDagNode->GetAbsTransform( currentWorldMat );
  701. // Now change the override parent enable state and update
  702. // the local transforms to match the world transforms
  703. pDagNode->EnableOverrideParent( bEnable );
  704. // Update the logs of the dag node so that it matches
  705. // its previous world space transform at all times
  706. SetDagWorldSpaceTransforms( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
  707. // Compute the new local matrix of the transform which
  708. // will maintain the world space position of the dag
  709. pDagNode->SetAbsTransform( currentWorldMat );
  710. }
  711. //-------------------------------------------------------------------------------------------------
  712. // Determine if the specified dag node has any constraints
  713. //-------------------------------------------------------------------------------------------------
  714. bool CDmAnimUtils::DagHasConstraints( CDmeDag *pDag )
  715. {
  716. if ( pDag != NULL )
  717. {
  718. // Find the constraint slaves referencing this dag
  719. CUtlVector< CDmeConstraintSlave* > constraintSlaves( 0, 8 );
  720. FindAncestorsReferencingElement( pDag, constraintSlaves );
  721. int nSlaves = constraintSlaves.Count();
  722. for ( int iSlave = 0; iSlave < nSlaves; ++iSlave )
  723. {
  724. CDmeRigBaseConstraintOperator *pConstraint = FindAncestorReferencingElement< CDmeRigBaseConstraintOperator >( constraintSlaves[ iSlave ] );
  725. if ( pConstraint )
  726. return true;
  727. }
  728. }
  729. return false;
  730. }
  731. //-------------------------------------------------------------------------------------------------
  732. // Remove all constraints from the currently selected dag nodes.
  733. //-------------------------------------------------------------------------------------------------
  734. void CDmAnimUtils::RemoveConstraints( CDmeDag *pDag )
  735. {
  736. CDmeRigBaseConstraintOperator::RemoveConstraintsFromDag( pDag );
  737. }
  738. //-------------------------------------------------------------------------------------------------
  739. // Create a constraint of the specified type
  740. //-------------------------------------------------------------------------------------------------
  741. CDmeRigBaseConstraintOperator *CDmAnimUtils::CreateConstraint( const char *pchName, EConstraintType constraintType, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetList, bool bPreserveOffset, float flTargetWeight, bool bOperate )
  742. {
  743. // Verify the input parameters are valid.
  744. if ( ( pConstrainedDag == NULL ) || ( targetList.Count() <= 0 ) )
  745. return NULL;
  746. // IK constraints should not be created through this path
  747. // since their setup is significantly different
  748. if ( constraintType == CT_IK )
  749. return NULL;
  750. // First see if there is already an existing constraint of the specified type on the dag node.
  751. CDmeRigBaseConstraintOperator *pConstraint = FindConstraintOnDag( pConstrainedDag, constraintType );
  752. // Create a constraint if one of the specified type did not already exist on the dag node.
  753. if ( pConstraint == NULL )
  754. {
  755. pConstraint = InstanceConstraint( pchName, constraintType, pConstrainedDag->GetFileId() );
  756. if ( pConstraint == NULL )
  757. return NULL;
  758. // Set the slave dag which will be controlled by the constraint
  759. pConstraint->SetSlave( pConstrainedDag );
  760. // Disconnect the channels from the attributes of slave transform that are written by the constraint.
  761. pConstraint->DisconnectTransformChannels();
  762. }
  763. // Initialize the weights
  764. int nTargets = targetList.Count();
  765. CUtlVector< float > weights;
  766. weights.SetCount( nTargets );
  767. for ( int i = 0; i < nTargets; ++i )
  768. {
  769. weights[ i ] = flTargetWeight;
  770. }
  771. // Add the handles to the constraint or update their weights if they already exist.
  772. pConstraint->AddHandles( nTargets, targetList.Base(), weights.Base(), bPreserveOffset, NULL );
  773. // If specified operate the constraint so that its result is available immediately
  774. if ( bOperate )
  775. {
  776. pConstraint->Operate();
  777. }
  778. return pConstraint;
  779. }
  780. //-------------------------------------------------------------------------------------------------
  781. // Create a Point constraint which will control the position of the specified dag such that it
  782. // matches the weighted target position
  783. //-------------------------------------------------------------------------------------------------
  784. CDmeRigPointConstraintOperator* CDmAnimUtils::CreatePointConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight )
  785. {
  786. CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_POINT, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, true );
  787. return CastElement< CDmeRigPointConstraintOperator >( pConstraint );
  788. }
  789. //-------------------------------------------------------------------------------------------------
  790. // Create a Orient constraint which will control the orientation of the specified dag such that it
  791. // matches the weighted target orientation
  792. //-------------------------------------------------------------------------------------------------
  793. CDmeRigOrientConstraintOperator* CDmAnimUtils::CreateOrientConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight )
  794. {
  795. CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_ORIENT, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, true );
  796. return CastElement< CDmeRigOrientConstraintOperator >( pConstraint );
  797. }
  798. //-------------------------------------------------------------------------------------------------
  799. // Create a Parent constraint which will control the position and orientation of the specified dag
  800. // such the dag behaves as if it is a child of the transform defined by the weighted target list
  801. //-------------------------------------------------------------------------------------------------
  802. CDmeRigParentConstraintOperator* CDmAnimUtils::CreateParentConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight )
  803. {
  804. CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_PARENT, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, true );
  805. return CastElement< CDmeRigParentConstraintOperator >( pConstraint );
  806. }
  807. //-------------------------------------------------------------------------------------------------
  808. // Create an Aim constraint which will control the orientation of the specified dag such the it
  809. // points toward the weighted target position
  810. //-------------------------------------------------------------------------------------------------
  811. CDmeRigAimConstraintOperator* CDmAnimUtils::CreateAimConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight, const Vector &upVector, TransformSpace_t upSpace, const CDmeDag* pReferenceDag )
  812. {
  813. CDmeRigAimConstraintOperator *pAimConstraint = NULL;
  814. CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_AIM, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, false );
  815. if ( pConstraint )
  816. {
  817. pAimConstraint = CastElement< CDmeRigAimConstraintOperator >( pConstraint );
  818. if ( pAimConstraint )
  819. {
  820. // Translate all spaces into a dag node by which the space is defined
  821. if ( upSpace != TS_REFERENCE_SPACE )
  822. {
  823. pReferenceDag = NULL;
  824. const CDmeDag *pSlave = pAimConstraint->GetSlave();
  825. if ( pSlave && ( upSpace == TS_OBJECT_SPACE ) )
  826. {
  827. pReferenceDag = pSlave->GetParent();
  828. }
  829. }
  830. pAimConstraint->SetUpVector( upVector, bPreserveOffset, pReferenceDag );
  831. pAimConstraint->Operate();
  832. }
  833. }
  834. return pAimConstraint;
  835. }
  836. //-------------------------------------------------------------------------------------------------
  837. // Create an ik constraint controlling the 2 bone chain from the specified root dag to the
  838. // specified end dag.
  839. //-------------------------------------------------------------------------------------------------
  840. CDmeRigIKConstraintOperator* CDmAnimUtils::CreateIKConstraint( const char *pchName, CDmeDag *pStartNode, CDmeDag *pEndNode, CDmeDag *pTargetDag, bool bPreserveOffset, const Vector &poleVector, CDmeDag *pPoleVectorTarget )
  841. {
  842. // All of the input dag nodes must be specified
  843. if ( pStartNode == NULL || pEndNode == NULL || pTargetDag == NULL )
  844. return NULL;
  845. // Find the middle node and make sure the chain is valid.
  846. CDmeDag *pMiddleNode = pEndNode->GetParent();
  847. if ( pMiddleNode == NULL )
  848. return NULL;
  849. if ( pMiddleNode->GetParent() != pStartNode )
  850. return NULL;
  851. // Make sure there are no existing ik constraints on the nodes
  852. if ( FindConstraintOnDag( pStartNode, CT_IK ) != NULL )
  853. return NULL;
  854. if ( FindConstraintOnDag( pMiddleNode, CT_IK ) != NULL )
  855. return NULL;
  856. if ( FindConstraintOnDag( pEndNode, CT_IK ) != NULL )
  857. return NULL;
  858. // Create the ik constraint
  859. CDmeRigIKConstraintOperator *pIKConstraint = CreateElement< CDmeRigIKConstraintOperator >( pchName, pStartNode->GetFileId() );
  860. if ( pIKConstraint == NULL )
  861. return NULL;
  862. // Set the joints that are controlled by the constraint
  863. pIKConstraint->SetJoints( pStartNode, pMiddleNode, pEndNode );
  864. // Disconnect the channels from the attributes of slave transform that are written by the constraint.
  865. pIKConstraint->DisconnectTransformChannels();
  866. // Add the handles to the constraint or update their weights if they already exist.
  867. pIKConstraint->AddHandles( 1, &pTargetDag, NULL, bPreserveOffset, NULL );
  868. // Initialize the pole vector, if a pole vector target dag is specified
  869. // it takes precedence over the pole vector direction
  870. pIKConstraint->SetPoleVector( poleVector );
  871. pIKConstraint->SetPoleVectorTarget( pPoleVectorTarget );
  872. // Initialize and run the constraint
  873. pIKConstraint->Setup( bPreserveOffset );
  874. pIKConstraint->Operate();
  875. return pIKConstraint;
  876. }
  877. //-------------------------------------------------------------------------------------------------
  878. // Allocate a constraint of the specified type.
  879. //-------------------------------------------------------------------------------------------------
  880. CDmeRigBaseConstraintOperator *CDmAnimUtils::InstanceConstraint( char const *pchName, EConstraintType eType, const DmFileId_t &fileId )
  881. {
  882. CDmeRigBaseConstraintOperator *pOperator = NULL;
  883. switch ( eType )
  884. {
  885. case CT_POINT:
  886. {
  887. pOperator = CreateElement< CDmeRigPointConstraintOperator >( pchName, fileId );
  888. }
  889. break;
  890. case CT_ORIENT:
  891. {
  892. pOperator = CreateElement< CDmeRigOrientConstraintOperator >( pchName, fileId );
  893. }
  894. break;
  895. case CT_AIM:
  896. {
  897. pOperator = CreateElement< CDmeRigAimConstraintOperator >( pchName, fileId );
  898. }
  899. break;
  900. case CT_IK:
  901. {
  902. pOperator = CreateElement< CDmeRigIKConstraintOperator >( pchName, fileId );
  903. }
  904. break;
  905. case CT_PARENT:
  906. {
  907. pOperator = CreateElement< CDmeRigParentConstraintOperator>( pchName, fileId );
  908. }
  909. break;
  910. default:
  911. return NULL;
  912. }
  913. return pOperator;
  914. }
  915. //-------------------------------------------------------------------------------------------------
  916. // Purpose: Find the position or orientation channel for the specified dag node
  917. //-------------------------------------------------------------------------------------------------
  918. CDmeChannel *CDmAnimUtils::FindDagTransformChannel( CDmeDag* pDag, const char *pchAttributeName )
  919. {
  920. if ( pDag == NULL )
  921. return NULL;
  922. CDmeChannel *pChannel = FindChannelTargetingElement( pDag->GetTransform(), pchAttributeName );
  923. // If no channel was attach directly to the dag, check for a
  924. // channel attached to constraint that is driving the dag node.
  925. if ( pChannel == NULL )
  926. {
  927. // Find the constraints associated with the specified dag node.
  928. CUtlVector< CDmeConstraintSlave* > constraintSlaves;
  929. FindAncestorsReferencingElement( pDag, constraintSlaves );
  930. int nSlaves = constraintSlaves.Count();
  931. for ( int i = 0; i < nSlaves; ++i )
  932. {
  933. CDmeConstraintSlave *pSlave = constraintSlaves[ i ];
  934. if ( pSlave == NULL )
  935. continue;
  936. // Find the channel targeting the constraint slave
  937. pChannel = FindChannelTargetingElement( pSlave, pchAttributeName );
  938. if ( pChannel != NULL )
  939. break;
  940. }
  941. }
  942. return pChannel;
  943. }
  944. //-------------------------------------------------------------------------------------------------
  945. // Purpose: Find the constraint of the specified type controlling the specified dag node
  946. //-------------------------------------------------------------------------------------------------
  947. CDmeRigBaseConstraintOperator *CDmAnimUtils::FindConstraintOnDag( CDmeDag* pDag, EConstraintType constraintType )
  948. {
  949. if ( pDag == NULL )
  950. return NULL;
  951. // Find the constraints associated with the specified dag node.
  952. CUtlVector< CDmeConstraintSlave* > constraintSlaves;
  953. FindAncestorsReferencingElement( pDag, constraintSlaves );
  954. int nSlaves = constraintSlaves.Count();
  955. for ( int i = 0; i < nSlaves; ++i )
  956. {
  957. CDmeConstraintSlave *pSlave = constraintSlaves[ i ];
  958. if ( pSlave == NULL )
  959. continue;
  960. CDmeRigBaseConstraintOperator *pConstraint = pSlave->GetConstraint();
  961. if ( pConstraint )
  962. {
  963. if ( pConstraint->GetConstraintType() == constraintType )
  964. {
  965. return pConstraint;
  966. }
  967. }
  968. }
  969. return NULL;
  970. }
  971. //-------------------------------------------------------------------------------------------------
  972. // Create the position and orientation channels for the specified dag node if they do not already
  973. // exist.
  974. //-------------------------------------------------------------------------------------------------
  975. void CDmAnimUtils::CreateTransformChannelsForDag( CDmeDag *pDag, CDmeChannelsClip *pChannelsClip, bool bPosition, bool bOrientation, CDmeChannel *&pPosChannel, CDmeChannel *&pRotChannel )
  976. {
  977. pPosChannel = NULL;
  978. pRotChannel = NULL;
  979. if ( pDag == NULL )
  980. return;
  981. CDmeTransform *pTransform = pDag->GetTransform();
  982. if ( pTransform == NULL )
  983. return;
  984. CDmeTransformControl *pTransformControl = NULL;
  985. // Create the position channel if it does not exist
  986. if ( bPosition )
  987. {
  988. pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
  989. if ( pPosChannel )
  990. {
  991. pTransformControl = CastElement< CDmeTransformControl >( pPosChannel->GetFromElement() );
  992. }
  993. else
  994. {
  995. char name[ 256 ];
  996. V_snprintf( name, sizeof( name ), "%s_p", pDag->GetName() );
  997. pPosChannel = CreateElement< CDmeChannel >( name, pDag->GetFileId() );
  998. pPosChannel->SetMode( CM_PLAY );
  999. pPosChannel->CreateLog( AT_VECTOR3 );
  1000. pPosChannel->SetOutput( pTransform, "position" );
  1001. if ( pChannelsClip )
  1002. {
  1003. pChannelsClip->m_Channels.AddToTail( pPosChannel );
  1004. }
  1005. }
  1006. }
  1007. // Create the orientation channel if it does not exist
  1008. if ( bOrientation )
  1009. {
  1010. pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
  1011. if ( pRotChannel )
  1012. {
  1013. pTransformControl = CastElement< CDmeTransformControl >( pRotChannel->GetFromElement() );
  1014. }
  1015. else
  1016. {
  1017. char name[ 256 ];
  1018. V_snprintf( name, sizeof( name ), "%s_o", pDag->GetName() );
  1019. pRotChannel = CreateElement< CDmeChannel >( name, pDag->GetFileId() );
  1020. pRotChannel->SetMode( CM_PLAY );
  1021. pRotChannel->CreateLog( AT_QUATERNION );
  1022. pRotChannel->SetOutput( pTransform, "orientation" );
  1023. if ( pChannelsClip )
  1024. {
  1025. pChannelsClip->m_Channels.AddToTail( pRotChannel );
  1026. }
  1027. }
  1028. }
  1029. // If either channel is valid make sure a transform control is properly attached
  1030. if ( pPosChannel || pRotChannel )
  1031. {
  1032. if ( pTransformControl == NULL )
  1033. {
  1034. pTransformControl = CreateElement< CDmeTransformControl >( pDag->GetName(), pDag->GetFileId() );
  1035. }
  1036. if ( pPosChannel )
  1037. {
  1038. pTransformControl->SetPosition( pTransform->GetPosition() );
  1039. pTransformControl->SetPositionChannel( pPosChannel );
  1040. pPosChannel->SetInput( pTransformControl->GetPositionAttr() );
  1041. }
  1042. if ( pRotChannel )
  1043. {
  1044. pTransformControl->SetOrientation( pTransform->GetOrientation() );
  1045. pTransformControl->SetOrientationChannel( pRotChannel );
  1046. pRotChannel->SetInput( pTransformControl->GetOrientationAttr() );
  1047. }
  1048. }
  1049. }
  1050. //-------------------------------------------------------------------------------------------------
  1051. // Print the position and orientation of the dag over time for debugging purposes
  1052. //-------------------------------------------------------------------------------------------------
  1053. void CDmAnimUtils::PrintDagTransformOverTime( CDmeDag* pDag, CDmeClip *pShot, CDmeClip *pMovie )
  1054. {
  1055. // Find the channels which will be needed to evaluate
  1056. // the position of the child and the parent dag.
  1057. CUtlVector< CDmeOperator* > operatorList;
  1058. pDag->FindRelevantOperators( operatorList );
  1059. CUtlVector< DmeTime_t > keyTimes;
  1060. CompileKeyTimeList( operatorList, keyTimes, NULL, pShot, pMovie );
  1061. CUtlVector< matrix3x4_t > transformList;
  1062. GenerateDagWorldTransformList( pDag, keyTimes, transformList, pShot, pMovie );
  1063. int nNumTransforms = transformList.Count();
  1064. for ( int i = 0; i < nNumTransforms; ++i )
  1065. {
  1066. Vector pos;
  1067. QAngle rot;
  1068. MatrixAngles( transformList[ i ], rot, pos );
  1069. Msg( "%i : pos < %f, %f, %f >, rot< %f, %f, %f >\n", keyTimes[ i ].GetTenthsOfMS(), pos.x, pos.y, pos.z, rot.x, rot.y, rot.z );
  1070. }
  1071. }
  1072. //-------------------------------------------------------------------------------------------------
  1073. // Update the logs of the dag so the the current local transform is the only value in the log
  1074. //-------------------------------------------------------------------------------------------------
  1075. void CDmAnimUtils::SetLogsToCurrentTransform( CDmeDag *pDag )
  1076. {
  1077. if ( pDag == NULL )
  1078. return;
  1079. CDmeTransform *pTransform = pDag->GetTransform();
  1080. if ( pTransform == NULL )
  1081. return;
  1082. matrix3x4_t mTransform;
  1083. pTransform->GetTransform( mTransform );
  1084. Vector pos;
  1085. Quaternion rot;
  1086. MatrixAngles( mTransform, rot, pos );
  1087. CDmeChannel *pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
  1088. if ( pPosChannel )
  1089. {
  1090. CDmeTypedLog< Vector > *pPosLog = CastElement< CDmeTypedLog< Vector > >( pPosChannel->GetLog() );
  1091. if ( pPosLog )
  1092. {
  1093. pPosLog->ClearKeys();
  1094. pPosLog->SetKey( pPosChannel->GetCurrentTime(), pos );
  1095. }
  1096. }
  1097. CDmeChannel *pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
  1098. if ( pRotChannel )
  1099. {
  1100. CDmeTypedLog< Quaternion > *pRotLog = CastElement< CDmeTypedLog< Quaternion > >( pRotChannel->GetLog() );
  1101. if ( pRotLog )
  1102. {
  1103. pRotLog->ClearKeys();
  1104. pRotLog->SetKey( pRotChannel->GetCurrentTime(), rot );
  1105. }
  1106. }
  1107. }
  1108. //-------------------------------------------------------------------------------------------------
  1109. // Purpose: Generate log samples for the specified dag node, if a parent is provided, generate the
  1110. // samples in the space of that parent, otherwise generate the samples in world space.
  1111. //
  1112. // Input : pDag - Pointer to the dag node for which log samples are to be generated.
  1113. // Input : pParent - Pointer to the dag node whose space samples are to be generated in
  1114. // Input : bPosition - Flag indicating if the position log of the transform will be modified
  1115. // Input : bRotation - Flag indicating if the rotation log of the transform will be modified
  1116. // Input : pTimeSelection - Pointer to a time selection over which the samples are to be generated,
  1117. // if NULL samples will be generated at all key points for the log.
  1118. //
  1119. //-------------------------------------------------------------------------------------------------
  1120. void CDmAnimUtils::GenerateLogSamples( CDmeDag* pDag, CDmeDag *pParent, bool bPosition, bool bRotation, const DmeLog_TimeSelection_t *pTimeSelection )
  1121. {
  1122. if ( pDag == NULL )
  1123. return;
  1124. CDmeClip *pShot = NULL;
  1125. CDmeClip *pMovie = NULL;
  1126. FindShotAndMovieForDag( pDag, pShot, pMovie );
  1127. // If position and rotation are both false then there is nothing to do.
  1128. if ( !bPosition && !bRotation )
  1129. return;
  1130. // If a time selection is provided use the re-sample interval and perform samples through the
  1131. // time selection, otherwise build a list of all of the key times for the relevant channels
  1132. CUtlVector< DmeTime_t > keyTimes;
  1133. // Find the channels which will be needed to evaluate
  1134. // the position of the child and the parent dag.
  1135. CUtlVector< CDmeOperator* > operatorList;
  1136. pDag->FindRelevantOperators( operatorList );
  1137. if ( pParent )
  1138. {
  1139. pParent->FindRelevantOperators( operatorList );
  1140. }
  1141. CompileKeyTimeList( operatorList, keyTimes, pTimeSelection, pShot, pMovie );
  1142. // Get the world space transform of the parent and the child at all of the key times
  1143. CUtlVector< matrix3x4_t > parentTransformList;
  1144. CUtlVector< matrix3x4_t > childTransformList;
  1145. GenerateDagWorldTransformList( pDag, keyTimes, childTransformList, pShot, pMovie );
  1146. if ( pParent )
  1147. {
  1148. GenerateDagWorldTransformList( pParent, keyTimes, parentTransformList, pShot, pMovie );
  1149. }
  1150. // Find the existing channels associated with the position and orientation of the dag transform
  1151. CDmeChannel *pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
  1152. CDmeChannel *pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
  1153. // Find the channels clip containing the position and rotation channel. They should both be in the
  1154. // same channels clip, so we pick the channels clip from the position unless position is not used
  1155. // and the rotation channels clip is valid, or if the position channels clip is not valid.
  1156. CDmeChannelsClip *pChannelsClipPos = FindAncestorReferencingElement< CDmeChannelsClip >( pPosChannel );
  1157. CDmeChannelsClip *pChannelsClipRot = FindAncestorReferencingElement< CDmeChannelsClip >( pRotChannel );
  1158. CDmeChannelsClip *pChannelsClip = ( ( !bPosition && ( pChannelsClipRot != NULL ) ) || ( pChannelsClipPos == NULL ) ) ? pChannelsClipRot : pChannelsClipPos;
  1159. // Ensure that the dag has channels for position and rotation
  1160. CreateTransformChannelsForDag( pDag, pChannelsClip, bPosition, bRotation, pPosChannel, pRotChannel );
  1161. // Find the position and rotation channels for the child dag and create a new layer in each channel
  1162. CDmeLog *pPosLog = ( ( pPosChannel != NULL ) && bPosition ) ? pPosChannel->GetLog() : NULL;
  1163. CDmeLog *pRotLog = ( ( pRotChannel != NULL ) && bRotation ) ? pRotChannel->GetLog() : NULL;
  1164. CDmeTypedLogLayer< Vector > *pPosLayer = ( pPosLog != NULL ) ? CastElement< CDmeTypedLogLayer< Vector > >( pPosLog->AddNewLayer() ) : NULL;
  1165. CDmeTypedLogLayer< Quaternion > *pRotLayer = ( pRotLog != NULL ) ? CastElement< CDmeTypedLogLayer< Quaternion > >( pRotLog->AddNewLayer() ) : NULL;
  1166. Assert( pPosLayer || !bPosition );
  1167. Assert( pRotLayer || !bRotation );
  1168. // Build the clip stack to convert the global time into a local time
  1169. DmeClipStack_t clipStack;
  1170. if ( pChannelsClip )
  1171. {
  1172. pChannelsClip->BuildClipStack( &clipStack, pMovie, pShot );
  1173. }
  1174. else if ( pMovie && pShot )
  1175. {
  1176. clipStack.AddClipToTail( pMovie );
  1177. clipStack.AddClipToTail( pShot );
  1178. }
  1179. // Disable the undo, the only thing that is actually
  1180. // needed is the add new layer and the flatten layers.
  1181. CDisableUndoScopeGuard undosg;
  1182. Vector position = vec3_origin;
  1183. Quaternion rotation = quat_identity;
  1184. DmeTime_t logTime = DMETIME_ZERO;
  1185. int nTimes = keyTimes.Count();
  1186. for ( int iTime = 0; iTime < nTimes; ++iTime )
  1187. {
  1188. // Calculate the time relative to the log
  1189. logTime = clipStack.ToChildMediaTime( keyTimes[ iTime ], false );
  1190. if ( pParent )
  1191. {
  1192. // Calculate the transform of the child relative to the parent
  1193. // needed to keep the child in its original location in world space.
  1194. const matrix3x4_t &parentMatrix = parentTransformList[ iTime ];
  1195. const matrix3x4_t &worldChildMatrix = childTransformList[ iTime ];
  1196. matrix3x4_t invParentMatrix;
  1197. matrix3x4_t localChildMatrix;
  1198. MatrixInvert( parentMatrix, invParentMatrix );
  1199. ConcatTransforms( invParentMatrix, worldChildMatrix, localChildMatrix );
  1200. MatrixAngles( localChildMatrix, rotation, position );
  1201. }
  1202. else
  1203. {
  1204. const matrix3x4_t &worldMatrix = childTransformList[ iTime ];
  1205. MatrixAngles( worldMatrix, rotation, position );
  1206. }
  1207. // Set a key storing the new position and rotation of the child at the current time
  1208. if ( pPosLayer )
  1209. {
  1210. pPosLayer->SetKey( logTime, position, SEGMENT_INTERPOLATE, CURVE_DEFAULT, false );
  1211. }
  1212. if ( pRotLayer )
  1213. {
  1214. pRotLayer->SetKey( logTime, rotation, SEGMENT_INTERPOLATE, CURVE_DEFAULT, false );
  1215. }
  1216. }
  1217. // If a time selection is specified blend the top level log layers containing the new
  1218. // position and orientation with the base log according to the time selection.
  1219. float flattenThreshold = DMELOG_DEFAULT_THRESHHOLD;
  1220. if ( pTimeSelection )
  1221. {
  1222. // Convert the time selection into the local time space of the log.
  1223. DmeLog_TimeSelection_t logTimeSelection = *pTimeSelection;
  1224. clipStack.ToChildMediaTime( logTimeSelection.m_nTimes );
  1225. flattenThreshold = pTimeSelection->m_flThreshold;
  1226. if ( pPosLog )
  1227. {
  1228. pPosLog->BlendLayersUsingTimeSelection( logTimeSelection );
  1229. }
  1230. if ( pRotLog )
  1231. {
  1232. pRotLog->BlendLayersUsingTimeSelection( logTimeSelection );
  1233. }
  1234. // Now flatten the all the layers in the log to finalize the new layer
  1235. {
  1236. // Re-enable the undo so the the flatten can be undone
  1237. CEnableUndoScopeGuard undosg;
  1238. if ( pPosLog )
  1239. {
  1240. pPosLog->FlattenLayers( flattenThreshold, 0 );
  1241. }
  1242. if ( pRotLog )
  1243. {
  1244. pRotLog->FlattenLayers( flattenThreshold, 0 );
  1245. }
  1246. }
  1247. }
  1248. else
  1249. {
  1250. CEnableUndoScopeGuard undosg;
  1251. // If not using a time selection then we only want data from the new log layer so just
  1252. // copy it to the base layer without blending and then remove all other layers.
  1253. if ( pPosLog )
  1254. {
  1255. CDmeLogLayer *pPosBaseLayer = pPosLog->GetLayer( 0 );
  1256. if ( pPosBaseLayer != pPosLayer )
  1257. {
  1258. pPosBaseLayer->CopyLayer( pPosLayer );
  1259. while ( pPosLog->GetNumLayers() > 1 )
  1260. {
  1261. pPosLog->RemoveLayerFromTail();
  1262. pPosLog->RemoveRedundantKeys( false );
  1263. }
  1264. }
  1265. }
  1266. if ( pRotLog )
  1267. {
  1268. CDmeLogLayer *pRotBaseLayer = pRotLog->GetLayer( 0 );
  1269. if ( pRotBaseLayer != pRotLayer )
  1270. {
  1271. pRotBaseLayer->CopyLayer( pRotLayer );
  1272. if ( pRotLog->GetNumLayers() > 1 )
  1273. {
  1274. pRotLog->RemoveLayerFromTail();
  1275. pRotLog->RemoveRedundantKeys( false );
  1276. }
  1277. }
  1278. }
  1279. }
  1280. }
  1281. //-------------------------------------------------------------------------------------------------
  1282. // Purpose: Generate a list of all of the world space transform of the specified dag node at each
  1283. // time in the provided list
  1284. //-------------------------------------------------------------------------------------------------
  1285. void CDmAnimUtils::GenerateDagWorldTransformList( CDmeDag *pDag, const CUtlVector< DmeTime_t > &times, CUtlVector< matrix3x4_t > &transformList, CDmeClip *pShot, CDmeClip *pMovie )
  1286. {
  1287. // Make sure nothing here gets add to the undo queue.
  1288. CDisableUndoScopeGuard undosg;
  1289. // Find the channels and operators which must be updated in order to evaluate transform of the dag.
  1290. CUtlVector< CDmeChannel* > channelList;
  1291. CUtlVector< CDmeOperator* > operatorList;
  1292. pDag->FindRelevantOperators( channelList, operatorList );
  1293. // Build a clip stack for each channel in the list and save the current time of each of the channels.
  1294. CUtlVector< DmeClipStack_t > channelClipStackList;
  1295. CUtlVector< DmeTime_t > channelOriginalTimeList;
  1296. BuildClipStackList( channelList, channelClipStackList, channelOriginalTimeList, pMovie, pShot );
  1297. // Get the number of times to be sampled and resize the transform list to hold one matrix for each time
  1298. int nTimes = times.Count();
  1299. transformList.SetCount( nTimes );
  1300. for ( int iTime = 0; iTime < nTimes; ++iTime )
  1301. {
  1302. DmeTime_t time = times[ iTime ];
  1303. // Evaluate all of the channels relevant to the dag at the specified time
  1304. // so that the transform evaluations will correspond to the correct time.
  1305. PlayChannelsAtTime( time, channelList, operatorList, channelClipStackList );
  1306. // Get the world space transform of the target
  1307. matrix3x4_t &targetTransform = transformList[ iTime ];
  1308. pDag->GetAbsTransform( targetTransform );
  1309. }
  1310. // Restore all of the channels to the original times so that this operation does not have side effects.
  1311. PlayChannelsAtLocalTimes( channelOriginalTimeList, channelList, operatorList, false );
  1312. }
  1313. //-------------------------------------------------------------------------------------------------
  1314. // Update the position and orientation logs of the specified dag node so that the dag node's world
  1315. // space transform matches the provided list
  1316. //-------------------------------------------------------------------------------------------------
  1317. void CDmAnimUtils::SetDagWorldSpaceTransforms( CDmeDag* pDag, CUtlVector< DmeTime_t > &times, const CUtlVector< matrix3x4_t > &transformList, CDmeClip *pShot, CDmeClip *pMovie )
  1318. {
  1319. if ( pDag == NULL )
  1320. return;
  1321. CDmeTransform *pTransform = pDag->GetTransform();
  1322. if ( pTransform == NULL )
  1323. return;
  1324. // Make sure nothing here gets add to the undo queue.
  1325. CDisableUndoScopeGuard undosg;
  1326. // Find the existing channels associated with the position and orientation of the dag transform
  1327. CDmeChannel *pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
  1328. CDmeChannel *pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
  1329. // Find the channels clip containing the position and rotation channel. They should both be in the
  1330. // same channels clip, so we pick the channels clip from the position unless position is not used
  1331. // and the rotation channels clip is valid, or if the position channels clip is not valid.
  1332. CDmeChannelsClip *pChannelsClipPos = FindAncestorReferencingElement< CDmeChannelsClip >( pPosChannel );
  1333. CDmeChannelsClip *pChannelsClipRot = FindAncestorReferencingElement< CDmeChannelsClip >( pRotChannel );
  1334. CDmeChannelsClip *pChannelsClip = ( pChannelsClipPos != NULL ) ? pChannelsClipPos : pChannelsClipRot;
  1335. // Build the clip stack to convert the global time into a local time
  1336. DmeClipStack_t clipStack;
  1337. if ( pChannelsClip )
  1338. {
  1339. pChannelsClip->BuildClipStack( &clipStack, pMovie, pShot );
  1340. }
  1341. else if ( pMovie && pShot )
  1342. {
  1343. clipStack.AddClipToTail( pMovie );
  1344. clipStack.AddClipToTail( pShot );
  1345. }
  1346. // Find the channels and operators which must be updated in order to evaluate transform of the dag.
  1347. CUtlVector< CDmeChannel* > channelList;
  1348. CUtlVector< CDmeOperator* > operatorList;
  1349. pDag->FindRelevantOperators( channelList, operatorList );
  1350. // Build a clip stack for each channel in the list and save the current time of each of the channels.
  1351. CUtlVector< DmeClipStack_t > channelClipStackList;
  1352. CUtlVector< DmeTime_t > channelOriginalTimeList;
  1353. BuildClipStackList( channelList, channelClipStackList, channelOriginalTimeList, pMovie, pShot );
  1354. // Get the number of times to be sampled and resize the transform list to hold one matrix for each time
  1355. int nTimes = times.Count();
  1356. CUtlVector< DmeTime_t > localTimes;
  1357. CUtlVector< Vector > localPositions;
  1358. CUtlVector< Quaternion > localOrientations;
  1359. localTimes.SetCount( nTimes );
  1360. localPositions.SetCount( nTimes );
  1361. localOrientations.SetCount( nTimes );
  1362. for ( int iTime = 0; iTime < nTimes; ++iTime )
  1363. {
  1364. DmeTime_t time = times[ iTime ];
  1365. // Convert the global time to the local time of the logs
  1366. localTimes[ iTime ] = clipStack.ToChildMediaTime( time, false );
  1367. // Evaluate all of the channels relevant to the dag at the specified time
  1368. // so that the transform evaluations will correspond to the correct time.
  1369. PlayChannelsAtTime( time, channelList, operatorList, channelClipStackList );
  1370. // Get the world space transform of the target
  1371. const matrix3x4_t &targetTransform = transformList[ iTime ];
  1372. pDag->SetAbsTransform( targetTransform );
  1373. localPositions[ iTime ] = pTransform->GetPosition();
  1374. localOrientations[ iTime ] = pTransform->GetOrientation();
  1375. }
  1376. // Restore all of the channels to the original times so that this operation does not have side effects.
  1377. PlayChannelsAtLocalTimes( channelOriginalTimeList, channelList, operatorList, false );
  1378. // Write the positions to the log
  1379. if ( pPosChannel )
  1380. {
  1381. CDmeLog *pPosLog = pPosChannel->GetLog();
  1382. if ( pPosLog )
  1383. {
  1384. CDmeTypedLogLayer< Vector > *pPosLayer = CastElement< CDmeTypedLogLayer< Vector > >( pPosLog->GetLayer( 0 ) );
  1385. if ( pPosLayer )
  1386. {
  1387. CEnableUndoScopeGuard undosg;
  1388. pPosLayer->SetAllKeys( localTimes, localPositions );
  1389. pPosLog->RemoveRedundantKeys( false );
  1390. }
  1391. }
  1392. }
  1393. // Write the orientations to the log
  1394. if ( pRotChannel )
  1395. {
  1396. CDmeLog *pRotLog = pRotChannel->GetLog();
  1397. if ( pRotLog )
  1398. {
  1399. CDmeTypedLogLayer< Quaternion > *pRotLayer = CastElement< CDmeTypedLogLayer< Quaternion > >( pRotLog->GetLayer( 0 ) );
  1400. if ( pRotLayer )
  1401. {
  1402. CEnableUndoScopeGuard undosg;
  1403. pRotLayer->SetAllKeys( localTimes, localOrientations );
  1404. pRotLog->RemoveRedundantKeys( false );
  1405. }
  1406. }
  1407. }
  1408. }
  1409. //-------------------------------------------------------------------------------------------------
  1410. // Purpose: Create a list of all of the key times in the provided list of channels
  1411. //-------------------------------------------------------------------------------------------------
  1412. void CDmAnimUtils::CompileKeyTimeList( const CUtlVector< CDmeOperator* > &channelList, CUtlVector< DmeTime_t > &keyTimes, const DmeLog_TimeSelection_t *pTimeSelection, CDmeClip *pShot, CDmeClip *pMovie )
  1413. {
  1414. // Make sure nothing here gets add to the undo queue.
  1415. CDisableUndoScopeGuard undosg;
  1416. // Iterate through all of the provided channels adding
  1417. // the time of all of the keys in each channel to list.
  1418. CUtlRBTree< DmeTime_t > logTimes( 0, 0, DefLessFunc( DmeTime_t ) );
  1419. int nChannels = channelList.Count();
  1420. for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
  1421. {
  1422. CDmeChannel *pChannel = CastElement< CDmeChannel >( channelList[ iChannel ] );
  1423. if ( pChannel == NULL )
  1424. continue;
  1425. CDmeLog *pLog = pChannel->GetLog();
  1426. if ( pLog == NULL )
  1427. continue;
  1428. // Build the clip stack for the channel
  1429. CDmeChannelsClip *pChannelsClip = FindAncestorReferencingElement< CDmeChannelsClip >( pChannel );
  1430. if ( pChannelsClip == NULL )
  1431. continue;
  1432. DmeClipStack_t clipStack;
  1433. pChannelsClip->BuildClipStack( &clipStack, pMovie, pShot );
  1434. // Iterate through all of the keys in the log and
  1435. // make sure there is an entry for the time of the key.
  1436. int nKeys = pLog->GetKeyCount();
  1437. // If the log only has one key, we can ignore the time
  1438. // since the value will be the same throughout time.
  1439. if ( nKeys < 2 )
  1440. continue;
  1441. for ( int iKey = 0; iKey < nKeys; ++iKey )
  1442. {
  1443. // Get the time of the key and convert it to a global time
  1444. DmeTime_t keyTime = pLog->GetKeyTime( iKey );
  1445. if ( ( keyTime <= DMETIME_MINTIME ) || ( keyTime >= DMETIME_MAXTIME ) )
  1446. continue;
  1447. DmeTime_t time = clipStack.FromChildMediaTime( keyTime, false );
  1448. // If a time selection was specified, add only times within it.
  1449. if ( pTimeSelection )
  1450. {
  1451. if ( ( time < pTimeSelection->m_nTimes[ TS_LEFT_FALLOFF ] ) ||
  1452. ( time > pTimeSelection->m_nTimes[ TS_RIGHT_FALLOFF ] ) )
  1453. continue;
  1454. }
  1455. // Add the key time if it is valid and has not already been added.
  1456. if ( ( time <= DMETIME_MINTIME ) || ( time >= DMETIME_MAXTIME ) )
  1457. continue;
  1458. logTimes.InsertIfNotFound( time );
  1459. }
  1460. }
  1461. DmeTime_t firstTime = DMETIME_ZERO;
  1462. DmeTime_t lastTime = DMETIME_ZERO;
  1463. if ( pTimeSelection )
  1464. {
  1465. firstTime = pTimeSelection->m_nTimes[ TS_LEFT_FALLOFF ];
  1466. lastTime = pTimeSelection->m_nTimes[ TS_RIGHT_FALLOFF ];
  1467. }
  1468. else if ( logTimes.Count() > 0 )
  1469. {
  1470. firstTime = logTimes[ logTimes.FirstInorder() ];
  1471. lastTime = logTimes[ logTimes.LastInorder() ];
  1472. }
  1473. // Use a re-sample time of 50ms unless otherwise specified by the time selection
  1474. DmeTime_t resampleInterval = DmeTime_t( 500 );
  1475. if ( pTimeSelection )
  1476. {
  1477. resampleInterval = MAX( pTimeSelection->m_nResampleInterval, DMETIME_MINDELTA );
  1478. }
  1479. // Calculate the starting time for the re-sampling. In order to try to keep the sampling
  1480. // consistent, make sure the starting time is on a multiple of the re-sample time.
  1481. int tmsResampleInterval = resampleInterval.GetTenthsOfMS();
  1482. int tmsFirstTime = firstTime.GetTenthsOfMS();
  1483. DmeTime_t sampleStartTime;
  1484. if ( tmsFirstTime < 0 )
  1485. {
  1486. int resampleStartBase = ( ( tmsFirstTime + 1 ) / tmsResampleInterval );
  1487. sampleStartTime = DmeTime_t( tmsResampleInterval * resampleStartBase );
  1488. }
  1489. else
  1490. {
  1491. int resampleStartBase = ( tmsFirstTime / tmsResampleInterval );
  1492. sampleStartTime = DmeTime_t( tmsResampleInterval * ( resampleStartBase + 1 ) );
  1493. }
  1494. Assert( sampleStartTime > firstTime );
  1495. Assert( sampleStartTime <= ( firstTime + resampleInterval ) );
  1496. // Calculate the approximate number of samples so that the key times array can be preallocated.
  1497. int nTimes = logTimes.Count() + ( lastTime - firstTime ).GetTenthsOfMS() / tmsResampleInterval;
  1498. keyTimes.RemoveAll();
  1499. keyTimes.EnsureCapacity( nTimes + 8 );
  1500. // Set the log time to either the first entry in the set of log times,
  1501. // or to the last time if there are no entries in the set of log times.
  1502. DmeTime_t logTime = lastTime;
  1503. int iLogTime = logTimes.FirstInorder();
  1504. if ( iLogTime != logTimes.InvalidIndex() )
  1505. {
  1506. logTime = logTimes[ iLogTime ];
  1507. }
  1508. // If the first log time is not the first time, with the first time, as there
  1509. // is nothing else that will since the sample times start after the first time.
  1510. if ( logTime != firstTime )
  1511. {
  1512. keyTimes.AddToTail( firstTime );
  1513. }
  1514. // Iterate through the sample times and log times, adding sample times until the next log
  1515. // time is reached and then adding log times until the next sample time is reached.
  1516. DmeTime_t sampleTime = sampleStartTime;
  1517. do
  1518. {
  1519. // Add sample time until the next log time is reached
  1520. while ( sampleTime < logTime )
  1521. {
  1522. keyTimes.AddToTail( sampleTime );
  1523. sampleTime += resampleInterval;
  1524. }
  1525. // If the next sample time is equal to the next log time, move the
  1526. // sample time up one more step since the log time will be added to
  1527. // the list. This guarantees sampleTime is greater than logTime.
  1528. if ( sampleTime == logTime )
  1529. {
  1530. sampleTime += resampleInterval;
  1531. }
  1532. // Add log times until the next sample time is reached or the last log time is
  1533. // hit. If the last log time is added, make the next log time the last time,
  1534. // this ensures the last time will be added even if it is not a sample time.
  1535. while ( logTime < sampleTime )
  1536. {
  1537. Assert( keyTimes.Find( logTime ) == keyTimes.InvalidIndex() );
  1538. keyTimes.AddToTail( logTime );
  1539. if ( iLogTime == logTimes.InvalidIndex() )
  1540. break;
  1541. iLogTime = logTimes.NextInorder( iLogTime );
  1542. if ( iLogTime == logTimes.InvalidIndex() )
  1543. {
  1544. if ( lastTime == logTime )
  1545. break;
  1546. logTime = lastTime;
  1547. }
  1548. else
  1549. {
  1550. logTime = logTimes[ iLogTime ];
  1551. }
  1552. }
  1553. }
  1554. while ( sampleTime <= lastTime );
  1555. Assert( logTime == lastTime );
  1556. Assert( keyTimes.Count() > 0 );
  1557. Assert( keyTimes[ keyTimes.Count() - 1 ] == lastTime );
  1558. for ( int i = 1; i < keyTimes.Count(); ++i )
  1559. {
  1560. Assert( keyTimes[ i - 1 ] != keyTimes[ i ] );
  1561. Assert( keyTimes[ i - 1 ] < keyTimes [ i ] );
  1562. }
  1563. }
  1564. //-------------------------------------------------------------------------------------------------
  1565. // Purpose: Find the position and orientation controls for the specified transform.
  1566. //-------------------------------------------------------------------------------------------------
  1567. CDmeTransformControl *CDmAnimUtils::GetTransformControl( const CDmeDag *pDag )
  1568. {
  1569. if ( pDag == NULL )
  1570. return NULL;
  1571. CUtlVector< CDmeChannel* > channelList( 0, 8 );
  1572. if ( pDag->GetTransform() )
  1573. {
  1574. FindAncestorsReferencingElement( pDag->GetTransform(), channelList );
  1575. }
  1576. CUtlVector< CDmeConstraintSlave* > slaveList( 0, 4 );
  1577. FindAncestorsReferencingElement( pDag, slaveList );
  1578. int nSlaves = slaveList.Count();
  1579. for ( int iSlave = 0; iSlave < nSlaves; ++iSlave )
  1580. {
  1581. CDmeConstraintSlave *pSlave = slaveList[ iSlave ];
  1582. if ( pSlave )
  1583. {
  1584. FindAncestorsReferencingElement( pSlave, channelList );
  1585. }
  1586. }
  1587. int nChannels = channelList.Count();
  1588. for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
  1589. {
  1590. CDmeChannel *pChannel = channelList[ iChannel ];
  1591. if ( pChannel == NULL )
  1592. continue;
  1593. CDmElement *pControl = pChannel->GetFromElement();
  1594. CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pControl );
  1595. if ( pTransformControl )
  1596. {
  1597. return pTransformControl;
  1598. }
  1599. }
  1600. return NULL;
  1601. }
  1602. //-------------------------------------------------------------------------------------------------
  1603. // Purpose: Get the local space default transform for the specified dag node
  1604. //-------------------------------------------------------------------------------------------------
  1605. void CDmAnimUtils::GetDefaultTransform( CDmeDag *pDagNode, matrix3x4_t &defaultTransform )
  1606. {
  1607. CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
  1608. Vector defaultPosition = vec3_origin;
  1609. Quaternion defaultOrientation = quat_identity;
  1610. if ( pTransformControl )
  1611. {
  1612. defaultPosition = pTransformControl->GetDefaultPosition();
  1613. defaultOrientation = pTransformControl->GetDefaultOrientation();
  1614. }
  1615. }
  1616. //-------------------------------------------------------------------------------------------------
  1617. // Purpose: Get the world space default transform for the specified dag node
  1618. //-------------------------------------------------------------------------------------------------
  1619. void CDmAnimUtils::GetDefaultAbsTransform( CDmeDag *pDagNode, matrix3x4_t &absDefaultTransform )
  1620. {
  1621. matrix3x4_t parentToWorld;
  1622. matrix3x4_t localMatrix;
  1623. if ( pDagNode == NULL )
  1624. {
  1625. SetIdentityMatrix( absDefaultTransform );
  1626. return;
  1627. }
  1628. GetDefaultTransform( pDagNode, localMatrix );
  1629. GetDefaultAbsTransform( pDagNode->GetParent(), parentToWorld );
  1630. ConcatTransforms( parentToWorld, localMatrix, absDefaultTransform );
  1631. }
  1632. //-------------------------------------------------------------------------------------------------
  1633. // Purpose: Find and operate all of the channels driving the specified dag nodes
  1634. //-------------------------------------------------------------------------------------------------
  1635. void CDmAnimUtils::OperateDagChannels( const CUtlVector< CDmeDag* > &dagList, ChannelMode_t mode, const DmeLog_TimeSelection_t &timeSelection, CDmeClip *pShot, CDmeClip *pMovie )
  1636. {
  1637. // Get all of the channels driving the selected dag nodes
  1638. CUtlVector< CDmeChannel* > channelList;
  1639. GetChannelsForDags( dagList, channelList );
  1640. // Operate all of the channels in the currently selected mode in order
  1641. // to apply the values from the controls to the dag node transforms.
  1642. if ( mode == CM_RECORD )
  1643. {
  1644. RecordChannels( channelList, timeSelection, pShot, pMovie );
  1645. }
  1646. else
  1647. {
  1648. OperateChannels( channelList, timeSelection, pShot, pMovie );
  1649. }
  1650. }
  1651. //-------------------------------------------------------------------------------------------------
  1652. // Purpose: Get all of the channels directly driving the specified dag nodes
  1653. //-------------------------------------------------------------------------------------------------
  1654. void CDmAnimUtils::GetChannelsForDags( const CUtlVector< CDmeDag* > &dagList, CUtlVector< CDmeChannel* > &channelList )
  1655. {
  1656. int nDagNodes = dagList.Count();
  1657. channelList.EnsureCapacity( channelList.Count() + ( nDagNodes * 2 ) );
  1658. for ( int iNode = 0; iNode < nDagNodes; ++iNode )
  1659. {
  1660. const CDmeDag *pDagNode = dagList[ iNode ];
  1661. if ( pDagNode )
  1662. {
  1663. FindAncestorsReferencingElement( pDagNode->GetTransform(), channelList );
  1664. }
  1665. }
  1666. }
  1667. //-------------------------------------------------------------------------------------------------
  1668. // Purpose: Operate all of the provided channels in the specified mode.
  1669. //-------------------------------------------------------------------------------------------------
  1670. void CDmAnimUtils::OperateChannels( const CUtlVector< CDmeChannel* > &channelList, const DmeLog_TimeSelection_t &timeSelection, CDmeClip *pShot, CDmeClip *pMovie )
  1671. {
  1672. int nChannels = channelList.Count();
  1673. for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
  1674. {
  1675. CDmeChannel *pChannel = channelList[ iChannel ];
  1676. if ( pChannel )
  1677. {
  1678. // Newly created channels may not have the current time, so
  1679. // make sure the time on the channel is correct before operating.
  1680. DmeClipStack_t clipStack;
  1681. pChannel->BuildClipStack( &clipStack, pMovie, pShot );
  1682. DmeTime_t localtime = clipStack.ToChildMediaTime( timeSelection.m_tHeadPosition );
  1683. pChannel->SetCurrentTime( localtime );
  1684. pChannel->Operate();
  1685. }
  1686. }
  1687. }
  1688. //-------------------------------------------------------------------------------------------------
  1689. // Purpose: Operate all of the provided channels in the specified mode.
  1690. //-------------------------------------------------------------------------------------------------
  1691. void CDmAnimUtils::RecordChannels( const CUtlVector< CDmeChannel* > &channelList, const DmeLog_TimeSelection_t &timeSelection, CDmeClip *pShot, CDmeClip *pMovie )
  1692. {
  1693. int nChannels = channelList.Count();
  1694. if ( nChannels == 0 )
  1695. return;
  1696. g_pChannelRecordingMgr->StartModificationLayer( &timeSelection, false );
  1697. g_pChannelRecordingMgr->StartLayerRecording( "Script channel record" );
  1698. for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
  1699. {
  1700. CDmeChannel *pChannel = channelList[ iChannel ];
  1701. if ( pChannel )
  1702. {
  1703. // Newly created channels may not have the current time, so
  1704. // make sure the time on the channel is correct before operating.
  1705. DmeClipStack_t clipStack;
  1706. pChannel->BuildClipStack( &clipStack, pMovie, pShot );
  1707. DmeTime_t localtime = clipStack.ToChildMediaTime( timeSelection.m_tHeadPosition );
  1708. pChannel->SetCurrentTime( localtime );
  1709. TransformWriteMode_t transformWriteMode = g_pChannelRecordingMgr->GetTransformWriteMode();
  1710. g_pChannelRecordingMgr->SetTransformWriteMode( timeSelection.m_TransformWriteMode );
  1711. g_pChannelRecordingMgr->AddChannelToRecordingLayer( pChannel, LOG_COMPONENTS_ALL, pMovie, pShot );
  1712. g_pChannelRecordingMgr->SetTransformWriteMode( transformWriteMode );
  1713. pChannel->Operate();
  1714. }
  1715. }
  1716. g_pChannelRecordingMgr->FinishLayerRecording( timeSelection.m_flThreshold );
  1717. g_pChannelRecordingMgr->FinishModificationLayer();
  1718. }
  1719. //-------------------------------------------------------------------------------------------------
  1720. // Find the shot and the movie to which the specified dag node belongs
  1721. //-------------------------------------------------------------------------------------------------
  1722. void CDmAnimUtils::FindShotAndMovieForDag( const CDmeDag *pDag, CDmeClip *&pShot, CDmeClip *&pMovie )
  1723. {
  1724. pShot = NULL;
  1725. pMovie = NULL;
  1726. if ( pDag == NULL )
  1727. return;
  1728. // First find the root of the dag hierarchy, this should be the scene.
  1729. const CDmeDag *pCurrentDag = pDag;
  1730. const CDmeDag *pParent = pCurrentDag->GetParent();
  1731. while ( pParent )
  1732. {
  1733. pCurrentDag = pParent;
  1734. pParent = pCurrentDag->GetParent();
  1735. }
  1736. // Now find the film clips referencing the scene, take the first one that
  1737. // the scene attribute is the scene dag containing the dag node in question.
  1738. const CDmeDag *pRoot = pCurrentDag;
  1739. CUtlVector< CDmeFilmClip* > filmClipList( 0, 4 );
  1740. FindAncestorsReferencingElement( pRoot, filmClipList );
  1741. int nNumClips = filmClipList.Count();
  1742. for ( int iClip = 0; iClip < nNumClips; ++iClip )
  1743. {
  1744. CDmeFilmClip *pClip = filmClipList[ iClip ];
  1745. if ( pClip == NULL )
  1746. continue;
  1747. if ( pClip->GetScene() == pRoot )
  1748. {
  1749. pShot = pClip;
  1750. break;
  1751. }
  1752. // The active camera does not have to be in the scene
  1753. if ( pRoot == ( const CDmeDag* )( pClip->GetCamera() ) )
  1754. {
  1755. pShot = pClip;
  1756. break;
  1757. }
  1758. }
  1759. // If the shot was not found don't bother with the movie.
  1760. if ( pShot == NULL )
  1761. return;
  1762. // Find all of the tracks that reference the shot. There may be multiple if the shot has been imported into
  1763. // the active session from a session in the misc bin. We want the track that belongs to the active session.
  1764. CUtlVector< CDmeTrack* > trackList;
  1765. FindAncestorsReferencingElement( pShot, trackList );
  1766. int nNumTracks = trackList.Count();
  1767. for ( int iTrack = 0; iTrack < nNumTracks; ++iTrack )
  1768. {
  1769. CDmeTrack *pTrack = trackList[ iTrack ];
  1770. if ( pTrack == NULL )
  1771. continue;
  1772. // The session should be root file element, so we can
  1773. // find the session using the file id of the shot
  1774. CDmElement *pSession = g_pDataModel->GetElement( g_pDataModel->GetFileRoot( pTrack->GetFileId() ) );
  1775. if ( pSession )
  1776. {
  1777. CDmeFilmClip *pClip = pSession->GetValueElement< CDmeFilmClip >( "activeClip" );
  1778. if ( pClip )
  1779. {
  1780. if ( pClip->FindTrackForClip( pShot ) == pTrack )
  1781. {
  1782. pMovie = pClip;
  1783. break;
  1784. }
  1785. }
  1786. }
  1787. }
  1788. }