home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / game / AF.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  35KB  |  1,337 lines

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