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.

660 lines
20 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "tier1/convar.h"
  8. #include "jigglebones.h"
  9. #ifdef CLIENT_DLL
  10. #include "engine/ivdebugoverlay.h"
  11. #include "cdll_client_int.h"
  12. #endif
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. //-----------------------------------------------------------------------------
  16. ConVar JiggleBoneDebug( "cl_jiggle_bone_debug", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" );
  17. ConVar JiggleBoneDebugYawConstraints( "cl_jiggle_bone_debug_yaw_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" );
  18. ConVar JiggleBoneDebugPitchConstraints( "cl_jiggle_bone_debug_pitch_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" );
  19. ConVar JiggleBoneInvert( "cl_jiggle_bone_invert", "0", FCVAR_CHEAT );
  20. ConVar JiggleBoneSanity( "cl_jiggle_bone_sanity", "1", 0, "Prevent jiggle bones from pointing directly away from their target in case of numerical instability." );
  21. static int s_id = 2;
  22. #ifdef CLIENT_DLL
  23. char *VarArgs( const char *format, ... );
  24. #else
  25. class CDummyOverlay
  26. {
  27. public:
  28. void AddLineOverlay(const Vector& origin, const Vector& dest, int r, int g, int b, bool noDepthTest, float duration) {};
  29. void AddTextOverlay(const Vector &origin, float duration, const char *format, ...) {}
  30. };
  31. SELECTANY CDummyOverlay *debugoverlay = new CDummyOverlay;
  32. char *VarArgs( const char *format, ... )
  33. {
  34. return NULL;
  35. }
  36. #endif
  37. //-----------------------------------------------------------------------------
  38. JiggleData *CJiggleBones::GetJiggleData( int bone, float currenttime, const Vector &initBasePos, const Vector &initTipPos )
  39. {
  40. FOR_EACH_LL( m_jiggleBoneState, it )
  41. {
  42. if ( m_jiggleBoneState[it].bone == bone )
  43. {
  44. JiggleData *data = &m_jiggleBoneState[it];
  45. if ( !IsFinite( data->lastUpdate ) )
  46. {
  47. Warning( "lastUpdate NaN\n" );
  48. }
  49. if ( !data->basePos.IsValid() )
  50. {
  51. Warning( "basePos NaN\n" );
  52. }
  53. if ( !data->baseLastPos.IsValid() )
  54. {
  55. Warning( "baseLastPos NaN\n" );
  56. }
  57. if ( !data->baseVel.IsValid() )
  58. {
  59. Warning( "baseVel NaN\n" );
  60. }
  61. if ( !data->baseAccel.IsValid() )
  62. {
  63. Warning( "baseAccel NaN\n" );
  64. }
  65. if ( !data->tipPos.IsValid() )
  66. {
  67. Warning( "tipPos NaN\n" );
  68. }
  69. if ( !data->tipVel.IsValid() )
  70. {
  71. Warning( "tipVel NaN\n" );
  72. }
  73. if ( !data->tipAccel.IsValid() )
  74. {
  75. Warning( "tipAccel NaN\n" );
  76. }
  77. return &m_jiggleBoneState[it];
  78. }
  79. }
  80. JiggleData data;
  81. data.Init( bone, currenttime, initBasePos, initTipPos );
  82. data.id = s_id++;
  83. int idx = m_jiggleBoneState.AddToHead( data );
  84. if ( idx == m_jiggleBoneState.InvalidIndex() )
  85. return NULL;
  86. return &m_jiggleBoneState[idx];
  87. }
  88. //-----------------------------------------------------------------------------
  89. /**
  90. * Do spring physics calculations and update "jiggle bone" matrix
  91. * (Michael Booth, Turtle Rock Studios)
  92. */
  93. void CJiggleBones::BuildJiggleTransformations( int boneIndex, float currenttime, const mstudiojigglebone_t *jiggleInfo, const matrix3x4_t &goalMX, matrix3x4_t &boneMX, bool coordSystemIsFlipped )
  94. {
  95. Vector goalBasePosition;
  96. MatrixPosition( goalMX, goalBasePosition );
  97. Vector goalForward, goalUp, goalLeft;
  98. MatrixGetColumn( goalMX, 0, goalLeft );
  99. MatrixGetColumn( goalMX, 1, goalUp );
  100. MatrixGetColumn( goalMX, 2, goalForward );
  101. // compute goal tip position
  102. Vector goalTip = goalBasePosition + jiggleInfo->length * goalForward;
  103. JiggleData *data = GetJiggleData( boneIndex, currenttime, goalBasePosition, goalTip );
  104. if ( !data )
  105. {
  106. return;
  107. }
  108. if ( currenttime - data->lastUpdate > 0.5f )
  109. {
  110. data->Init( boneIndex, currenttime, goalBasePosition, goalTip );
  111. }
  112. if ( JiggleBoneInvert.GetBool() )
  113. {
  114. data->basePos = -data->basePos;
  115. data->baseLastPos = -data->baseLastPos;
  116. data->baseVel = -data->baseVel;
  117. data->baseAccel = -data->baseAccel;
  118. data->tipPos = -data->tipPos;
  119. data->tipVel = -data->tipVel;
  120. data->tipAccel = -data->tipAccel;
  121. }
  122. #ifdef CLIENT_DLL
  123. if ( data->id == JiggleBoneDebug.GetInt() )
  124. {
  125. int line = 20;
  126. engine->Con_NPrintf( line++, "basePos %f %f %f", data->basePos.x, data->basePos.y, data->basePos.z );
  127. engine->Con_NPrintf( line++, "baseLastPos %f %f %f", data->baseLastPos.x, data->baseLastPos.y, data->baseLastPos.z );
  128. engine->Con_NPrintf( line++, "baseVel %f %f %f", data->baseVel.x, data->baseVel.y, data->baseVel.z );
  129. engine->Con_NPrintf( line++, "baseAccel %f %f %f", data->baseAccel.x, data->baseAccel.y, data->baseAccel.z );
  130. engine->Con_NPrintf( line++, "tipPos %f %f %f", data->tipPos.x, data->tipPos.y, data->tipPos.z );
  131. engine->Con_NPrintf( line++, "tipVel %f %f %f", data->tipVel.x, data->tipVel.y, data->tipVel.z );
  132. engine->Con_NPrintf( line++, "tipAccel %f %f %f", data->tipAccel.x, data->tipAccel.y, data->tipAccel.z );
  133. }
  134. #endif
  135. if ( JiggleBoneSanity.GetBool() )
  136. {
  137. Vector goalDir = goalTip - goalBasePosition;
  138. goalDir.NormalizeInPlace();
  139. Vector dataDir = data->tipPos - goalBasePosition;
  140. dataDir.NormalizeInPlace();
  141. float dot = goalDir.Dot( dataDir );
  142. if ( dot < -0.9f ) // if we end up pointing almost completely away, just reset
  143. {
  144. data->Init( boneIndex, currenttime, goalBasePosition, goalTip );
  145. }
  146. }
  147. //Vector bodyVel;
  148. //EstimateAbsVelocity( bodyVel );
  149. // limit maximum deltaT to avoid simulation blowups
  150. // if framerate gets very low, jiggle will run in slow motion
  151. const float thirtyHZ = 0.0333f;
  152. const float thousandHZ = 0.001f;
  153. float deltaT = clamp( currenttime - data->lastUpdate, thousandHZ, thirtyHZ );
  154. data->lastUpdate = currenttime;
  155. //
  156. // Bone tip flex
  157. //
  158. if (jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID))
  159. {
  160. // apply gravity in global space
  161. data->tipAccel.z -= jiggleInfo->tipMass;
  162. if (jiggleInfo->flags & JIGGLE_IS_FLEXIBLE)
  163. {
  164. // We want to run the simulation for this part of the jiggle with smaller time steps.
  165. static const float MIN_DELTA_T_PER_ITERATION = 1.0f / 120.0f;
  166. for (float deltaTLeft = deltaT; deltaTLeft > 0.0f; deltaTLeft -= MIN_DELTA_T_PER_ITERATION )
  167. {
  168. float usableDeltaT = deltaTLeft > MIN_DELTA_T_PER_ITERATION ? MIN_DELTA_T_PER_ITERATION : deltaTLeft;
  169. // decompose into local coordinates
  170. Vector error = goalTip - data->tipPos;
  171. Vector localError;
  172. localError.x = DotProduct( goalLeft, error );
  173. localError.y = DotProduct( goalUp, error );
  174. localError.z = DotProduct( goalForward, error );
  175. Vector localVel;
  176. localVel.x = DotProduct( goalLeft, data->tipVel );
  177. localVel.y = DotProduct( goalUp, data->tipVel );
  178. localVel.z = 0.0f; // TODO: this was uninitialized, but is being used
  179. // yaw spring
  180. float yawAccel = jiggleInfo->yawStiffness * localError.x - jiggleInfo->yawDamping * localVel.x;
  181. // pitch spring
  182. float pitchAccel = jiggleInfo->pitchStiffness * localError.y - jiggleInfo->pitchDamping * localVel.y;
  183. if (jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT)
  184. {
  185. // drive tip towards goal tip position
  186. data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp;
  187. }
  188. else
  189. {
  190. // allow flex along length of spring
  191. localVel.z = DotProduct( goalForward, data->tipVel );
  192. // along spring
  193. float alongAccel = jiggleInfo->alongStiffness * localError.z - jiggleInfo->alongDamping * localVel.z;
  194. // drive tip towards goal tip position
  195. data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp + alongAccel * goalForward;
  196. }
  197. // simple euler integration
  198. data->tipVel += data->tipAccel * usableDeltaT;
  199. data->tipPos += data->tipVel * usableDeltaT;
  200. }
  201. }
  202. // clear this timestep's accumulated accelerations
  203. data->tipAccel = vec3_origin;
  204. //
  205. // Apply optional constraints
  206. //
  207. if (jiggleInfo->flags & (JIGGLE_HAS_YAW_CONSTRAINT | JIGGLE_HAS_PITCH_CONSTRAINT))
  208. {
  209. // find components of spring vector in local coordinate system
  210. Vector along = data->tipPos - goalBasePosition;
  211. Vector localAlong;
  212. localAlong.x = DotProduct( goalLeft, along );
  213. localAlong.y = DotProduct( goalUp, along );
  214. localAlong.z = DotProduct( goalForward, along );
  215. Vector localVel;
  216. localVel.x = DotProduct( goalLeft, data->tipVel );
  217. localVel.y = DotProduct( goalUp, data->tipVel );
  218. localVel.z = DotProduct( goalForward, data->tipVel );
  219. if (jiggleInfo->flags & JIGGLE_HAS_YAW_CONSTRAINT)
  220. {
  221. // enforce yaw constraints in local XZ plane
  222. float yawError = atan2( localAlong.x, localAlong.z );
  223. bool isAtLimit = false;
  224. float yaw = 0.0f;
  225. if (yawError < jiggleInfo->minYaw)
  226. {
  227. // at angular limit
  228. isAtLimit = true;
  229. yaw = jiggleInfo->minYaw;
  230. }
  231. else if (yawError > jiggleInfo->maxYaw)
  232. {
  233. // at angular limit
  234. isAtLimit = true;
  235. yaw = jiggleInfo->maxYaw;
  236. }
  237. if (isAtLimit)
  238. {
  239. float sy, cy;
  240. SinCos( yaw, &sy, &cy );
  241. // yaw matrix
  242. matrix3x4_t yawMatrix;
  243. yawMatrix[0][0] = cy;
  244. yawMatrix[1][0] = 0;
  245. yawMatrix[2][0] = -sy;
  246. yawMatrix[0][1] = 0;
  247. yawMatrix[1][1] = 1.0f;
  248. yawMatrix[2][1] = 0;
  249. yawMatrix[0][2] = sy;
  250. yawMatrix[1][2] = 0;
  251. yawMatrix[2][2] = cy;
  252. yawMatrix[0][3] = 0;
  253. yawMatrix[1][3] = 0;
  254. yawMatrix[2][3] = 0;
  255. // global coordinates of limit
  256. matrix3x4_t limitMatrix;
  257. ConcatTransforms( goalMX, yawMatrix, limitMatrix );
  258. Vector limitLeft( limitMatrix.m_flMatVal[0][0],
  259. limitMatrix.m_flMatVal[1][0],
  260. limitMatrix.m_flMatVal[2][0] );
  261. Vector limitUp( limitMatrix.m_flMatVal[0][1],
  262. limitMatrix.m_flMatVal[1][1],
  263. limitMatrix.m_flMatVal[2][1] );
  264. Vector limitForward( limitMatrix.m_flMatVal[0][2],
  265. limitMatrix.m_flMatVal[1][2],
  266. limitMatrix.m_flMatVal[2][2] );
  267. if (JiggleBoneDebugYawConstraints.GetBool())
  268. {
  269. float dT = 0.01f;
  270. const float axisSize = 10.0f;
  271. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT );
  272. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT );
  273. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT );
  274. }
  275. Vector limitAlong( DotProduct( limitLeft, along ),
  276. DotProduct( limitUp, along ),
  277. DotProduct( limitForward, along ) );
  278. // clip to limit plane
  279. data->tipPos = goalBasePosition + limitAlong.y * limitUp + limitAlong.z * limitForward;
  280. // yaw friction - rubbing along limit plane
  281. Vector limitVel;
  282. limitVel.x = 0.0f; // TODO: this was uninitialized, and is used when yawBounce is non-zero!
  283. limitVel.y = DotProduct( limitUp, data->tipVel );
  284. limitVel.z = DotProduct( limitForward, data->tipVel );
  285. data->tipAccel -= jiggleInfo->yawFriction * (limitVel.y * limitUp + limitVel.z * limitForward);
  286. // update velocity reaction to hitting constraint
  287. data->tipVel = -jiggleInfo->yawBounce * limitVel.x * limitLeft + limitVel.y * limitUp + limitVel.z * limitForward;
  288. // update along vectors for use by pitch constraint
  289. along = data->tipPos - goalBasePosition;
  290. localAlong.x = DotProduct( goalLeft, along );
  291. localAlong.y = DotProduct( goalUp, along );
  292. localAlong.z = DotProduct( goalForward, along );
  293. localVel.x = DotProduct( goalLeft, data->tipVel );
  294. localVel.y = DotProduct( goalUp, data->tipVel );
  295. localVel.z = DotProduct( goalForward, data->tipVel );
  296. }
  297. }
  298. if (jiggleInfo->flags & JIGGLE_HAS_PITCH_CONSTRAINT)
  299. {
  300. // enforce pitch constraints in local YZ plane
  301. float pitchError = atan2( localAlong.y, localAlong.z );
  302. bool isAtLimit = false;
  303. float pitch = 0.0f;
  304. if (pitchError < jiggleInfo->minPitch)
  305. {
  306. // at angular limit
  307. isAtLimit = true;
  308. pitch = jiggleInfo->minPitch;
  309. }
  310. else if (pitchError > jiggleInfo->maxPitch)
  311. {
  312. // at angular limit
  313. isAtLimit = true;
  314. pitch = jiggleInfo->maxPitch;
  315. }
  316. if (isAtLimit)
  317. {
  318. float sp, cp;
  319. SinCos( pitch, &sp, &cp );
  320. // pitch matrix
  321. matrix3x4_t pitchMatrix;
  322. pitchMatrix[0][0] = 1.0f;
  323. pitchMatrix[1][0] = 0;
  324. pitchMatrix[2][0] = 0;
  325. pitchMatrix[0][1] = 0;
  326. pitchMatrix[1][1] = cp;
  327. pitchMatrix[2][1] = -sp;
  328. pitchMatrix[0][2] = 0;
  329. pitchMatrix[1][2] = sp;
  330. pitchMatrix[2][2] = cp;
  331. pitchMatrix[0][3] = 0;
  332. pitchMatrix[1][3] = 0;
  333. pitchMatrix[2][3] = 0;
  334. // global coordinates of limit
  335. matrix3x4_t limitMatrix;
  336. ConcatTransforms( goalMX, pitchMatrix, limitMatrix );
  337. Vector limitLeft( limitMatrix.m_flMatVal[0][0],
  338. limitMatrix.m_flMatVal[1][0],
  339. limitMatrix.m_flMatVal[2][0] );
  340. Vector limitUp( limitMatrix.m_flMatVal[0][1],
  341. limitMatrix.m_flMatVal[1][1],
  342. limitMatrix.m_flMatVal[2][1] );
  343. Vector limitForward( limitMatrix.m_flMatVal[0][2],
  344. limitMatrix.m_flMatVal[1][2],
  345. limitMatrix.m_flMatVal[2][2] );
  346. if (JiggleBoneDebugPitchConstraints.GetBool())
  347. {
  348. float dT = 0.01f;
  349. const float axisSize = 10.0f;
  350. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT );
  351. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT );
  352. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT );
  353. }
  354. Vector limitAlong( DotProduct( limitLeft, along ),
  355. DotProduct( limitUp, along ),
  356. DotProduct( limitForward, along ) );
  357. // clip to limit plane
  358. data->tipPos = goalBasePosition + limitAlong.x * limitLeft + limitAlong.z * limitForward;
  359. // pitch friction - rubbing along limit plane
  360. Vector limitVel;
  361. limitVel.x = 0.0f; // TODO: this was uninitialized, but is being used
  362. limitVel.y = DotProduct( limitUp, data->tipVel );
  363. limitVel.z = DotProduct( limitForward, data->tipVel );
  364. data->tipAccel -= jiggleInfo->pitchFriction * (limitVel.x * limitLeft + limitVel.z * limitForward);
  365. // update velocity reaction to hitting constraint
  366. data->tipVel = limitVel.x * limitLeft - jiggleInfo->pitchBounce * limitVel.y * limitUp + limitVel.z * limitForward;
  367. }
  368. }
  369. }
  370. // needed for matrix assembly below
  371. Vector forward = data->tipPos - goalBasePosition;
  372. forward.NormalizeInPlace();
  373. if (jiggleInfo->flags & JIGGLE_HAS_ANGLE_CONSTRAINT)
  374. {
  375. // enforce max angular error
  376. Vector error = goalTip - data->tipPos;
  377. float dot = DotProduct( forward, goalForward );
  378. float angleBetween = acos( dot );
  379. if (dot < 0.0f)
  380. {
  381. angleBetween = 2.0f * M_PI - angleBetween;
  382. }
  383. if (angleBetween > jiggleInfo->angleLimit)
  384. {
  385. // at angular limit
  386. float maxBetween = jiggleInfo->length * sin( jiggleInfo->angleLimit );
  387. Vector delta = goalTip - data->tipPos;
  388. delta.NormalizeInPlace();
  389. data->tipPos = goalTip - maxBetween * delta;
  390. forward = data->tipPos - goalBasePosition;
  391. forward.NormalizeInPlace();
  392. }
  393. }
  394. if (jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT)
  395. {
  396. // enforce spring length
  397. data->tipPos = goalBasePosition + jiggleInfo->length * forward;
  398. // zero velocity along forward bone axis
  399. data->tipVel -= DotProduct( data->tipVel, forward ) * forward;
  400. }
  401. //
  402. // Build bone matrix to align along current tip direction
  403. //
  404. Vector left, up;
  405. if ( coordSystemIsFlipped )
  406. {
  407. // If the coordinate system is flipped, use left handed rules.
  408. left = CrossProduct( forward, goalUp );
  409. left.NormalizeInPlace();
  410. up = CrossProduct( left, forward );
  411. }
  412. else
  413. {
  414. left = CrossProduct( goalUp, forward );
  415. left.NormalizeInPlace();
  416. up = CrossProduct( forward, left );
  417. }
  418. boneMX[0][0] = left.x;
  419. boneMX[1][0] = left.y;
  420. boneMX[2][0] = left.z;
  421. boneMX[0][1] = up.x;
  422. boneMX[1][1] = up.y;
  423. boneMX[2][1] = up.z;
  424. boneMX[0][2] = forward.x;
  425. boneMX[1][2] = forward.y;
  426. boneMX[2][2] = forward.z;
  427. boneMX[0][3] = goalBasePosition.x;
  428. boneMX[1][3] = goalBasePosition.y;
  429. boneMX[2][3] = goalBasePosition.z;
  430. }
  431. //
  432. // Bone base flex
  433. //
  434. if (jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING)
  435. {
  436. // gravity
  437. data->baseAccel.z -= jiggleInfo->baseMass;
  438. // simple spring
  439. Vector error = goalBasePosition - data->basePos;
  440. data->baseAccel += jiggleInfo->baseStiffness * error - jiggleInfo->baseDamping * data->baseVel;
  441. data->baseVel += data->baseAccel * deltaT;
  442. data->basePos += data->baseVel * deltaT;
  443. // clear this timestep's accumulated accelerations
  444. data->baseAccel = vec3_origin;
  445. // constrain to limits
  446. error = data->basePos - goalBasePosition;
  447. Vector localError;
  448. localError.x = DotProduct( goalLeft, error );
  449. localError.y = DotProduct( goalUp, error );
  450. localError.z = DotProduct( goalForward, error );
  451. Vector localVel;
  452. localVel.x = DotProduct( goalLeft, data->baseVel );
  453. localVel.y = DotProduct( goalUp, data->baseVel );
  454. localVel.z = DotProduct( goalForward, data->baseVel );
  455. // horizontal constraint
  456. if (localError.x < jiggleInfo->baseMinLeft)
  457. {
  458. localError.x = jiggleInfo->baseMinLeft;
  459. // friction
  460. data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward);
  461. }
  462. else if (localError.x > jiggleInfo->baseMaxLeft)
  463. {
  464. localError.x = jiggleInfo->baseMaxLeft;
  465. // friction
  466. data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward);
  467. }
  468. if (localError.y < jiggleInfo->baseMinUp)
  469. {
  470. localError.y = jiggleInfo->baseMinUp;
  471. // friction
  472. data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward);
  473. }
  474. else if (localError.y > jiggleInfo->baseMaxUp)
  475. {
  476. localError.y = jiggleInfo->baseMaxUp;
  477. // friction
  478. data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward);
  479. }
  480. if (localError.z < jiggleInfo->baseMinForward)
  481. {
  482. localError.z = jiggleInfo->baseMinForward;
  483. // friction
  484. data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp);
  485. }
  486. else if (localError.z > jiggleInfo->baseMaxForward)
  487. {
  488. localError.z = jiggleInfo->baseMaxForward;
  489. // friction
  490. data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp);
  491. }
  492. data->basePos = goalBasePosition + localError.x * goalLeft + localError.y * goalUp + localError.z * goalForward;
  493. // fix up velocity
  494. data->baseVel = (data->basePos - data->baseLastPos) / deltaT;
  495. data->baseLastPos = data->basePos;
  496. if (!(jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID)))
  497. {
  498. // no tip flex - use bone's goal orientation
  499. boneMX = goalMX;
  500. }
  501. // update bone position
  502. MatrixSetColumn( data->basePos, 3, boneMX );
  503. }
  504. else if (!(jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID)))
  505. {
  506. // no flex at all - just use goal matrix
  507. boneMX = goalMX;
  508. }
  509. // debug display
  510. if ( JiggleBoneDebug.GetInt() == 1 || JiggleBoneDebug.GetInt() == data->id )
  511. {
  512. float dT = 0.01f;
  513. const float axisSize = 5.0f;
  514. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalLeft, 255, 0, 0, true, dT );
  515. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalUp, 0, 255, 0, true, dT );
  516. debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalForward, 0, 0, 255, true, dT );
  517. debugoverlay->AddTextOverlay( goalBasePosition, dT, "%d", data->id );
  518. const float sz = 1.0f;
  519. if (jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID))
  520. {
  521. debugoverlay->AddLineOverlay( goalBasePosition,
  522. data->tipPos, 255, 255, 0, true, dT );
  523. debugoverlay->AddLineOverlay( data->tipPos + Vector( -sz, 0, 0 ),
  524. data->tipPos + Vector( sz, 0, 0 ), 0, 255, 255, true, dT );
  525. debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, -sz, 0 ),
  526. data->tipPos + Vector( 0, sz, 0 ), 0, 255, 255, true, dT );
  527. debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, 0, -sz ),
  528. data->tipPos + Vector( 0, 0, sz ), 0, 255, 255, true, dT );
  529. }
  530. if (jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING)
  531. {
  532. debugoverlay->AddLineOverlay( data->basePos + Vector( -sz, 0, 0 ),
  533. data->basePos + Vector( sz, 0, 0 ), 255, 0, 255, true, dT );
  534. debugoverlay->AddLineOverlay( data->basePos + Vector( 0, -sz, 0 ),
  535. data->basePos + Vector( 0, sz, 0 ), 255, 0, 255, true, dT );
  536. debugoverlay->AddLineOverlay( data->basePos + Vector( 0, 0, -sz ),
  537. data->basePos + Vector( 0, 0, sz ), 255, 0, 255, true, dT );
  538. }
  539. }
  540. }