AF.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. /*
  24. ===============================================================================
  25. Articulated figure controller.
  26. ===============================================================================
  27. */
  28. #define ARTICULATED_FIGURE_ANIM "af_pose"
  29. #define POSE_BOUNDS_EXPANSION 5.0f
  30. /*
  31. ================
  32. idAF::idAF
  33. ================
  34. */
  35. idAF::idAF( void ) {
  36. self = NULL;
  37. animator = NULL;
  38. modifiedAnim = 0;
  39. baseOrigin.Zero();
  40. baseAxis.Identity();
  41. poseTime = -1;
  42. restStartTime = -1;
  43. isLoaded = false;
  44. isActive = false;
  45. hasBindConstraints = false;
  46. }
  47. /*
  48. ================
  49. idAF::~idAF
  50. ================
  51. */
  52. idAF::~idAF( void ) {
  53. }
  54. /*
  55. ================
  56. idAF::Save
  57. ================
  58. */
  59. void idAF::Save( idSaveGame *savefile ) const {
  60. savefile->WriteObject( self );
  61. savefile->WriteString( GetName() );
  62. savefile->WriteBool( hasBindConstraints );
  63. savefile->WriteVec3( baseOrigin );
  64. savefile->WriteMat3( baseAxis );
  65. savefile->WriteInt( poseTime );
  66. savefile->WriteInt( restStartTime );
  67. savefile->WriteBool( isLoaded );
  68. savefile->WriteBool( isActive );
  69. savefile->WriteStaticObject( physicsObj );
  70. }
  71. /*
  72. ================
  73. idAF::Restore
  74. ================
  75. */
  76. void idAF::Restore( idRestoreGame *savefile ) {
  77. savefile->ReadObject( reinterpret_cast<idClass *&>( self ) );
  78. savefile->ReadString( name );
  79. savefile->ReadBool( hasBindConstraints );
  80. savefile->ReadVec3( baseOrigin );
  81. savefile->ReadMat3( baseAxis );
  82. savefile->ReadInt( poseTime );
  83. savefile->ReadInt( restStartTime );
  84. savefile->ReadBool( isLoaded );
  85. savefile->ReadBool( isActive );
  86. animator = NULL;
  87. modifiedAnim = 0;
  88. if ( self ) {
  89. SetAnimator( self->GetAnimator() );
  90. Load( self, name );
  91. if ( hasBindConstraints ) {
  92. AddBindConstraints();
  93. }
  94. }
  95. savefile->ReadStaticObject( physicsObj );
  96. if ( self ) {
  97. if ( isActive ) {
  98. // clear all animations
  99. animator->ClearAllAnims( gameLocal.time, 0 );
  100. animator->ClearAllJoints();
  101. // switch to articulated figure physics
  102. self->RestorePhysics( &physicsObj );
  103. physicsObj.EnableClip();
  104. }
  105. UpdateAnimation();
  106. }
  107. }
  108. /*
  109. ================
  110. idAF::UpdateAnimation
  111. ================
  112. */
  113. bool idAF::UpdateAnimation( void ) {
  114. int i;
  115. idVec3 origin, renderOrigin, bodyOrigin;
  116. idMat3 axis, renderAxis, bodyAxis;
  117. renderEntity_t *renderEntity;
  118. if ( !IsLoaded() ) {
  119. return false;
  120. }
  121. if ( !IsActive() ) {
  122. return false;
  123. }
  124. renderEntity = self->GetRenderEntity();
  125. if ( !renderEntity ) {
  126. return false;
  127. }
  128. if ( physicsObj.IsAtRest() ) {
  129. if ( restStartTime == physicsObj.GetRestStartTime() ) {
  130. return false;
  131. }
  132. restStartTime = physicsObj.GetRestStartTime();
  133. }
  134. // get the render position
  135. origin = physicsObj.GetOrigin( 0 );
  136. axis = physicsObj.GetAxis( 0 );
  137. renderAxis = baseAxis.Transpose() * axis;
  138. renderOrigin = origin - baseOrigin * renderAxis;
  139. // create an animation frame which reflects the current pose of the articulated figure
  140. animator->InitAFPose();
  141. for ( i = 0; i < jointMods.Num(); i++ ) {
  142. // check for the origin joint
  143. if ( jointMods[i].jointHandle == 0 ) {
  144. continue;
  145. }
  146. bodyOrigin = physicsObj.GetOrigin( jointMods[i].bodyId );
  147. bodyAxis = physicsObj.GetAxis( jointMods[i].bodyId );
  148. axis = jointMods[i].jointBodyAxis.Transpose() * ( bodyAxis * renderAxis.Transpose() );
  149. origin = ( bodyOrigin - jointMods[i].jointBodyOrigin * axis - renderOrigin ) * renderAxis.Transpose();
  150. animator->SetAFPoseJointMod( jointMods[i].jointHandle, jointMods[i].jointMod, axis, origin );
  151. }
  152. animator->FinishAFPose( modifiedAnim, GetBounds().Expand( POSE_BOUNDS_EXPANSION ), gameLocal.time );
  153. animator->SetAFPoseBlendWeight( 1.0f );
  154. return true;
  155. }
  156. /*
  157. ================
  158. idAF::GetBounds
  159. returns bounds for the current pose
  160. ================
  161. */
  162. idBounds idAF::GetBounds( void ) const {
  163. int i;
  164. idAFBody *body;
  165. idVec3 origin, entityOrigin;
  166. idMat3 axis, entityAxis;
  167. idBounds bounds, b;
  168. bounds.Clear();
  169. // get model base transform
  170. origin = physicsObj.GetOrigin( 0 );
  171. axis = physicsObj.GetAxis( 0 );
  172. entityAxis = baseAxis.Transpose() * axis;
  173. entityOrigin = origin - baseOrigin * entityAxis;
  174. // get bounds relative to base
  175. for ( i = 0; i < jointMods.Num(); i++ ) {
  176. body = physicsObj.GetBody( jointMods[i].bodyId );
  177. origin = ( body->GetWorldOrigin() - entityOrigin ) * entityAxis.Transpose();
  178. axis = body->GetWorldAxis() * entityAxis.Transpose();
  179. b.FromTransformedBounds( body->GetClipModel()->GetBounds(), origin, axis );
  180. bounds += b;
  181. }
  182. return bounds;
  183. }
  184. /*
  185. ================
  186. idAF::SetupPose
  187. Transforms the articulated figure to match the current animation pose of the given entity.
  188. ================
  189. */
  190. void idAF::SetupPose( idEntity *ent, int time ) {
  191. int i;
  192. idAFBody *body;
  193. idVec3 origin;
  194. idMat3 axis;
  195. idAnimator *animatorPtr;
  196. renderEntity_t *renderEntity;
  197. if ( !IsLoaded() || !ent ) {
  198. return;
  199. }
  200. animatorPtr = ent->GetAnimator();
  201. if ( !animatorPtr ) {
  202. return;
  203. }
  204. renderEntity = ent->GetRenderEntity();
  205. if ( !renderEntity ) {
  206. return;
  207. }
  208. // if the animation is driven by the physics
  209. if ( self->GetPhysics() == &physicsObj ) {
  210. return;
  211. }
  212. // if the pose was already updated this frame
  213. if ( poseTime == time ) {
  214. return;
  215. }
  216. poseTime = time;
  217. for ( i = 0; i < jointMods.Num(); i++ ) {
  218. body = physicsObj.GetBody( jointMods[i].bodyId );
  219. animatorPtr->GetJointTransform( jointMods[i].jointHandle, time, origin, axis );
  220. body->SetWorldOrigin( renderEntity->origin + ( origin + jointMods[i].jointBodyOrigin * axis ) * renderEntity->axis );
  221. body->SetWorldAxis( jointMods[i].jointBodyAxis * axis * renderEntity->axis );
  222. }
  223. if ( isActive ) {
  224. physicsObj.UpdateClipModels();
  225. }
  226. }
  227. /*
  228. ================
  229. idAF::ChangePose
  230. Change the articulated figure to match the current animation pose of the given entity
  231. and set the velocity relative to the previous pose.
  232. ================
  233. */
  234. void idAF::ChangePose( idEntity *ent, int time ) {
  235. int i;
  236. float invDelta;
  237. idAFBody *body;
  238. idVec3 origin, lastOrigin;
  239. idMat3 axis;
  240. idAnimator *animatorPtr;
  241. renderEntity_t *renderEntity;
  242. if ( !IsLoaded() || !ent ) {
  243. return;
  244. }
  245. animatorPtr = ent->GetAnimator();
  246. if ( !animatorPtr ) {
  247. return;
  248. }
  249. renderEntity = ent->GetRenderEntity();
  250. if ( !renderEntity ) {
  251. return;
  252. }
  253. // if the animation is driven by the physics
  254. if ( self->GetPhysics() == &physicsObj ) {
  255. return;
  256. }
  257. // if the pose was already updated this frame
  258. if ( poseTime == time ) {
  259. return;
  260. }
  261. invDelta = 1.0f / MS2SEC( time - poseTime );
  262. poseTime = time;
  263. for ( i = 0; i < jointMods.Num(); i++ ) {
  264. body = physicsObj.GetBody( jointMods[i].bodyId );
  265. animatorPtr->GetJointTransform( jointMods[i].jointHandle, time, origin, axis );
  266. lastOrigin = body->GetWorldOrigin();
  267. body->SetWorldOrigin( renderEntity->origin + ( origin + jointMods[i].jointBodyOrigin * axis ) * renderEntity->axis );
  268. body->SetWorldAxis( jointMods[i].jointBodyAxis * axis * renderEntity->axis );
  269. body->SetLinearVelocity( ( body->GetWorldOrigin() - lastOrigin ) * invDelta );
  270. }
  271. physicsObj.UpdateClipModels();
  272. }
  273. /*
  274. ================
  275. idAF::EntitiesTouchingAF
  276. ================
  277. */
  278. int idAF::EntitiesTouchingAF( afTouch_t touchList[ MAX_GENTITIES ] ) const {
  279. int i, j, numClipModels;
  280. idAFBody *body;
  281. idClipModel *cm;
  282. idClipModel *clipModels[ MAX_GENTITIES ];
  283. int numTouching;
  284. if ( !IsLoaded() ) {
  285. return 0;
  286. }
  287. numTouching = 0;
  288. numClipModels = gameLocal.clip.ClipModelsTouchingBounds( physicsObj.GetAbsBounds(), -1, clipModels, MAX_GENTITIES );
  289. for ( i = 0; i < jointMods.Num(); i++ ) {
  290. body = physicsObj.GetBody( jointMods[i].bodyId );
  291. for ( j = 0; j < numClipModels; j++ ) {
  292. cm = clipModels[j];
  293. if ( !cm || cm->GetEntity() == self ) {
  294. continue;
  295. }
  296. if ( !cm->IsTraceModel() ) {
  297. continue;
  298. }
  299. if ( !body->GetClipModel()->GetAbsBounds().IntersectsBounds( cm->GetAbsBounds() ) ) {
  300. continue;
  301. }
  302. if ( gameLocal.clip.ContentsModel( body->GetWorldOrigin(), body->GetClipModel(), body->GetWorldAxis(), -1, cm->Handle(), cm->GetOrigin(), cm->GetAxis() ) ) {
  303. touchList[ numTouching ].touchedByBody = body;
  304. touchList[ numTouching ].touchedClipModel = cm;
  305. touchList[ numTouching ].touchedEnt = cm->GetEntity();
  306. numTouching++;
  307. clipModels[j] = NULL;
  308. }
  309. }
  310. }
  311. return numTouching;
  312. }
  313. /*
  314. ================
  315. idAF::BodyForClipModelId
  316. ================
  317. */
  318. int idAF::BodyForClipModelId( int id ) const {
  319. if ( id >= 0 ) {
  320. return id;
  321. } else {
  322. id = CLIPMODEL_ID_TO_JOINT_HANDLE( id );
  323. if ( id < jointBody.Num() ) {
  324. return jointBody[id];
  325. } else {
  326. return 0;
  327. }
  328. }
  329. }
  330. /*
  331. ================
  332. idAF::GetPhysicsToVisualTransform
  333. ================
  334. */
  335. void idAF::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) const {
  336. origin = - baseOrigin;
  337. axis = baseAxis.Transpose();
  338. }
  339. /*
  340. ================
  341. idAF::GetImpactInfo
  342. ================
  343. */
  344. void idAF::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
  345. SetupPose( self, gameLocal.time );
  346. physicsObj.GetImpactInfo( BodyForClipModelId( id ), point, info );
  347. }
  348. /*
  349. ================
  350. idAF::ApplyImpulse
  351. ================
  352. */
  353. void idAF::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
  354. SetupPose( self, gameLocal.time );
  355. physicsObj.ApplyImpulse( BodyForClipModelId( id ), point, impulse );
  356. }
  357. /*
  358. ================
  359. idAF::AddForce
  360. ================
  361. */
  362. void idAF::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
  363. SetupPose( self, gameLocal.time );
  364. physicsObj.AddForce( BodyForClipModelId( id ), point, force );
  365. }
  366. /*
  367. ================
  368. idAF::AddBody
  369. Adds a body.
  370. ================
  371. */
  372. void idAF::AddBody( idAFBody *body, const idJointMat *joints, const char *jointName, const AFJointModType_t mod ) {
  373. int index;
  374. jointHandle_t handle;
  375. idVec3 origin;
  376. idMat3 axis;
  377. handle = animator->GetJointHandle( jointName );
  378. if ( handle == INVALID_JOINT ) {
  379. gameLocal.Error( "idAF for entity '%s' at (%s) modifies unknown joint '%s'", self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0), jointName );
  380. }
  381. assert( handle < animator->NumJoints() );
  382. origin = joints[ handle ].ToVec3();
  383. axis = joints[ handle ].ToMat3();
  384. index = jointMods.Num();
  385. jointMods.SetNum( index + 1, false );
  386. jointMods[index].bodyId = physicsObj.GetBodyId( body );
  387. jointMods[index].jointHandle = handle;
  388. jointMods[index].jointMod = mod;
  389. jointMods[index].jointBodyOrigin = ( body->GetWorldOrigin() - origin ) * axis.Transpose();
  390. jointMods[index].jointBodyAxis = body->GetWorldAxis() * axis.Transpose();
  391. }
  392. /*
  393. ================
  394. idAF::SetBase
  395. Sets the base body.
  396. ================
  397. */
  398. void idAF::SetBase( idAFBody *body, const idJointMat *joints ) {
  399. physicsObj.ForceBodyId( body, 0 );
  400. baseOrigin = body->GetWorldOrigin();
  401. baseAxis = body->GetWorldAxis();
  402. AddBody( body, joints, animator->GetJointName( animator->GetFirstChild( "origin" ) ), AF_JOINTMOD_AXIS );
  403. }
  404. /*
  405. ================
  406. idAF::LoadBody
  407. ================
  408. */
  409. bool idAF::LoadBody( const idDeclAF_Body *fb, const idJointMat *joints ) {
  410. int id, i;
  411. float length, mass;
  412. idTraceModel trm;
  413. idClipModel *clip;
  414. idAFBody *body;
  415. idMat3 axis, inertiaTensor;
  416. idVec3 centerOfMass, origin;
  417. idBounds bounds;
  418. idList<jointHandle_t> jointList;
  419. origin = fb->origin.ToVec3();
  420. axis = fb->angles.ToMat3();
  421. bounds[0] = fb->v1.ToVec3();
  422. bounds[1] = fb->v2.ToVec3();
  423. switch( fb->modelType ) {
  424. case TRM_BOX: {
  425. trm.SetupBox( bounds );
  426. break;
  427. }
  428. case TRM_OCTAHEDRON: {
  429. trm.SetupOctahedron( bounds );
  430. break;
  431. }
  432. case TRM_DODECAHEDRON: {
  433. trm.SetupDodecahedron( bounds );
  434. break;
  435. }
  436. case TRM_CYLINDER: {
  437. trm.SetupCylinder( bounds, fb->numSides );
  438. break;
  439. }
  440. case TRM_CONE: {
  441. // place the apex at the origin
  442. bounds[0].z -= bounds[1].z;
  443. bounds[1].z = 0.0f;
  444. trm.SetupCone( bounds, fb->numSides );
  445. break;
  446. }
  447. case TRM_BONE: {
  448. // direction of bone
  449. axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
  450. length = axis[2].Normalize();
  451. // axis of bone trace model
  452. axis[2].NormalVectors( axis[0], axis[1] );
  453. axis[1] = -axis[1];
  454. // create bone trace model
  455. trm.SetupBone( length, fb->width );
  456. break;
  457. }
  458. default:
  459. assert( 0 );
  460. break;
  461. }
  462. trm.GetMassProperties( 1.0f, mass, centerOfMass, inertiaTensor );
  463. trm.Translate( -centerOfMass );
  464. origin += centerOfMass * axis;
  465. body = physicsObj.GetBody( fb->name );
  466. if ( body ) {
  467. clip = body->GetClipModel();
  468. if ( !clip->IsEqual( trm ) ) {
  469. clip = new idClipModel( trm );
  470. clip->SetContents( fb->contents );
  471. clip->Link( gameLocal.clip, self, 0, origin, axis );
  472. body->SetClipModel( clip );
  473. }
  474. clip->SetContents( fb->contents );
  475. body->SetDensity( fb->density, fb->inertiaScale );
  476. body->SetWorldOrigin( origin );
  477. body->SetWorldAxis( axis );
  478. id = physicsObj.GetBodyId( body );
  479. }
  480. else {
  481. clip = new idClipModel( trm );
  482. clip->SetContents( fb->contents );
  483. clip->Link( gameLocal.clip, self, 0, origin, axis );
  484. body = new idAFBody( fb->name, clip, fb->density );
  485. if ( fb->inertiaScale != mat3_identity ) {
  486. body->SetDensity( fb->density, fb->inertiaScale );
  487. }
  488. id = physicsObj.AddBody( body );
  489. }
  490. if ( fb->linearFriction != -1.0f ) {
  491. body->SetFriction( fb->linearFriction, fb->angularFriction, fb->contactFriction );
  492. }
  493. body->SetClipMask( fb->clipMask );
  494. body->SetSelfCollision( fb->selfCollision );
  495. if ( fb->jointName == "origin" ) {
  496. SetBase( body, joints );
  497. } else {
  498. AFJointModType_t mod;
  499. if ( fb->jointMod == DECLAF_JOINTMOD_AXIS ) {
  500. mod = AF_JOINTMOD_AXIS;
  501. } else if ( fb->jointMod == DECLAF_JOINTMOD_ORIGIN ) {
  502. mod = AF_JOINTMOD_ORIGIN;
  503. } else if ( fb->jointMod == DECLAF_JOINTMOD_BOTH ) {
  504. mod = AF_JOINTMOD_BOTH;
  505. } else {
  506. mod = AF_JOINTMOD_AXIS;
  507. }
  508. AddBody( body, joints, fb->jointName, mod );
  509. }
  510. if ( fb->frictionDirection.ToVec3() != vec3_origin ) {
  511. body->SetFrictionDirection( fb->frictionDirection.ToVec3() );
  512. }
  513. if ( fb->contactMotorDirection.ToVec3() != vec3_origin ) {
  514. body->SetContactMotorDirection( fb->contactMotorDirection.ToVec3() );
  515. }
  516. // update table to find the nearest articulated figure body for a joint of the skeletal model
  517. animator->GetJointList( fb->containedJoints, jointList );
  518. for( i = 0; i < jointList.Num(); i++ ) {
  519. if ( jointBody[ jointList[ i ] ] != -1 ) {
  520. gameLocal.Warning( "%s: joint '%s' is already contained by body '%s'",
  521. name.c_str(), animator->GetJointName( (jointHandle_t)jointList[i] ),
  522. physicsObj.GetBody( jointBody[ jointList[ i ] ] )->GetName().c_str() );
  523. }
  524. jointBody[ jointList[ i ] ] = id;
  525. }
  526. return true;
  527. }
  528. /*
  529. ================
  530. idAF::LoadConstraint
  531. ================
  532. */
  533. bool idAF::LoadConstraint( const idDeclAF_Constraint *fc ) {
  534. idAFBody *body1, *body2;
  535. idAngles angles;
  536. idMat3 axis;
  537. body1 = physicsObj.GetBody( fc->body1 );
  538. body2 = physicsObj.GetBody( fc->body2 );
  539. switch( fc->type ) {
  540. case DECLAF_CONSTRAINT_FIXED: {
  541. idAFConstraint_Fixed *c;
  542. c = static_cast<idAFConstraint_Fixed *>(physicsObj.GetConstraint( fc->name ));
  543. if ( c ) {
  544. c->SetBody1( body1 );
  545. c->SetBody2( body2 );
  546. }
  547. else {
  548. c = new idAFConstraint_Fixed( fc->name, body1, body2 );
  549. physicsObj.AddConstraint( c );
  550. }
  551. break;
  552. }
  553. case DECLAF_CONSTRAINT_BALLANDSOCKETJOINT: {
  554. idAFConstraint_BallAndSocketJoint *c;
  555. c = static_cast<idAFConstraint_BallAndSocketJoint *>(physicsObj.GetConstraint( fc->name ));
  556. if ( c ) {
  557. c->SetBody1( body1 );
  558. c->SetBody2( body2 );
  559. }
  560. else {
  561. c = new idAFConstraint_BallAndSocketJoint( fc->name, body1, body2 );
  562. physicsObj.AddConstraint( c );
  563. }
  564. c->SetAnchor( fc->anchor.ToVec3() );
  565. c->SetFriction( fc->friction );
  566. switch( fc->limit ) {
  567. case idDeclAF_Constraint::LIMIT_CONE: {
  568. c->SetConeLimit( fc->limitAxis.ToVec3(), fc->limitAngles[0], fc->shaft[0].ToVec3() );
  569. break;
  570. }
  571. case idDeclAF_Constraint::LIMIT_PYRAMID: {
  572. angles = fc->limitAxis.ToVec3().ToAngles();
  573. angles.roll = fc->limitAngles[2];
  574. axis = angles.ToMat3();
  575. c->SetPyramidLimit( axis[0], axis[1], fc->limitAngles[0], fc->limitAngles[1], fc->shaft[0].ToVec3() );
  576. break;
  577. }
  578. default: {
  579. c->SetNoLimit();
  580. break;
  581. }
  582. }
  583. break;
  584. }
  585. case DECLAF_CONSTRAINT_UNIVERSALJOINT: {
  586. idAFConstraint_UniversalJoint *c;
  587. c = static_cast<idAFConstraint_UniversalJoint *>(physicsObj.GetConstraint( fc->name ));
  588. if ( c ) {
  589. c->SetBody1( body1 );
  590. c->SetBody2( body2 );
  591. }
  592. else {
  593. c = new idAFConstraint_UniversalJoint( fc->name, body1, body2 );
  594. physicsObj.AddConstraint( c );
  595. }
  596. c->SetAnchor( fc->anchor.ToVec3() );
  597. c->SetShafts( fc->shaft[0].ToVec3(), fc->shaft[1].ToVec3() );
  598. c->SetFriction( fc->friction );
  599. switch( fc->limit ) {
  600. case idDeclAF_Constraint::LIMIT_CONE: {
  601. c->SetConeLimit( fc->limitAxis.ToVec3(), fc->limitAngles[0] );
  602. break;
  603. }
  604. case idDeclAF_Constraint::LIMIT_PYRAMID: {
  605. angles = fc->limitAxis.ToVec3().ToAngles();
  606. angles.roll = fc->limitAngles[2];
  607. axis = angles.ToMat3();
  608. c->SetPyramidLimit( axis[0], axis[1], fc->limitAngles[0], fc->limitAngles[1] );
  609. break;
  610. }
  611. default: {
  612. c->SetNoLimit();
  613. break;
  614. }
  615. }
  616. break;
  617. }
  618. case DECLAF_CONSTRAINT_HINGE: {
  619. idAFConstraint_Hinge *c;
  620. c = static_cast<idAFConstraint_Hinge *>(physicsObj.GetConstraint( fc->name ));
  621. if ( c ) {
  622. c->SetBody1( body1 );
  623. c->SetBody2( body2 );
  624. }
  625. else {
  626. c = new idAFConstraint_Hinge( fc->name, body1, body2 );
  627. physicsObj.AddConstraint( c );
  628. }
  629. c->SetAnchor( fc->anchor.ToVec3() );
  630. c->SetAxis( fc->axis.ToVec3() );
  631. c->SetFriction( fc->friction );
  632. switch( fc->limit ) {
  633. case idDeclAF_Constraint::LIMIT_CONE: {
  634. idVec3 left, up, axis, shaft;
  635. fc->axis.ToVec3().OrthogonalBasis( left, up );
  636. axis = left * idRotation( vec3_origin, fc->axis.ToVec3(), fc->limitAngles[0] );
  637. shaft = left * idRotation( vec3_origin, fc->axis.ToVec3(), fc->limitAngles[2] );
  638. c->SetLimit( axis, fc->limitAngles[1], shaft );
  639. break;
  640. }
  641. default: {
  642. c->SetNoLimit();
  643. break;
  644. }
  645. }
  646. break;
  647. }
  648. case DECLAF_CONSTRAINT_SLIDER: {
  649. idAFConstraint_Slider *c;
  650. c = static_cast<idAFConstraint_Slider *>(physicsObj.GetConstraint( fc->name ));
  651. if ( c ) {
  652. c->SetBody1( body1 );
  653. c->SetBody2( body2 );
  654. }
  655. else {
  656. c = new idAFConstraint_Slider( fc->name, body1, body2 );
  657. physicsObj.AddConstraint( c );
  658. }
  659. c->SetAxis( fc->axis.ToVec3() );
  660. break;
  661. }
  662. case DECLAF_CONSTRAINT_SPRING: {
  663. idAFConstraint_Spring *c;
  664. c = static_cast<idAFConstraint_Spring *>(physicsObj.GetConstraint( fc->name ));
  665. if ( c ) {
  666. c->SetBody1( body1 );
  667. c->SetBody2( body2 );
  668. }
  669. else {
  670. c = new idAFConstraint_Spring( fc->name, body1, body2 );
  671. physicsObj.AddConstraint( c );
  672. }
  673. c->SetAnchor( fc->anchor.ToVec3(), fc->anchor2.ToVec3() );
  674. c->SetSpring( fc->stretch, fc->compress, fc->damping, fc->restLength );
  675. c->SetLimit( fc->minLength, fc->maxLength );
  676. break;
  677. }
  678. }
  679. return true;
  680. }
  681. /*
  682. ================
  683. GetJointTransform
  684. ================
  685. */
  686. static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
  687. jointHandle_t joint;
  688. joint = reinterpret_cast<idAnimator *>(model)->GetJointHandle( jointName );
  689. if ( ( joint >= 0 ) && ( joint < reinterpret_cast<idAnimator *>(model)->NumJoints() ) ) {
  690. origin = frame[ joint ].ToVec3();
  691. axis = frame[ joint ].ToMat3();
  692. return true;
  693. } else {
  694. return false;
  695. }
  696. }
  697. /*
  698. ================
  699. idAF::Load
  700. ================
  701. */
  702. bool idAF::Load( idEntity *ent, const char *fileName ) {
  703. int i, j;
  704. const idDeclAF *file;
  705. const idDeclModelDef *modelDef;
  706. idRenderModel *model;
  707. int numJoints;
  708. idJointMat *joints;
  709. assert( ent );
  710. self = ent;
  711. physicsObj.SetSelf( self );
  712. if ( animator == NULL ) {
  713. gameLocal.Warning( "Couldn't load af '%s' for entity '%s' at (%s): NULL animator\n", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0) );
  714. return false;
  715. }
  716. name = fileName;
  717. name.StripFileExtension();
  718. file = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, name ) );
  719. if ( !file ) {
  720. gameLocal.Warning( "Couldn't load af '%s' for entity '%s' at (%s)\n", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0) );
  721. return false;
  722. }
  723. if ( file->bodies.Num() == 0 || file->bodies[0]->jointName != "origin" ) {
  724. gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no body which modifies the origin joint.",
  725. name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0) );
  726. return false;
  727. }
  728. modelDef = animator->ModelDef();
  729. if ( modelDef == NULL || modelDef->GetState() == DS_DEFAULTED ) {
  730. gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no or defaulted modelDef '%s'",
  731. name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0), modelDef ? modelDef->GetName() : "" );
  732. return false;
  733. }
  734. model = animator->ModelHandle();
  735. if ( model == NULL || model->IsDefaultModel() ) {
  736. gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no or defaulted model '%s'",
  737. name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0), model ? model->Name() : "" );
  738. return false;
  739. }
  740. // get the modified animation
  741. modifiedAnim = animator->GetAnim( ARTICULATED_FIGURE_ANIM );
  742. if ( !modifiedAnim ) {
  743. gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no modified animation '%s'",
  744. name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0), ARTICULATED_FIGURE_ANIM );
  745. return false;
  746. }
  747. // create the animation frame used to setup the articulated figure
  748. numJoints = animator->NumJoints();
  749. joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) );
  750. gameEdit->ANIM_CreateAnimFrame( model, animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset(), animator->RemoveOrigin() );
  751. // set all vector positions from model joints
  752. file->Finish( GetJointTransform, joints, animator );
  753. // initialize articulated figure physics
  754. physicsObj.SetGravity( gameLocal.GetGravity() );
  755. physicsObj.SetClipMask( file->clipMask );
  756. physicsObj.SetDefaultFriction( file->defaultLinearFriction, file->defaultAngularFriction, file->defaultContactFriction );
  757. physicsObj.SetSuspendSpeed( file->suspendVelocity, file->suspendAcceleration );
  758. physicsObj.SetSuspendTolerance( file->noMoveTime, file->noMoveTranslation, file->noMoveRotation );
  759. physicsObj.SetSuspendTime( file->minMoveTime, file->maxMoveTime );
  760. physicsObj.SetSelfCollision( file->selfCollision );
  761. // clear the list with transforms from joints to bodies
  762. jointMods.SetNum( 0, false );
  763. // clear the joint to body conversion list
  764. jointBody.AssureSize( animator->NumJoints() );
  765. for ( i = 0; i < jointBody.Num(); i++ ) {
  766. jointBody[i] = -1;
  767. }
  768. // delete any bodies in the physicsObj that are no longer in the idDeclAF
  769. for ( i = 0; i < physicsObj.GetNumBodies(); i++ ) {
  770. idAFBody *body = physicsObj.GetBody( i );
  771. for ( j = 0; j < file->bodies.Num(); j++ ) {
  772. if ( file->bodies[j]->name.Icmp( body->GetName() ) == 0 ) {
  773. break;
  774. }
  775. }
  776. if ( j >= file->bodies.Num() ) {
  777. physicsObj.DeleteBody( i );
  778. i--;
  779. }
  780. }
  781. // delete any constraints in the physicsObj that are no longer in the idDeclAF
  782. for ( i = 0; i < physicsObj.GetNumConstraints(); i++ ) {
  783. idAFConstraint *constraint = physicsObj.GetConstraint( i );
  784. for ( j = 0; j < file->constraints.Num(); j++ ) {
  785. if ( file->constraints[j]->name.Icmp( constraint->GetName() ) == 0 &&
  786. file->constraints[j]->type == constraint->GetType() ) {
  787. break;
  788. }
  789. }
  790. if ( j >= file->constraints.Num() ) {
  791. physicsObj.DeleteConstraint( i );
  792. i--;
  793. }
  794. }
  795. // load bodies from the file
  796. for ( i = 0; i < file->bodies.Num(); i++ ) {
  797. LoadBody( file->bodies[i], joints );
  798. }
  799. // load constraints from the file
  800. for ( i = 0; i < file->constraints.Num(); i++ ) {
  801. LoadConstraint( file->constraints[i] );
  802. }
  803. physicsObj.UpdateClipModels();
  804. // check if each joint is contained by a body
  805. for( i = 0; i < animator->NumJoints(); i++ ) {
  806. if ( jointBody[i] == -1 ) {
  807. gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) joint '%s' is not contained by a body",
  808. name.c_str(), self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0), animator->GetJointName( (jointHandle_t)i ) );
  809. }
  810. }
  811. physicsObj.SetMass( file->totalMass );
  812. physicsObj.SetChanged();
  813. // disable the articulated figure for collision detection until activated
  814. physicsObj.DisableClip();
  815. isLoaded = true;
  816. return true;
  817. }
  818. /*
  819. ================
  820. idAF::Start
  821. ================
  822. */
  823. void idAF::Start( void ) {
  824. if ( !IsLoaded() ) {
  825. return;
  826. }
  827. // clear all animations
  828. animator->ClearAllAnims( gameLocal.time, 0 );
  829. animator->ClearAllJoints();
  830. // switch to articulated figure physics
  831. self->SetPhysics( &physicsObj );
  832. // start the articulated figure physics simulation
  833. physicsObj.EnableClip();
  834. physicsObj.Activate();
  835. isActive = true;
  836. }
  837. /*
  838. ================
  839. idAF::TestSolid
  840. ================
  841. */
  842. bool idAF::TestSolid( void ) const {
  843. int i;
  844. idAFBody *body;
  845. trace_t trace;
  846. idStr str;
  847. bool solid;
  848. if ( !IsLoaded() ) {
  849. return false;
  850. }
  851. if ( !af_testSolid.GetBool() ) {
  852. return false;
  853. }
  854. solid = false;
  855. for ( i = 0; i < physicsObj.GetNumBodies(); i++ ) {
  856. body = physicsObj.GetBody( i );
  857. if ( gameLocal.clip.Translation( trace, body->GetWorldOrigin(), body->GetWorldOrigin(), body->GetClipModel(), body->GetWorldAxis(), body->GetClipMask(), self ) ) {
  858. float depth = idMath::Fabs( trace.c.point * trace.c.normal - trace.c.dist );
  859. body->SetWorldOrigin( body->GetWorldOrigin() + trace.c.normal * ( depth + 8.0f ) );
  860. gameLocal.DWarning( "%s: body '%s' stuck in %d (normal = %.2f %.2f %.2f, depth = %.2f)", self->name.c_str(),
  861. body->GetName().c_str(), trace.c.contents, trace.c.normal.x, trace.c.normal.y, trace.c.normal.z, depth );
  862. solid = true;
  863. }
  864. }
  865. return solid;
  866. }
  867. /*
  868. ================
  869. idAF::StartFromCurrentPose
  870. ================
  871. */
  872. void idAF::StartFromCurrentPose( int inheritVelocityTime ) {
  873. if ( !IsLoaded() ) {
  874. return;
  875. }
  876. // if the ragdoll should inherit velocity from the animation
  877. if ( inheritVelocityTime > 0 ) {
  878. // make sure the ragdoll is at rest
  879. physicsObj.PutToRest();
  880. // set the pose for some time back
  881. SetupPose( self, gameLocal.time - inheritVelocityTime );
  882. // change the pose for the current time and set velocities
  883. ChangePose( self, gameLocal.time );
  884. }
  885. else {
  886. // transform the articulated figure to reflect the current animation pose
  887. SetupPose( self, gameLocal.time );
  888. }
  889. physicsObj.UpdateClipModels();
  890. TestSolid();
  891. Start();
  892. UpdateAnimation();
  893. // update the render entity origin and axis
  894. self->UpdateModel();
  895. // make sure the renderer gets the updated origin and axis
  896. self->Present();
  897. }
  898. /*
  899. ================
  900. idAF::Stop
  901. ================
  902. */
  903. void idAF::Stop( void ) {
  904. // disable the articulated figure for collision detection
  905. physicsObj.UnlinkClip();
  906. isActive = false;
  907. }
  908. /*
  909. ================
  910. idAF::Rest
  911. ================
  912. */
  913. void idAF::Rest( void ) {
  914. physicsObj.PutToRest();
  915. }
  916. /*
  917. ================
  918. idAF::SetConstraintPosition
  919. Only moves constraints that bind the entity to another entity.
  920. ================
  921. */
  922. void idAF::SetConstraintPosition( const char *name, const idVec3 &pos ) {
  923. idAFConstraint *constraint;
  924. constraint = GetPhysics()->GetConstraint( name );
  925. if ( !constraint ) {
  926. gameLocal.Warning( "can't find a constraint with the name '%s'", name );
  927. return;
  928. }
  929. if ( constraint->GetBody2() != NULL ) {
  930. gameLocal.Warning( "constraint '%s' does not bind to another entity", name );
  931. return;
  932. }
  933. switch( constraint->GetType() ) {
  934. case CONSTRAINT_BALLANDSOCKETJOINT: {
  935. idAFConstraint_BallAndSocketJoint *bs = static_cast<idAFConstraint_BallAndSocketJoint *>(constraint);
  936. bs->Translate( pos - bs->GetAnchor() );
  937. break;
  938. }
  939. case CONSTRAINT_UNIVERSALJOINT: {
  940. idAFConstraint_UniversalJoint *uj = static_cast<idAFConstraint_UniversalJoint *>(constraint);
  941. uj->Translate( pos - uj->GetAnchor() );
  942. break;
  943. }
  944. case CONSTRAINT_HINGE: {
  945. idAFConstraint_Hinge *hinge = static_cast<idAFConstraint_Hinge *>(constraint);
  946. hinge->Translate( pos - hinge->GetAnchor() );
  947. break;
  948. }
  949. default: {
  950. gameLocal.Warning( "cannot set the constraint position for '%s'", name );
  951. break;
  952. }
  953. }
  954. }
  955. /*
  956. ================
  957. idAF::SaveState
  958. ================
  959. */
  960. void idAF::SaveState( idDict &args ) const {
  961. int i;
  962. idAFBody *body;
  963. idStr key, value;
  964. for ( i = 0; i < jointMods.Num(); i++ ) {
  965. body = physicsObj.GetBody( jointMods[i].bodyId );
  966. key = "body " + body->GetName();
  967. value = body->GetWorldOrigin().ToString( 8 );
  968. value += " ";
  969. value += body->GetWorldAxis().ToAngles().ToString( 8 );
  970. args.Set( key, value );
  971. }
  972. }
  973. /*
  974. ================
  975. idAF::LoadState
  976. ================
  977. */
  978. void idAF::LoadState( const idDict &args ) {
  979. const idKeyValue *kv;
  980. idStr name;
  981. idAFBody *body;
  982. idVec3 origin;
  983. idAngles angles;
  984. kv = args.MatchPrefix( "body ", NULL );
  985. while ( kv ) {
  986. name = kv->GetKey();
  987. name.Strip( "body " );
  988. body = physicsObj.GetBody( name );
  989. if ( body ) {
  990. sscanf( kv->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
  991. body->SetWorldOrigin( origin );
  992. body->SetWorldAxis( angles.ToMat3() );
  993. } else {
  994. gameLocal.Warning("Unknown body part %s in articulated figure %s", name.c_str(), this->name.c_str());
  995. }
  996. kv = args.MatchPrefix( "body ", kv );
  997. }
  998. physicsObj.UpdateClipModels();
  999. }
  1000. /*
  1001. ================
  1002. idAF::AddBindConstraints
  1003. ================
  1004. */
  1005. void idAF::AddBindConstraints( void ) {
  1006. const idKeyValue *kv;
  1007. idStr name;
  1008. idAFBody *body;
  1009. idLexer lexer;
  1010. idToken type, bodyName, jointName;
  1011. idVec3 origin, renderOrigin;
  1012. idMat3 axis, renderAxis;
  1013. if ( !IsLoaded() ) {
  1014. return;
  1015. }
  1016. const idDict &args = self->spawnArgs;
  1017. // get the render position
  1018. origin = physicsObj.GetOrigin( 0 );
  1019. axis = physicsObj.GetAxis( 0 );
  1020. renderAxis = baseAxis.Transpose() * axis;
  1021. renderOrigin = origin - baseOrigin * renderAxis;
  1022. // parse all the bind constraints
  1023. for ( kv = args.MatchPrefix( "bindConstraint ", NULL ); kv; kv = args.MatchPrefix( "bindConstraint ", kv ) ) {
  1024. name = kv->GetKey();
  1025. name.Strip( "bindConstraint " );
  1026. lexer.LoadMemory( kv->GetValue(), kv->GetValue().Length(), kv->GetKey() );
  1027. lexer.ReadToken( &type );
  1028. lexer.ReadToken( &bodyName );
  1029. body = physicsObj.GetBody( bodyName );
  1030. if ( !body ) {
  1031. gameLocal.Warning( "idAF::AddBindConstraints: body '%s' not found on entity '%s'", bodyName.c_str(), self->name.c_str() );
  1032. lexer.FreeSource();
  1033. continue;
  1034. }
  1035. if ( type.Icmp( "fixed" ) == 0 ) {
  1036. idAFConstraint_Fixed *c;
  1037. c = new idAFConstraint_Fixed( name, body, NULL );
  1038. physicsObj.AddConstraint( c );
  1039. }
  1040. else if ( type.Icmp( "ballAndSocket" ) == 0 ) {
  1041. idAFConstraint_BallAndSocketJoint *c;
  1042. c = new idAFConstraint_BallAndSocketJoint( name, body, NULL );
  1043. physicsObj.AddConstraint( c );
  1044. lexer.ReadToken( &jointName );
  1045. jointHandle_t joint = animator->GetJointHandle( jointName );
  1046. if ( joint == INVALID_JOINT ) {
  1047. gameLocal.Warning( "idAF::AddBindConstraints: joint '%s' not found", jointName.c_str() );
  1048. }
  1049. animator->GetJointTransform( joint, gameLocal.time, origin, axis );
  1050. c->SetAnchor( renderOrigin + origin * renderAxis );
  1051. }
  1052. else if ( type.Icmp( "universal" ) == 0 ) {
  1053. idAFConstraint_UniversalJoint *c;
  1054. c = new idAFConstraint_UniversalJoint( name, body, NULL );
  1055. physicsObj.AddConstraint( c );
  1056. lexer.ReadToken( &jointName );
  1057. jointHandle_t joint = animator->GetJointHandle( jointName );
  1058. if ( joint == INVALID_JOINT ) {
  1059. gameLocal.Warning( "idAF::AddBindConstraints: joint '%s' not found", jointName.c_str() );
  1060. }
  1061. animator->GetJointTransform( joint, gameLocal.time, origin, axis );
  1062. c->SetAnchor( renderOrigin + origin * renderAxis );
  1063. c->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) );
  1064. }
  1065. else {
  1066. gameLocal.Warning( "idAF::AddBindConstraints: unknown constraint type '%s' on entity '%s'", type.c_str(), self->name.c_str() );
  1067. }
  1068. lexer.FreeSource();
  1069. }
  1070. hasBindConstraints = true;
  1071. }
  1072. /*
  1073. ================
  1074. idAF::RemoveBindConstraints
  1075. ================
  1076. */
  1077. void idAF::RemoveBindConstraints( void ) {
  1078. const idKeyValue *kv;
  1079. if ( !IsLoaded() ) {
  1080. return;
  1081. }
  1082. const idDict &args = self->spawnArgs;
  1083. idStr name;
  1084. kv = args.MatchPrefix( "bindConstraint ", NULL );
  1085. while ( kv ) {
  1086. name = kv->GetKey();
  1087. name.Strip( "bindConstraint " );
  1088. if ( physicsObj.GetConstraint( name ) ) {
  1089. physicsObj.DeleteConstraint( name );
  1090. }
  1091. kv = args.MatchPrefix( "bindConstraint ", kv );
  1092. }
  1093. hasBindConstraints = false;
  1094. }