2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
32 #include "../Game_local.h"
34 CLASS_DECLARATION( idPhysics_Base, idPhysics_AF )
37 const float ERROR_REDUCTION = 0.5f;
38 const float ERROR_REDUCTION_MAX = 256.0f;
39 const float LIMIT_ERROR_REDUCTION = 0.3f;
40 const float LCP_EPSILON = 1e-7f;
41 const float LIMIT_LCP_EPSILON = 1e-4f;
42 const float CONTACT_LCP_EPSILON = 1e-6f;
43 const float CENTER_OF_MASS_EPSILON = 1e-4f;
44 const float NO_MOVE_TIME = 1.0f;
45 const float NO_MOVE_TRANSLATION_TOLERANCE = 10.0f;
46 const float NO_MOVE_ROTATION_TOLERANCE = 10.0f;
47 const float MIN_MOVE_TIME = -1.0f;
48 const float MAX_MOVE_TIME = -1.0f;
49 const float IMPULSE_THRESHOLD = 500.0f;
50 const float SUSPEND_LINEAR_VELOCITY = 10.0f;
51 const float SUSPEND_ANGULAR_VELOCITY = 15.0f;
52 const float SUSPEND_LINEAR_ACCELERATION = 20.0f;
53 const float SUSPEND_ANGULAR_ACCELERATION = 30.0f;
54 const idVec6 vec6_lcp_epsilon = idVec6( LCP_EPSILON, LCP_EPSILON, LCP_EPSILON,
55 LCP_EPSILON, LCP_EPSILON, LCP_EPSILON );
60 static int lastTimerReset = 0;
61 static int numArticulatedFigures = 0;
62 static idTimer timer_total, timer_pc, timer_ac, timer_collision, timer_lcp;
67 //===============================================================
71 //===============================================================
75 idAFConstraint::idAFConstraint
78 idAFConstraint::idAFConstraint( void ) {
79 type = CONSTRAINT_INVALID;
86 lo.SubVec6(0) = -vec6_infinity;
88 hi.SubVec6(0) = vec6_infinity;
90 e.SubVec6(0) = vec6_lcp_epsilon;
102 memset( &fl, 0, sizeof( fl ) );
107 idAFConstraint::~idAFConstraint
110 idAFConstraint::~idAFConstraint( void ) {
115 idAFConstraint::SetBody1
118 void idAFConstraint::SetBody1( idAFBody *body ) {
119 if ( body1 != body) {
122 physics->SetChanged();
129 idAFConstraint::SetBody2
132 void idAFConstraint::SetBody2( idAFBody *body ) {
133 if ( body2 != body ) {
136 physics->SetChanged();
143 idAFConstraint::GetMultiplier
146 const idVecX &idAFConstraint::GetMultiplier( void ) {
152 idAFConstraint::Evaluate
155 void idAFConstraint::Evaluate( float invTimeStep ) {
161 idAFConstraint::ApplyFriction
164 void idAFConstraint::ApplyFriction( float invTimeStep ) {
169 idAFConstraint::GetForce
172 void idAFConstraint::GetForce( idAFBody *body, idVec6 &force ) {
175 v.SetData( 6, VECX_ALLOCA( 6 ) );
176 if ( body == body1 ) {
177 J1.TransposeMultiply( v, lm );
179 else if ( body == body2 ) {
180 J2.TransposeMultiply( v, lm );
185 force[0] = v[0]; force[1] = v[1]; force[2] = v[2]; force[3] = v[3]; force[4] = v[4]; force[5] = v[5];
190 idAFConstraint::Translate
193 void idAFConstraint::Translate( const idVec3 &translation ) {
199 idAFConstraint::Rotate
202 void idAFConstraint::Rotate( const idRotation &rotation ) {
208 idAFConstraint::GetCenter
211 void idAFConstraint::GetCenter( idVec3 ¢er ) {
217 idAFConstraint::DebugDraw
220 void idAFConstraint::DebugDraw( void ) {
225 idAFConstraint::InitSize
228 void idAFConstraint::InitSize( int size ) {
242 void idAFConstraint::Save( idSaveGame *saveFile ) const {
243 saveFile->WriteInt( type );
248 idAFConstraint::Restore
251 void idAFConstraint::Restore( idRestoreGame *saveFile ) {
253 saveFile->ReadInt( (int &)t );
258 //===============================================================
260 // idAFConstraint_Fixed
262 //===============================================================
266 idAFConstraint_Fixed::idAFConstraint_Fixed
269 idAFConstraint_Fixed::idAFConstraint_Fixed( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
271 type = CONSTRAINT_FIXED;
276 fl.allowPrimary = true;
277 fl.noCollision = true;
284 idAFConstraint_Fixed::InitOffset
287 void idAFConstraint_Fixed::InitOffset( void ) {
289 offset = ( body1->GetWorldOrigin() - body2->GetWorldOrigin() ) * body2->GetWorldAxis().Transpose();
290 relAxis = body1->GetWorldAxis() * body2->GetWorldAxis().Transpose();
293 offset = body1->GetWorldOrigin();
294 relAxis = body1->GetWorldAxis();
300 idAFConstraint_Fixed::SetBody1
303 void idAFConstraint_Fixed::SetBody1( idAFBody *body ) {
304 if ( body1 != body) {
308 physics->SetChanged();
315 idAFConstraint_Fixed::SetBody2
318 void idAFConstraint_Fixed::SetBody2( idAFBody *body ) {
319 if ( body2 != body ) {
323 physics->SetChanged();
330 idAFConstraint_Fixed::Evaluate
333 void idAFConstraint_Fixed::Evaluate( float invTimeStep ) {
339 master = body2 ? body2 : physics->GetMasterBody();
342 a2 = offset * master->GetWorldAxis();
343 ofs = a2 + master->GetWorldOrigin();
344 ax = relAxis * master->GetWorldAxis();
352 J1.Set( mat3_identity, mat3_zero,
353 mat3_zero, mat3_identity );
356 J2.Set( -mat3_identity, SkewSymmetric( a2 ),
357 mat3_zero, -mat3_identity );
363 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( ofs - body1->GetWorldOrigin() );
364 r = ( body1->GetWorldAxis().Transpose() * ax ).ToRotation();
365 c1.SubVec3(1) = -( invTimeStep * ERROR_REDUCTION ) * ( r.GetVec() * -(float) DEG2RAD( r.GetAngle() ) );
367 c1.Clamp( -ERROR_REDUCTION_MAX, ERROR_REDUCTION_MAX );
372 idAFConstraint_Fixed::ApplyFriction
375 void idAFConstraint_Fixed::ApplyFriction( float invTimeStep ) {
381 idAFConstraint_Fixed::Translate
384 void idAFConstraint_Fixed::Translate( const idVec3 &translation ) {
386 offset += translation;
392 idAFConstraint_Fixed::Rotate
395 void idAFConstraint_Fixed::Rotate( const idRotation &rotation ) {
398 relAxis *= rotation.ToMat3();
404 idAFConstraint_Fixed::GetCenter
407 void idAFConstraint_Fixed::GetCenter( idVec3 ¢er ) {
408 center = body1->GetWorldOrigin();
413 idAFConstraint_Fixed::DebugDraw
416 void idAFConstraint_Fixed::DebugDraw( void ) {
419 master = body2 ? body2 : physics->GetMasterBody();
421 gameRenderWorld->DebugLine( colorRed, body1->GetWorldOrigin(), master->GetWorldOrigin() );
424 gameRenderWorld->DebugLine( colorRed, body1->GetWorldOrigin(), vec3_origin );
430 idAFConstraint_Fixed::Save
433 void idAFConstraint_Fixed::Save( idSaveGame *saveFile ) const {
434 idAFConstraint::Save( saveFile );
435 saveFile->WriteVec3( offset );
436 saveFile->WriteMat3( relAxis );
441 idAFConstraint_Fixed::Restore
444 void idAFConstraint_Fixed::Restore( idRestoreGame *saveFile ) {
445 idAFConstraint::Restore( saveFile );
446 saveFile->ReadVec3( offset );
447 saveFile->ReadMat3( relAxis );
451 //===============================================================
453 // idAFConstraint_BallAndSocketJoint
455 //===============================================================
459 idAFConstraint_BallAndSocketJoint::idAFConstraint_BallAndSocketJoint
462 idAFConstraint_BallAndSocketJoint::idAFConstraint_BallAndSocketJoint( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
464 type = CONSTRAINT_BALLANDSOCKETJOINT;
473 fl.allowPrimary = true;
474 fl.noCollision = true;
479 idAFConstraint_BallAndSocketJoint::~idAFConstraint_BallAndSocketJoint
482 idAFConstraint_BallAndSocketJoint::~idAFConstraint_BallAndSocketJoint( void ) {
486 if ( pyramidLimit ) {
493 idAFConstraint_BallAndSocketJoint::SetAnchor
496 void idAFConstraint_BallAndSocketJoint::SetAnchor( const idVec3 &worldPosition ) {
498 // get anchor relative to center of mass of body1
499 anchor1 = ( worldPosition - body1->GetWorldOrigin() ) * body1->GetWorldAxis().Transpose();
501 // get anchor relative to center of mass of body2
502 anchor2 = ( worldPosition - body2->GetWorldOrigin() ) * body2->GetWorldAxis().Transpose();
505 anchor2 = worldPosition;
509 coneLimit->SetAnchor( anchor2 );
511 if ( pyramidLimit ) {
512 pyramidLimit->SetAnchor( anchor2 );
518 idAFConstraint_BallAndSocketJoint::GetAnchor
521 idVec3 idAFConstraint_BallAndSocketJoint::GetAnchor( void ) const {
523 return body2->GetWorldOrigin() + body2->GetWorldAxis() * anchor2;
530 idAFConstraint_BallAndSocketJoint::SetNoLimit
533 void idAFConstraint_BallAndSocketJoint::SetNoLimit( void ) {
538 if ( pyramidLimit ) {
546 idAFConstraint_BallAndSocketJoint::SetConeLimit
549 void idAFConstraint_BallAndSocketJoint::SetConeLimit( const idVec3 &coneAxis, const float coneAngle, const idVec3 &body1Axis ) {
550 if ( pyramidLimit ) {
555 coneLimit = new idAFConstraint_ConeLimit;
556 coneLimit->SetPhysics( physics );
559 coneLimit->Setup( body1, body2, anchor2, coneAxis * body2->GetWorldAxis().Transpose(), coneAngle, body1Axis * body1->GetWorldAxis().Transpose() );
562 coneLimit->Setup( body1, body2, anchor2, coneAxis, coneAngle, body1Axis * body1->GetWorldAxis().Transpose() );
568 idAFConstraint_BallAndSocketJoint::SetPyramidLimit
571 void idAFConstraint_BallAndSocketJoint::SetPyramidLimit( const idVec3 &pyramidAxis, const idVec3 &baseAxis,
572 const float angle1, const float angle2, const idVec3 &body1Axis ) {
577 if ( !pyramidLimit ) {
578 pyramidLimit = new idAFConstraint_PyramidLimit;
579 pyramidLimit->SetPhysics( physics );
582 pyramidLimit->Setup( body1, body2, anchor2, pyramidAxis * body2->GetWorldAxis().Transpose(),
583 baseAxis * body2->GetWorldAxis().Transpose(), angle1, angle2,
584 body1Axis * body1->GetWorldAxis().Transpose() );
587 pyramidLimit->Setup( body1, body2, anchor2, pyramidAxis, baseAxis, angle1, angle2,
588 body1Axis * body1->GetWorldAxis().Transpose() );
594 idAFConstraint_BallAndSocketJoint::SetLimitEpsilon
597 void idAFConstraint_BallAndSocketJoint::SetLimitEpsilon( const float e ) {
599 coneLimit->SetEpsilon( e );
601 if ( pyramidLimit ) {
602 pyramidLimit->SetEpsilon( e );
608 idAFConstraint_BallAndSocketJoint::GetFriction
611 float idAFConstraint_BallAndSocketJoint::GetFriction( void ) const {
612 if ( af_forceFriction.GetFloat() > 0.0f ) {
613 return af_forceFriction.GetFloat();
615 return friction * physics->GetJointFrictionScale();
620 idAFConstraint_BallAndSocketJoint::Evaluate
623 void idAFConstraint_BallAndSocketJoint::Evaluate( float invTimeStep ) {
627 master = body2 ? body2 : physics->GetMasterBody();
629 a1 = anchor1 * body1->GetWorldAxis();
632 a2 = anchor2 * master->GetWorldAxis();
633 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( a2 + master->GetWorldOrigin() - ( a1 + body1->GetWorldOrigin() ) );
636 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( anchor2 - ( a1 + body1->GetWorldOrigin() ) );
639 c1.Clamp( -ERROR_REDUCTION_MAX, ERROR_REDUCTION_MAX );
641 J1.Set( mat3_identity, -SkewSymmetric( a1 ) );
644 J2.Set( -mat3_identity, SkewSymmetric( a2 ) );
651 coneLimit->Add( physics, invTimeStep );
653 else if ( pyramidLimit ) {
654 pyramidLimit->Add( physics, invTimeStep );
660 idAFConstraint_BallAndSocketJoint::ApplyFriction
663 void idAFConstraint_BallAndSocketJoint::ApplyFriction( float invTimeStep ) {
665 float invMass, currentFriction;
667 currentFriction = GetFriction();
669 if ( currentFriction <= 0.0f ) {
673 if ( af_useImpulseFriction.GetBool() || af_useJointImpulseFriction.GetBool() ) {
675 angular = body1->GetAngularVelocity();
676 invMass = body1->GetInverseMass();
678 angular -= body2->GetAngularVelocity();
679 invMass += body2->GetInverseMass();
682 angular *= currentFriction / invMass;
684 body1->SetAngularVelocity( body1->GetAngularVelocity() - angular * body1->GetInverseMass() );
686 body2->SetAngularVelocity( body2->GetAngularVelocity() + angular * body2->GetInverseMass() );
691 fc = new idAFConstraint_BallAndSocketJointFriction;
695 fc->Add( physics, invTimeStep );
701 idAFConstraint_BallAndSocketJoint::GetForce
704 void idAFConstraint_BallAndSocketJoint::GetForce( idAFBody *body, idVec6 &force ) {
705 idAFConstraint::GetForce( body, force );
706 // FIXME: add limit force
711 idAFConstraint_BallAndSocketJoint::Translate
714 void idAFConstraint_BallAndSocketJoint::Translate( const idVec3 &translation ) {
716 anchor2 += translation;
719 coneLimit->Translate( translation );
721 else if ( pyramidLimit ) {
722 pyramidLimit->Translate( translation );
728 idAFConstraint_BallAndSocketJoint::Rotate
731 void idAFConstraint_BallAndSocketJoint::Rotate( const idRotation &rotation ) {
736 coneLimit->Rotate( rotation );
738 else if ( pyramidLimit ) {
739 pyramidLimit->Rotate( rotation );
745 idAFConstraint_BallAndSocketJoint::GetCenter
748 void idAFConstraint_BallAndSocketJoint::GetCenter( idVec3 ¢er ) {
749 center = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
754 idAFConstraint_BallAndSocketJoint::DebugDraw
757 void idAFConstraint_BallAndSocketJoint::DebugDraw( void ) {
758 idVec3 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
759 gameRenderWorld->DebugLine( colorBlue, a1 - idVec3( 5, 0, 0 ), a1 + idVec3( 5, 0, 0 ) );
760 gameRenderWorld->DebugLine( colorBlue, a1 - idVec3( 0, 5, 0 ), a1 + idVec3( 0, 5, 0 ) );
761 gameRenderWorld->DebugLine( colorBlue, a1 - idVec3( 0, 0, 5 ), a1 + idVec3( 0, 0, 5 ) );
763 if ( af_showLimits.GetBool() ) {
765 coneLimit->DebugDraw();
767 if ( pyramidLimit ) {
768 pyramidLimit->DebugDraw();
775 idAFConstraint_BallAndSocketJoint::Save
778 void idAFConstraint_BallAndSocketJoint::Save( idSaveGame *saveFile ) const {
779 idAFConstraint::Save( saveFile );
780 saveFile->WriteVec3( anchor1 );
781 saveFile->WriteVec3( anchor2 );
782 saveFile->WriteFloat( friction );
784 coneLimit->Save( saveFile );
786 if ( pyramidLimit ) {
787 pyramidLimit->Save( saveFile );
793 idAFConstraint_BallAndSocketJoint::Restore
796 void idAFConstraint_BallAndSocketJoint::Restore( idRestoreGame *saveFile ) {
797 idAFConstraint::Restore( saveFile );
798 saveFile->ReadVec3( anchor1 );
799 saveFile->ReadVec3( anchor2 );
800 saveFile->ReadFloat( friction );
802 coneLimit->Restore( saveFile );
804 if ( pyramidLimit ) {
805 pyramidLimit->Restore( saveFile );
810 //===============================================================
812 // idAFConstraint_BallAndSocketJointFriction
814 //===============================================================
818 idAFConstraint_BallAndSocketJointFriction::idAFConstraint_BallAndSocketJointFriction
821 idAFConstraint_BallAndSocketJointFriction::idAFConstraint_BallAndSocketJointFriction( void ) {
822 type = CONSTRAINT_FRICTION;
823 name = "ballAndSocketJointFriction";
826 fl.allowPrimary = false;
827 fl.frameConstraint = true;
832 idAFConstraint_BallAndSocketJointFriction::Setup
835 void idAFConstraint_BallAndSocketJointFriction::Setup( idAFConstraint_BallAndSocketJoint *bsj ) {
837 body1 = bsj->GetBody1();
838 body2 = bsj->GetBody2();
843 idAFConstraint_BallAndSocketJointFriction::Evaluate
846 void idAFConstraint_BallAndSocketJointFriction::Evaluate( float invTimeStep ) {
852 idAFConstraint_BallAndSocketJointFriction::ApplyFriction
855 void idAFConstraint_BallAndSocketJointFriction::ApplyFriction( float invTimeStep ) {
861 idAFConstraint_BallAndSocketJointFriction::Add
864 bool idAFConstraint_BallAndSocketJointFriction::Add( idPhysics_AF *phys, float invTimeStep ) {
869 f = joint->GetFriction() * joint->GetMultiplier().Length();
874 lo[0] = lo[1] = lo[2] = -f;
875 hi[0] = hi[1] = hi[2] = f;
878 J1[0][3] = J1[1][4] = J1[2][5] = 1.0f;
883 J2[0][3] = J2[1][4] = J2[2][5] = 1.0f;
886 physics->AddFrameConstraint( this );
893 idAFConstraint_BallAndSocketJointFriction::Translate
896 void idAFConstraint_BallAndSocketJointFriction::Translate( const idVec3 &translation ) {
901 idAFConstraint_BallAndSocketJointFriction::Rotate
904 void idAFConstraint_BallAndSocketJointFriction::Rotate( const idRotation &rotation ) {
908 //===============================================================
910 // idAFConstraint_UniversalJoint
912 //===============================================================
916 idAFConstraint_UniversalJoint::idAFConstraint_UniversalJoint
919 idAFConstraint_UniversalJoint::idAFConstraint_UniversalJoint( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
921 type = CONSTRAINT_UNIVERSALJOINT;
930 fl.allowPrimary = true;
931 fl.noCollision = true;
936 idAFConstraint_UniversalJoint::~idAFConstraint_UniversalJoint
939 idAFConstraint_UniversalJoint::~idAFConstraint_UniversalJoint( void ) {
943 if ( pyramidLimit ) {
953 idAFConstraint_UniversalJoint::SetAnchor
956 void idAFConstraint_UniversalJoint::SetAnchor( const idVec3 &worldPosition ) {
958 // get anchor relative to center of mass of body1
959 anchor1 = ( worldPosition - body1->GetWorldOrigin() ) * body1->GetWorldAxis().Transpose();
961 // get anchor relative to center of mass of body2
962 anchor2 = ( worldPosition - body2->GetWorldOrigin() ) * body2->GetWorldAxis().Transpose();
965 anchor2 = worldPosition;
969 coneLimit->SetAnchor( anchor2 );
971 if ( pyramidLimit ) {
972 pyramidLimit->SetAnchor( anchor2 );
978 idAFConstraint_UniversalJoint::GetAnchor
981 idVec3 idAFConstraint_UniversalJoint::GetAnchor( void ) const {
983 return body2->GetWorldOrigin() + body2->GetWorldAxis() * anchor2;
990 idAFConstraint_UniversalJoint::SetShafts
993 void idAFConstraint_UniversalJoint::SetShafts( const idVec3 &cardanShaft1, const idVec3 &cardanShaft2 ) {
997 shaft1 = cardanShaft1;
998 l = shaft1.Normalize();
1000 shaft2 = cardanShaft2;
1001 l = shaft2.Normalize();
1002 assert( l != 0.0f );
1004 // the cardan axis is a vector orthogonal to both cardan shafts
1005 cardanAxis = shaft1.Cross( shaft2 );
1006 if ( cardanAxis.Normalize() == 0.0f ) {
1008 shaft1.OrthogonalBasis( cardanAxis, vecY );
1009 cardanAxis.Normalize();
1012 shaft1 *= body1->GetWorldAxis().Transpose();
1013 axis1 = cardanAxis * body1->GetWorldAxis().Transpose();
1015 shaft2 *= body2->GetWorldAxis().Transpose();
1016 axis2 = cardanAxis * body2->GetWorldAxis().Transpose();
1023 coneLimit->SetBody1Axis( shaft1 );
1025 if ( pyramidLimit ) {
1026 pyramidLimit->SetBody1Axis( shaft1 );
1032 idAFConstraint_UniversalJoint::SetNoLimit
1035 void idAFConstraint_UniversalJoint::SetNoLimit( void ) {
1040 if ( pyramidLimit ) {
1041 delete pyramidLimit;
1042 pyramidLimit = NULL;
1048 idAFConstraint_UniversalJoint::SetConeLimit
1051 void idAFConstraint_UniversalJoint::SetConeLimit( const idVec3 &coneAxis, const float coneAngle ) {
1052 if ( pyramidLimit ) {
1053 delete pyramidLimit;
1054 pyramidLimit = NULL;
1057 coneLimit = new idAFConstraint_ConeLimit;
1058 coneLimit->SetPhysics( physics );
1061 coneLimit->Setup( body1, body2, anchor2, coneAxis * body2->GetWorldAxis().Transpose(), coneAngle, shaft1 );
1064 coneLimit->Setup( body1, body2, anchor2, coneAxis, coneAngle, shaft1 );
1070 idAFConstraint_UniversalJoint::SetPyramidLimit
1073 void idAFConstraint_UniversalJoint::SetPyramidLimit( const idVec3 &pyramidAxis, const idVec3 &baseAxis,
1074 const float angle1, const float angle2 ) {
1079 if ( !pyramidLimit ) {
1080 pyramidLimit = new idAFConstraint_PyramidLimit;
1081 pyramidLimit->SetPhysics( physics );
1084 pyramidLimit->Setup( body1, body2, anchor2, pyramidAxis * body2->GetWorldAxis().Transpose(),
1085 baseAxis * body2->GetWorldAxis().Transpose(), angle1, angle2, shaft1 );
1088 pyramidLimit->Setup( body1, body2, anchor2, pyramidAxis, baseAxis, angle1, angle2, shaft1 );
1094 idAFConstraint_UniversalJoint::SetLimitEpsilon
1097 void idAFConstraint_UniversalJoint::SetLimitEpsilon( const float e ) {
1099 coneLimit->SetEpsilon( e );
1101 if ( pyramidLimit ) {
1102 pyramidLimit->SetEpsilon( e );
1108 idAFConstraint_UniversalJoint::GetFriction
1111 float idAFConstraint_UniversalJoint::GetFriction( void ) const {
1112 if ( af_forceFriction.GetFloat() > 0.0f ) {
1113 return af_forceFriction.GetFloat();
1115 return friction * physics->GetJointFrictionScale();
1120 idAFConstraint_UniversalJoint::Evaluate
1122 NOTE: this joint is homokinetic
1125 void idAFConstraint_UniversalJoint::Evaluate( float invTimeStep ) {
1126 idVec3 a1, a2, s1, s2, d1, d2, v;
1129 master = body2 ? body2 : physics->GetMasterBody();
1131 a1 = anchor1 * body1->GetWorldAxis();
1132 s1 = shaft1 * body1->GetWorldAxis();
1133 d1 = s1.Cross( axis1 * body1->GetWorldAxis() );
1136 a2 = anchor2 * master->GetWorldAxis();
1137 s2 = shaft2 * master->GetWorldAxis();
1138 d2 = axis2 * master->GetWorldAxis();
1139 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( a2 + master->GetWorldOrigin() - ( a1 + body1->GetWorldOrigin() ) );
1145 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( a2 - ( a1 + body1->GetWorldOrigin() ) );
1148 J1.Set( mat3_identity, -SkewSymmetric( a1 ),
1149 mat3_zero, idMat3( s1[0], s1[1], s1[2],
1151 0.0f, 0.0f, 0.0f ) );
1155 J2.Set( -mat3_identity, SkewSymmetric( a2 ),
1156 mat3_zero, idMat3( s2[0], s2[1], s2[2],
1158 0.0f, 0.0f, 0.0f ) );
1166 if ( v.Normalize() != 0.0f ) {
1171 m1[2] = v.Cross( m1[0] );
1175 m2[2] = v.Cross( m2[0] );
1177 d2 *= m2.Transpose() * m1;
1180 c1[3] = -( invTimeStep * ERROR_REDUCTION ) * ( d1 * d2 );
1182 c1.Clamp( -ERROR_REDUCTION_MAX, ERROR_REDUCTION_MAX );
1185 coneLimit->Add( physics, invTimeStep );
1187 else if ( pyramidLimit ) {
1188 pyramidLimit->Add( physics, invTimeStep );
1194 idAFConstraint_UniversalJoint::ApplyFriction
1197 void idAFConstraint_UniversalJoint::ApplyFriction( float invTimeStep ) {
1199 float invMass, currentFriction;
1201 currentFriction = GetFriction();
1203 if ( currentFriction <= 0.0f ) {
1207 if ( af_useImpulseFriction.GetBool() || af_useJointImpulseFriction.GetBool() ) {
1209 angular = body1->GetAngularVelocity();
1210 invMass = body1->GetInverseMass();
1212 angular -= body2->GetAngularVelocity();
1213 invMass += body2->GetInverseMass();
1216 angular *= currentFriction / invMass;
1218 body1->SetAngularVelocity( body1->GetAngularVelocity() - angular * body1->GetInverseMass() );
1220 body2->SetAngularVelocity( body2->GetAngularVelocity() + angular * body2->GetInverseMass() );
1225 fc = new idAFConstraint_UniversalJointFriction;
1229 fc->Add( physics, invTimeStep );
1235 idAFConstraint_UniversalJoint::GetForce
1238 void idAFConstraint_UniversalJoint::GetForce( idAFBody *body, idVec6 &force ) {
1239 idAFConstraint::GetForce( body, force );
1240 // FIXME: add limit force
1245 idAFConstraint_UniversalJoint::Translate
1248 void idAFConstraint_UniversalJoint::Translate( const idVec3 &translation ) {
1250 anchor2 += translation;
1253 coneLimit->Translate( translation );
1255 else if ( pyramidLimit ) {
1256 pyramidLimit->Translate( translation );
1262 idAFConstraint_UniversalJoint::Rotate
1265 void idAFConstraint_UniversalJoint::Rotate( const idRotation &rotation ) {
1267 anchor2 *= rotation;
1268 shaft2 *= rotation.ToMat3();
1269 axis2 *= rotation.ToMat3();
1272 coneLimit->Rotate( rotation );
1274 else if ( pyramidLimit ) {
1275 pyramidLimit->Rotate( rotation );
1281 idAFConstraint_UniversalJoint::GetCenter
1284 void idAFConstraint_UniversalJoint::GetCenter( idVec3 ¢er ) {
1285 center = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
1290 idAFConstraint_UniversalJoint::DebugDraw
1293 void idAFConstraint_UniversalJoint::DebugDraw( void ) {
1294 idVec3 a1, a2, s1, s2, d1, d2, v;
1297 master = body2 ? body2 : physics->GetMasterBody();
1299 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
1300 s1 = shaft1 * body1->GetWorldAxis();
1301 d1 = axis1 * body1->GetWorldAxis();
1304 a2 = master->GetWorldOrigin() + anchor2 * master->GetWorldAxis();
1305 s2 = shaft2 * master->GetWorldAxis();
1306 d2 = axis2 * master->GetWorldAxis();
1315 if ( v.Normalize() != 0.0f ) {
1320 m1[2] = v.Cross( m1[0] );
1324 m2[2] = v.Cross( m2[0] );
1326 d2 *= m2.Transpose() * m1;
1329 gameRenderWorld->DebugArrow( colorCyan, a1, a1 + s1 * 5.0f, 1.0f );
1330 gameRenderWorld->DebugArrow( colorBlue, a2, a2 + s2 * 5.0f, 1.0f );
1331 gameRenderWorld->DebugLine( colorGreen, a1, a1 + d1 * 5.0f );
1332 gameRenderWorld->DebugLine( colorGreen, a2, a2 + d2 * 5.0f );
1334 if ( af_showLimits.GetBool() ) {
1336 coneLimit->DebugDraw();
1338 if ( pyramidLimit ) {
1339 pyramidLimit->DebugDraw();
1346 idAFConstraint_UniversalJoint::Save
1349 void idAFConstraint_UniversalJoint::Save( idSaveGame *saveFile ) const {
1350 idAFConstraint::Save( saveFile );
1351 saveFile->WriteVec3( anchor1 );
1352 saveFile->WriteVec3( anchor2 );
1353 saveFile->WriteVec3( shaft1 );
1354 saveFile->WriteVec3( shaft2 );
1355 saveFile->WriteVec3( axis1 );
1356 saveFile->WriteVec3( axis2 );
1357 saveFile->WriteFloat( friction );
1359 coneLimit->Save( saveFile );
1361 if ( pyramidLimit ) {
1362 pyramidLimit->Save( saveFile );
1368 idAFConstraint_UniversalJoint::Restore
1371 void idAFConstraint_UniversalJoint::Restore( idRestoreGame *saveFile ) {
1372 idAFConstraint::Restore( saveFile );
1373 saveFile->ReadVec3( anchor1 );
1374 saveFile->ReadVec3( anchor2 );
1375 saveFile->ReadVec3( shaft1 );
1376 saveFile->ReadVec3( shaft2 );
1377 saveFile->ReadVec3( axis1 );
1378 saveFile->ReadVec3( axis2 );
1379 saveFile->ReadFloat( friction );
1381 coneLimit->Restore( saveFile );
1383 if ( pyramidLimit ) {
1384 pyramidLimit->Restore( saveFile );
1389 //===============================================================
1391 // idAFConstraint_UniversalJointFriction
1393 //===============================================================
1397 idAFConstraint_UniversalJointFriction::idAFConstraint_UniversalJointFriction
1400 idAFConstraint_UniversalJointFriction::idAFConstraint_UniversalJointFriction( void ) {
1401 type = CONSTRAINT_FRICTION;
1402 name = "universalJointFriction";
1405 fl.allowPrimary = false;
1406 fl.frameConstraint = true;
1411 idAFConstraint_UniversalJointFriction::Setup
1414 void idAFConstraint_UniversalJointFriction::Setup( idAFConstraint_UniversalJoint *uj ) {
1416 body1 = uj->GetBody1();
1417 body2 = uj->GetBody2();
1422 idAFConstraint_UniversalJointFriction::Evaluate
1425 void idAFConstraint_UniversalJointFriction::Evaluate( float invTimeStep ) {
1431 idAFConstraint_UniversalJointFriction::ApplyFriction
1434 void idAFConstraint_UniversalJointFriction::ApplyFriction( float invTimeStep ) {
1440 idAFConstraint_UniversalJointFriction::Add
1443 bool idAFConstraint_UniversalJointFriction::Add( idPhysics_AF *phys, float invTimeStep ) {
1444 idVec3 s1, s2, dir1, dir2;
1449 f = joint->GetFriction() * joint->GetMultiplier().Length();
1457 joint->GetShafts( s1, s2 );
1459 s1 *= body1->GetWorldAxis();
1460 s1.NormalVectors( dir1, dir2 );
1463 J1.SubVec6(0).SubVec3(0).Zero();
1464 J1.SubVec6(0).SubVec3(1) = dir1;
1465 J1.SubVec6(1).SubVec3(0).Zero();
1466 J1.SubVec6(1).SubVec3(1) = dir2;
1471 J2.SubVec6(0).SubVec3(0).Zero();
1472 J2.SubVec6(0).SubVec3(1) = -dir1;
1473 J2.SubVec6(1).SubVec3(0).Zero();
1474 J2.SubVec6(1).SubVec3(1) = -dir2;
1477 physics->AddFrameConstraint( this );
1484 idAFConstraint_UniversalJointFriction::Translate
1487 void idAFConstraint_UniversalJointFriction::Translate( const idVec3 &translation ) {
1492 idAFConstraint_UniversalJointFriction::Rotate
1495 void idAFConstraint_UniversalJointFriction::Rotate( const idRotation &rotation ) {
1499 //===============================================================
1501 // idAFConstraint_CylindricalJoint
1503 //===============================================================
1507 idAFConstraint_CylindricalJoint::idAFConstraint_CylindricalJoint
1510 idAFConstraint_CylindricalJoint::idAFConstraint_CylindricalJoint( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
1511 assert( 0 ); // FIXME: implement
1516 idAFConstraint_CylindricalJoint::Evaluate
1519 void idAFConstraint_CylindricalJoint::Evaluate( float invTimeStep ) {
1520 assert( 0 ); // FIXME: implement
1525 idAFConstraint_CylindricalJoint::ApplyFriction
1528 void idAFConstraint_CylindricalJoint::ApplyFriction( float invTimeStep ) {
1529 assert( 0 ); // FIXME: implement
1534 idAFConstraint_CylindricalJoint::Translate
1537 void idAFConstraint_CylindricalJoint::Translate( const idVec3 &translation ) {
1538 assert( 0 ); // FIXME: implement
1543 idAFConstraint_CylindricalJoint::Rotate
1546 void idAFConstraint_CylindricalJoint::Rotate( const idRotation &rotation ) {
1547 assert( 0 ); // FIXME: implement
1552 idAFConstraint_CylindricalJoint::DebugDraw
1555 void idAFConstraint_CylindricalJoint::DebugDraw( void ) {
1556 assert( 0 ); // FIXME: implement
1560 //===============================================================
1562 // idAFConstraint_Hinge
1564 //===============================================================
1568 idAFConstraint_Hinge::idAFConstraint_Hinge
1571 idAFConstraint_Hinge::idAFConstraint_Hinge( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
1573 type = CONSTRAINT_HINGE;
1575 this->body1 = body1;
1576 this->body2 = body2;
1582 fl.allowPrimary = true;
1583 fl.noCollision = true;
1584 initialAxis = body1->GetWorldAxis();
1586 initialAxis *= body2->GetWorldAxis().Transpose();
1592 idAFConstraint_Hinge::~idAFConstraint_Hinge
1595 idAFConstraint_Hinge::~idAFConstraint_Hinge( void ) {
1609 idAFConstraint_Hinge::SetAnchor
1612 void idAFConstraint_Hinge::SetAnchor( const idVec3 &worldPosition ) {
1613 // get anchor relative to center of mass of body1
1614 anchor1 = ( worldPosition - body1->GetWorldOrigin() ) * body1->GetWorldAxis().Transpose();
1616 // get anchor relative to center of mass of body2
1617 anchor2 = ( worldPosition - body2->GetWorldOrigin() ) * body2->GetWorldAxis().Transpose();
1620 anchor2 = worldPosition;
1624 coneLimit->SetAnchor( anchor2 );
1630 idAFConstraint_Hinge::GetAnchor
1633 idVec3 idAFConstraint_Hinge::GetAnchor( void ) const {
1635 return body2->GetWorldOrigin() + body2->GetWorldAxis() * anchor2;
1642 idAFConstraint_Hinge::SetAxis
1645 void idAFConstraint_Hinge::SetAxis( const idVec3 &axis ) {
1649 normAxis.Normalize();
1651 // get axis relative to body1
1652 axis1 = normAxis * body1->GetWorldAxis().Transpose();
1654 // get axis relative to body2
1655 axis2 = normAxis * body2->GetWorldAxis().Transpose();
1664 idAFConstraint_Hinge::GetAxis
1667 idVec3 idAFConstraint_Hinge::GetAxis( void ) const {
1669 return axis2 * body2->GetWorldAxis();
1676 idAFConstraint_Hinge::SetNoLimit
1679 void idAFConstraint_Hinge::SetNoLimit( void ) {
1688 idAFConstraint_Hinge::SetLimit
1691 void idAFConstraint_Hinge::SetLimit( const idVec3 &axis, const float angle, const idVec3 &body1Axis ) {
1693 coneLimit = new idAFConstraint_ConeLimit;
1694 coneLimit->SetPhysics( physics );
1697 coneLimit->Setup( body1, body2, anchor2, axis * body2->GetWorldAxis().Transpose(), angle, body1Axis * body1->GetWorldAxis().Transpose() );
1700 coneLimit->Setup( body1, body2, anchor2, axis, angle, body1Axis * body1->GetWorldAxis().Transpose() );
1706 idAFConstraint_Hinge::SetLimitEpsilon
1709 void idAFConstraint_Hinge::SetLimitEpsilon( const float e ) {
1711 coneLimit->SetEpsilon( e );
1717 idAFConstraint_Hinge::GetFriction
1720 float idAFConstraint_Hinge::GetFriction( void ) const {
1721 if ( af_forceFriction.GetFloat() > 0.0f ) {
1722 return af_forceFriction.GetFloat();
1724 return friction * physics->GetJointFrictionScale();
1729 idAFConstraint_Hinge::GetAngle
1732 float idAFConstraint_Hinge::GetAngle( void ) const {
1734 idRotation rotation;
1737 axis = body1->GetWorldAxis() * body2->GetWorldAxis().Transpose() * initialAxis.Transpose();
1738 rotation = axis.ToRotation();
1739 angle = rotation.GetAngle();
1740 if ( rotation.GetVec() * axis1 < 0.0f ) {
1748 idAFConstraint_Hinge::SetSteerAngle
1751 void idAFConstraint_Hinge::SetSteerAngle( const float degrees ) {
1757 steering = new idAFConstraint_HingeSteering();
1758 steering->Setup( this );
1760 steering->SetSteerAngle( degrees );
1765 idAFConstraint_Hinge::SetSteerSpeed
1768 void idAFConstraint_Hinge::SetSteerSpeed( const float speed ) {
1770 steering->SetSteerSpeed( speed );
1776 idAFConstraint_Hinge::Evaluate
1779 void idAFConstraint_Hinge::Evaluate( float invTimeStep ) {
1781 idVec3 x1, x2, cross;
1785 master = body2 ? body2 : physics->GetMasterBody();
1787 x1 = axis1 * body1->GetWorldAxis(); // axis in body1 space
1788 x1.OrthogonalBasis( vecX, vecY ); // basis for axis in body1 space
1790 a1 = anchor1 * body1->GetWorldAxis(); // anchor in body1 space
1793 a2 = anchor2 * master->GetWorldAxis(); // anchor in master space
1794 x2 = axis2 * master->GetWorldAxis();
1795 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( a2 + master->GetWorldOrigin() - ( a1 + body1->GetWorldOrigin() ) );
1800 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( a2 - ( a1 + body1->GetWorldOrigin() ) );
1803 J1.Set( mat3_identity, -SkewSymmetric( a1 ),
1804 mat3_zero, idMat3( vecX[0], vecX[1], vecX[2],
1805 vecY[0], vecY[1], vecY[2],
1806 0.0f, 0.0f, 0.0f ) );
1810 J2.Set( -mat3_identity, SkewSymmetric( a2 ),
1811 mat3_zero, idMat3( -vecX[0], -vecX[1], -vecX[2],
1812 -vecY[0], -vecY[1], -vecY[2],
1813 0.0f, 0.0f, 0.0f ) );
1820 cross = x1.Cross( x2 );
1822 c1[3] = -( invTimeStep * ERROR_REDUCTION ) * ( cross * vecX );
1823 c1[4] = -( invTimeStep * ERROR_REDUCTION ) * ( cross * vecY );
1825 c1.Clamp( -ERROR_REDUCTION_MAX, ERROR_REDUCTION_MAX );
1828 steering->Add( physics, invTimeStep );
1830 else if ( coneLimit ) {
1831 coneLimit->Add( physics, invTimeStep );
1837 idAFConstraint_Hinge::ApplyFriction
1840 void idAFConstraint_Hinge::ApplyFriction( float invTimeStep ) {
1842 float invMass, currentFriction;
1844 currentFriction = GetFriction();
1846 if ( currentFriction <= 0.0f ) {
1850 if ( af_useImpulseFriction.GetBool() || af_useJointImpulseFriction.GetBool() ) {
1852 angular = body1->GetAngularVelocity();
1853 invMass = body1->GetInverseMass();
1855 angular -= body2->GetAngularVelocity();
1856 invMass += body2->GetInverseMass();
1859 angular *= currentFriction / invMass;
1861 body1->SetAngularVelocity( body1->GetAngularVelocity() - angular * body1->GetInverseMass() );
1863 body2->SetAngularVelocity( body2->GetAngularVelocity() + angular * body2->GetInverseMass() );
1868 fc = new idAFConstraint_HingeFriction;
1872 fc->Add( physics, invTimeStep );
1878 idAFConstraint_Hinge::GetForce
1881 void idAFConstraint_Hinge::GetForce( idAFBody *body, idVec6 &force ) {
1882 idAFConstraint::GetForce( body, force );
1883 // FIXME: add limit force
1888 idAFConstraint_Hinge::Translate
1891 void idAFConstraint_Hinge::Translate( const idVec3 &translation ) {
1893 anchor2 += translation;
1896 coneLimit->Translate( translation );
1902 idAFConstraint_Hinge::Rotate
1905 void idAFConstraint_Hinge::Rotate( const idRotation &rotation ) {
1907 anchor2 *= rotation;
1908 axis2 *= rotation.ToMat3();
1911 coneLimit->Rotate( rotation );
1917 idAFConstraint_Hinge::GetCenter
1920 void idAFConstraint_Hinge::GetCenter( idVec3 ¢er ) {
1921 center = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
1926 idAFConstraint_Hinge::DebugDraw
1929 void idAFConstraint_Hinge::DebugDraw( void ) {
1931 idVec3 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
1932 idVec3 x1 = axis1 * body1->GetWorldAxis();
1933 x1.OrthogonalBasis( vecX, vecY );
1935 gameRenderWorld->DebugArrow( colorBlue, a1 - 4.0f * x1, a1 + 4.0f * x1, 1 );
1936 gameRenderWorld->DebugLine( colorBlue, a1 - 2.0f * vecX, a1 + 2.0f * vecX );
1937 gameRenderWorld->DebugLine( colorBlue, a1 - 2.0f * vecY, a1 + 2.0f * vecY );
1939 if ( af_showLimits.GetBool() ) {
1941 coneLimit->DebugDraw();
1948 idAFConstraint_Hinge::Save
1951 void idAFConstraint_Hinge::Save( idSaveGame *saveFile ) const {
1952 idAFConstraint::Save( saveFile );
1953 saveFile->WriteVec3( anchor1 );
1954 saveFile->WriteVec3( anchor2 );
1955 saveFile->WriteVec3( axis1 );
1956 saveFile->WriteVec3( axis2 );
1957 saveFile->WriteMat3( initialAxis );
1958 saveFile->WriteFloat( friction );
1960 saveFile->WriteBool( true );
1961 coneLimit->Save( saveFile );
1963 saveFile->WriteBool( false );
1966 saveFile->WriteBool( true );
1967 steering->Save( saveFile );
1969 saveFile->WriteBool( false );
1972 saveFile->WriteBool( true );
1973 fc->Save( saveFile );
1975 saveFile->WriteBool( false );
1981 idAFConstraint_Hinge::Restore
1984 void idAFConstraint_Hinge::Restore( idRestoreGame *saveFile ) {
1986 idAFConstraint::Restore( saveFile );
1987 saveFile->ReadVec3( anchor1 );
1988 saveFile->ReadVec3( anchor2 );
1989 saveFile->ReadVec3( axis1 );
1990 saveFile->ReadVec3( axis2 );
1991 saveFile->ReadMat3( initialAxis );
1992 saveFile->ReadFloat( friction );
1994 saveFile->ReadBool( b );
1997 coneLimit = new idAFConstraint_ConeLimit;
1999 coneLimit->SetPhysics( physics );
2000 coneLimit->Restore( saveFile );
2002 saveFile->ReadBool( b );
2005 steering = new idAFConstraint_HingeSteering;
2007 steering->Setup( this );
2008 steering->Restore( saveFile );
2010 saveFile->ReadBool( b );
2013 fc = new idAFConstraint_HingeFriction;
2016 fc->Restore( saveFile );
2021 //===============================================================
2023 // idAFConstraint_HingeFriction
2025 //===============================================================
2029 idAFConstraint_HingeFriction::idAFConstraint_HingeFriction
2032 idAFConstraint_HingeFriction::idAFConstraint_HingeFriction( void ) {
2033 type = CONSTRAINT_FRICTION;
2034 name = "hingeFriction";
2037 fl.allowPrimary = false;
2038 fl.frameConstraint = true;
2043 idAFConstraint_HingeFriction::Setup
2046 void idAFConstraint_HingeFriction::Setup( idAFConstraint_Hinge *h ) {
2048 body1 = h->GetBody1();
2049 body2 = h->GetBody2();
2054 idAFConstraint_HingeFriction::Evaluate
2057 void idAFConstraint_HingeFriction::Evaluate( float invTimeStep ) {
2063 idAFConstraint_HingeFriction::ApplyFriction
2066 void idAFConstraint_HingeFriction::ApplyFriction( float invTimeStep ) {
2072 idAFConstraint_HingeFriction::Add
2075 bool idAFConstraint_HingeFriction::Add( idPhysics_AF *phys, float invTimeStep ) {
2081 f = hinge->GetFriction() * hinge->GetMultiplier().Length();
2089 hinge->GetAxis( a1, a2 );
2091 a1 *= body1->GetWorldAxis();
2094 J1.SubVec6(0).SubVec3(0).Zero();
2095 J1.SubVec6(0).SubVec3(1) = a1;
2098 a2 *= body2->GetWorldAxis();
2101 J2.SubVec6(0).SubVec3(0).Zero();
2102 J2.SubVec6(0).SubVec3(1) = -a2;
2105 physics->AddFrameConstraint( this );
2112 idAFConstraint_HingeFriction::Translate
2115 void idAFConstraint_HingeFriction::Translate( const idVec3 &translation ) {
2120 idAFConstraint_HingeFriction::Rotate
2123 void idAFConstraint_HingeFriction::Rotate( const idRotation &rotation ) {
2127 //===============================================================
2129 // idAFConstraint_HingeSteering
2131 //===============================================================
2135 idAFConstraint_HingeSteering::idAFConstraint_HingeSteering
2138 idAFConstraint_HingeSteering::idAFConstraint_HingeSteering( void ) {
2139 type = CONSTRAINT_HINGESTEERING;
2140 name = "hingeFriction";
2143 fl.allowPrimary = false;
2144 fl.frameConstraint = true;
2146 epsilon = LCP_EPSILON;
2151 idAFConstraint_HingeSteering::Save
2154 void idAFConstraint_HingeSteering::Save( idSaveGame *saveFile ) const {
2155 saveFile->WriteFloat(steerAngle);
2156 saveFile->WriteFloat(steerSpeed);
2157 saveFile->WriteFloat(epsilon);
2162 idAFConstraint_HingeSteering::Restore
2165 void idAFConstraint_HingeSteering::Restore( idRestoreGame *saveFile ) {
2166 saveFile->ReadFloat(steerAngle);
2167 saveFile->ReadFloat(steerSpeed);
2168 saveFile->ReadFloat(epsilon);
2173 idAFConstraint_HingeSteering::Setup
2176 void idAFConstraint_HingeSteering::Setup( idAFConstraint_Hinge *h ) {
2178 body1 = h->GetBody1();
2179 body2 = h->GetBody2();
2184 idAFConstraint_HingeSteering::Evaluate
2187 void idAFConstraint_HingeSteering::Evaluate( float invTimeStep ) {
2193 idAFConstraint_HingeSteering::ApplyFriction
2196 void idAFConstraint_HingeSteering::ApplyFriction( float invTimeStep ) {
2202 idAFConstraint_HingeSteering::Add
2205 bool idAFConstraint_HingeSteering::Add( idPhysics_AF *phys, float invTimeStep ) {
2211 hinge->GetAxis( a1, a2 );
2212 angle = hinge->GetAngle();
2214 a1 *= body1->GetWorldAxis();
2217 J1.SubVec6(0).SubVec3(0).Zero();
2218 J1.SubVec6(0).SubVec3(1) = a1;
2221 a2 *= body2->GetWorldAxis();
2224 J2.SubVec6(0).SubVec3(0).Zero();
2225 J2.SubVec6(0).SubVec3(1) = -a2;
2228 speed = steerAngle - angle;
2229 if ( steerSpeed != 0.0f ) {
2230 if ( speed > steerSpeed ) {
2233 else if ( speed < -steerSpeed ) {
2234 speed = -steerSpeed;
2238 c1[0] = DEG2RAD( speed ) * invTimeStep;
2240 physics->AddFrameConstraint( this );
2247 idAFConstraint_HingeSteering::Translate
2250 void idAFConstraint_HingeSteering::Translate( const idVec3 &translation ) {
2255 idAFConstraint_HingeSteering::Rotate
2258 void idAFConstraint_HingeSteering::Rotate( const idRotation &rotation ) {
2262 //===============================================================
2264 // idAFConstraint_Slider
2266 //===============================================================
2270 idAFConstraint_Slider::idAFConstraint_Slider
2273 idAFConstraint_Slider::idAFConstraint_Slider( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
2275 type = CONSTRAINT_SLIDER;
2277 this->body1 = body1;
2278 this->body2 = body2;
2280 fl.allowPrimary = true;
2281 fl.noCollision = true;
2284 offset = ( body1->GetWorldOrigin() - body2->GetWorldOrigin() ) * body1->GetWorldAxis().Transpose();
2285 relAxis = body1->GetWorldAxis() * body2->GetWorldAxis().Transpose();
2288 offset = body1->GetWorldOrigin();
2289 relAxis = body1->GetWorldAxis();
2295 idAFConstraint_Slider::SetAxis
2298 void idAFConstraint_Slider::SetAxis( const idVec3 &ax ) {
2301 // get normalized axis relative to body1
2303 normAxis.Normalize();
2305 axis = normAxis * body2->GetWorldAxis().Transpose();
2314 idAFConstraint_Slider::Evaluate
2317 void idAFConstraint_Slider::Evaluate( float invTimeStep ) {
2318 idVec3 vecX, vecY, ofs;
2322 master = body2 ? body2 : physics->GetMasterBody();
2325 (axis * master->GetWorldAxis()).OrthogonalBasis( vecX, vecY );
2326 ofs = master->GetWorldOrigin() + master->GetWorldAxis() * offset - body1->GetWorldOrigin();
2327 r = ( body1->GetWorldAxis().Transpose() * (relAxis * master->GetWorldAxis()) ).ToRotation();
2330 axis.OrthogonalBasis( vecX, vecY );
2331 ofs = offset - body1->GetWorldOrigin();
2332 r = ( body1->GetWorldAxis().Transpose() * relAxis ).ToRotation();
2335 J1.Set( mat3_zero, mat3_identity,
2336 idMat3( vecX, vecY, vec3_origin ), mat3_zero );
2341 J2.Set( mat3_zero, -mat3_identity,
2342 idMat3( -vecX, -vecY, vec3_origin ), mat3_zero );
2349 c1.SubVec3(0) = -( invTimeStep * ERROR_REDUCTION ) * ( r.GetVec() * - (float) DEG2RAD( r.GetAngle() ) );
2351 c1[3] = -( invTimeStep * ERROR_REDUCTION ) * ( vecX * ofs );
2352 c1[4] = -( invTimeStep * ERROR_REDUCTION ) * ( vecY * ofs );
2354 c1.Clamp( -ERROR_REDUCTION_MAX, ERROR_REDUCTION_MAX );
2359 idAFConstraint_Slider::ApplyFriction
2362 void idAFConstraint_Slider::ApplyFriction( float invTimeStep ) {
2368 idAFConstraint_Slider::Translate
2371 void idAFConstraint_Slider::Translate( const idVec3 &translation ) {
2373 offset += translation;
2379 idAFConstraint_Slider::Rotate
2382 void idAFConstraint_Slider::Rotate( const idRotation &rotation ) {
2390 idAFConstraint_Slider::GetCenter
2393 void idAFConstraint_Slider::GetCenter( idVec3 ¢er ) {
2396 master = body2 ? body2 : physics->GetMasterBody();
2398 center = master->GetWorldOrigin() + master->GetWorldAxis() * offset - body1->GetWorldOrigin();
2401 center = offset - body1->GetWorldOrigin();
2407 idAFConstraint_Slider::DebugDraw
2410 void idAFConstraint_Slider::DebugDraw( void ) {
2414 master = body2 ? body2 : physics->GetMasterBody();
2416 ofs = master->GetWorldOrigin() + master->GetWorldAxis() * offset - body1->GetWorldOrigin();
2419 ofs = offset - body1->GetWorldOrigin();
2421 gameRenderWorld->DebugLine( colorGreen, ofs, ofs + axis * body1->GetWorldAxis() );
2426 idAFConstraint_Slider::Save
2429 void idAFConstraint_Slider::Save( idSaveGame *saveFile ) const {
2430 idAFConstraint::Save( saveFile );
2431 saveFile->WriteVec3( axis );
2432 saveFile->WriteVec3( offset );
2433 saveFile->WriteMat3( relAxis );
2438 idAFConstraint_Slider::Restore
2441 void idAFConstraint_Slider::Restore( idRestoreGame *saveFile ) {
2442 idAFConstraint::Restore( saveFile );
2443 saveFile->ReadVec3( axis );
2444 saveFile->ReadVec3( offset );
2445 saveFile->ReadMat3( relAxis );
2449 //===============================================================
2451 // idAFConstraint_Line
2453 //===============================================================
2457 idAFConstraint_Line::idAFConstraint_Line
2460 idAFConstraint_Line::idAFConstraint_Line( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
2461 assert( 0 ); // FIXME: implement
2466 idAFConstraint_Line::Evaluate
2469 void idAFConstraint_Line::Evaluate( float invTimeStep ) {
2470 assert( 0 ); // FIXME: implement
2475 idAFConstraint_Line::ApplyFriction
2478 void idAFConstraint_Line::ApplyFriction( float invTimeStep ) {
2479 assert( 0 ); // FIXME: implement
2484 idAFConstraint_Line::Translate
2487 void idAFConstraint_Line::Translate( const idVec3 &translation ) {
2488 assert( 0 ); // FIXME: implement
2493 idAFConstraint_Line::Rotate
2496 void idAFConstraint_Line::Rotate( const idRotation &rotation ) {
2497 assert( 0 ); // FIXME: implement
2502 idAFConstraint_Line::DebugDraw
2505 void idAFConstraint_Line::DebugDraw( void ) {
2506 assert( 0 ); // FIXME: implement
2510 //===============================================================
2512 // idAFConstraint_Plane
2514 //===============================================================
2518 idAFConstraint_Plane::idAFConstraint_Plane
2521 idAFConstraint_Plane::idAFConstraint_Plane( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
2523 type = CONSTRAINT_PLANE;
2525 this->body1 = body1;
2526 this->body2 = body2;
2528 fl.allowPrimary = true;
2529 fl.noCollision = true;
2534 idAFConstraint_Plane::SetPlane
2537 void idAFConstraint_Plane::SetPlane( const idVec3 &normal, const idVec3 &anchor ) {
2538 // get anchor relative to center of mass of body1
2539 anchor1 = ( anchor - body1->GetWorldOrigin() ) * body1->GetWorldAxis().Transpose();
2541 // get anchor relative to center of mass of body2
2542 anchor2 = ( anchor - body2->GetWorldOrigin() ) * body2->GetWorldAxis().Transpose();
2543 planeNormal = normal * body2->GetWorldAxis().Transpose();
2547 planeNormal = normal;
2553 idAFConstraint_Plane::Evaluate
2556 void idAFConstraint_Plane::Evaluate( float invTimeStep ) {
2557 idVec3 a1, a2, normal, p;
2561 master = body2 ? body2 : physics->GetMasterBody();
2563 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
2565 a2 = master->GetWorldOrigin() + anchor2 * master->GetWorldAxis();
2566 normal = planeNormal * master->GetWorldAxis();
2570 normal = planeNormal;
2573 p = a1 - body1->GetWorldOrigin();
2574 v.SubVec3(0) = normal;
2575 v.SubVec3(1) = p.Cross( normal );
2576 J1.Set( 1, 6, v.ToFloatPtr() );
2579 p = a1 - body2->GetWorldOrigin();
2580 v.SubVec3(0) = -normal;
2581 v.SubVec3(1) = p.Cross( -normal );
2582 J2.Set( 1, 6, v.ToFloatPtr() );
2585 c1[0] = -( invTimeStep * ERROR_REDUCTION ) * (a1 * normal - a2 * normal);
2587 c1.Clamp( -ERROR_REDUCTION_MAX, ERROR_REDUCTION_MAX );
2592 idAFConstraint_Plane::ApplyFriction
2595 void idAFConstraint_Plane::ApplyFriction( float invTimeStep ) {
2601 idAFConstraint_Plane::Translate
2604 void idAFConstraint_Plane::Translate( const idVec3 &translation ) {
2606 anchor2 += translation;
2612 idAFConstraint_Plane::Rotate
2615 void idAFConstraint_Plane::Rotate( const idRotation &rotation ) {
2617 anchor2 *= rotation;
2618 planeNormal *= rotation.ToMat3();
2624 idAFConstraint_Plane::DebugDraw
2627 void idAFConstraint_Plane::DebugDraw( void ) {
2628 idVec3 a1, normal, right, up;
2631 master = body2 ? body2 : physics->GetMasterBody();
2633 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
2635 normal = planeNormal * master->GetWorldAxis();
2638 normal = planeNormal;
2640 normal.NormalVectors( right, up );
2645 gameRenderWorld->DebugLine( colorCyan, a1 - right, a1 + right );
2646 gameRenderWorld->DebugLine( colorCyan, a1 - up, a1 + up );
2647 gameRenderWorld->DebugArrow( colorCyan, a1, a1 + normal, 1 );
2652 idAFConstraint_Plane::Save
2655 void idAFConstraint_Plane::Save( idSaveGame *saveFile ) const {
2656 idAFConstraint::Save( saveFile );
2657 saveFile->WriteVec3( anchor1 );
2658 saveFile->WriteVec3( anchor2 );
2659 saveFile->WriteVec3( planeNormal );
2664 idAFConstraint_Plane::Restore
2667 void idAFConstraint_Plane::Restore( idRestoreGame *saveFile ) {
2668 idAFConstraint::Restore( saveFile );
2669 saveFile->ReadVec3( anchor1 );
2670 saveFile->ReadVec3( anchor2 );
2671 saveFile->ReadVec3( planeNormal );
2675 //===============================================================
2677 // idAFConstraint_Spring
2679 //===============================================================
2683 idAFConstraint_Spring::idAFConstraint_Spring
2686 idAFConstraint_Spring::idAFConstraint_Spring( const idStr &name, idAFBody *body1, idAFBody *body2 ) {
2688 type = CONSTRAINT_SPRING;
2690 this->body1 = body1;
2691 this->body2 = body2;
2693 fl.allowPrimary = false;
2694 kstretch = kcompress = damping = 1.0f;
2695 minLength = maxLength = restLength = 0.0f;
2700 idAFConstraint_Spring::SetAnchor
2703 void idAFConstraint_Spring::SetAnchor( const idVec3 &worldAnchor1, const idVec3 &worldAnchor2 ) {
2704 // get anchor relative to center of mass of body1
2705 anchor1 = ( worldAnchor1 - body1->GetWorldOrigin() ) * body1->GetWorldAxis().Transpose();
2707 // get anchor relative to center of mass of body2
2708 anchor2 = ( worldAnchor2 - body2->GetWorldOrigin() ) * body2->GetWorldAxis().Transpose();
2711 anchor2 = worldAnchor2;
2717 idAFConstraint_Spring::SetSpring
2720 void idAFConstraint_Spring::SetSpring( const float stretch, const float compress, const float damping, const float restLength ) {
2721 assert( stretch >= 0.0f && compress >= 0.0f && restLength >= 0.0f );
2722 this->kstretch = stretch;
2723 this->kcompress = compress;
2724 this->damping = damping;
2725 this->restLength = restLength;
2730 idAFConstraint_Spring::SetLimit
2733 void idAFConstraint_Spring::SetLimit( const float minLength, const float maxLength ) {
2734 assert( minLength >= 0.0f && maxLength >= 0.0f && maxLength >= minLength );
2735 this->minLength = minLength;
2736 this->maxLength = maxLength;
2741 idAFConstraint_Spring::Evaluate
2744 void idAFConstraint_Spring::Evaluate( float invTimeStep ) {
2745 idVec3 a1, a2, velocity1, velocity2, force;
2747 float d, dampingForce, length, error;
2751 master = body2 ? body2 : physics->GetMasterBody();
2753 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
2754 velocity1 = body1->GetPointVelocity( a1 );
2757 a2 = master->GetWorldOrigin() + anchor2 * master->GetWorldAxis();
2758 velocity2 = master->GetPointVelocity( a2 );
2768 dampingForce = damping * idMath::Fabs( (velocity2 - velocity1) * force ) / d;
2771 dampingForce = 0.0f;
2773 length = force.Normalize();
2775 if ( length > restLength ) {
2776 if ( kstretch > 0.0f ) {
2777 idVec3 springForce = force * ( Square( length - restLength ) * kstretch - dampingForce );
2778 body1->AddForce( a1, springForce );
2780 master->AddForce( a2, -springForce );
2785 if ( kcompress > 0.0f ) {
2786 idVec3 springForce = force * -( Square( restLength - length ) * kcompress - dampingForce );
2787 body1->AddForce( a1, springForce );
2789 master->AddForce( a2, -springForce );
2794 // check for spring limits
2795 if ( length < minLength ) {
2797 error = minLength - length;
2800 else if ( maxLength > 0.0f && length > maxLength ) {
2801 error = length - maxLength;
2810 a1 -= body1->GetWorldOrigin();
2811 v1.SubVec3(0) = force;
2812 v1.SubVec3(1) = a1.Cross( force );
2813 J1.Set( 1, 6, v1.ToFloatPtr() );
2815 a2 -= body2->GetWorldOrigin();
2816 v2.SubVec3(0) = -force;
2817 v2.SubVec3(1) = a2.Cross( -force );
2818 J2.Set( 1, 6, v2.ToFloatPtr() );
2820 c1[0] = -( invTimeStep * ERROR_REDUCTION ) * error;
2828 c1.Clamp( -ERROR_REDUCTION_MAX, ERROR_REDUCTION_MAX );
2833 idAFConstraint_Spring::ApplyFriction
2836 void idAFConstraint_Spring::ApplyFriction( float invTimeStep ) {
2842 idAFConstraint_Spring::Translate
2845 void idAFConstraint_Spring::Translate( const idVec3 &translation ) {
2847 anchor2 += translation;
2853 idAFConstraint_Spring::Rotate
2856 void idAFConstraint_Spring::Rotate( const idRotation &rotation ) {
2858 anchor2 *= rotation;
2864 idAFConstraint_Spring::GetCenter
2867 void idAFConstraint_Spring::GetCenter( idVec3 ¢er ) {
2871 master = body2 ? body2 : physics->GetMasterBody();
2872 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
2874 a2 = master->GetWorldOrigin() + anchor2 * master->GetWorldAxis();
2879 center = ( a1 + a2 ) * 0.5f;
2884 idAFConstraint_Spring::DebugDraw
2887 void idAFConstraint_Spring::DebugDraw( void ) {
2890 idVec3 a1, a2, dir, mid, p;
2892 master = body2 ? body2 : physics->GetMasterBody();
2893 a1 = body1->GetWorldOrigin() + anchor1 * body1->GetWorldAxis();
2895 a2 = master->GetWorldOrigin() + anchor2 * master->GetWorldAxis();
2901 mid = a1 + 0.5f * dir;
2902 length = dir.Normalize();
2905 gameRenderWorld->DebugLine( colorGreen, a1, a2 );
2908 p = restLength * 0.5f * dir;
2909 gameRenderWorld->DebugCircle( colorWhite, mid + p, dir, 1.0f, 10 );
2910 gameRenderWorld->DebugCircle( colorWhite, mid - p, dir, 1.0f, 10 );
2911 if ( restLength > length ) {
2912 gameRenderWorld->DebugLine( colorWhite, a2, mid + p );
2913 gameRenderWorld->DebugLine( colorWhite, a1, mid - p );
2916 if ( minLength > 0.0f ) {
2918 gameRenderWorld->DebugCircle( colorBlue, mid + minLength * 0.5f * dir, dir, 2.0f, 10 );
2919 gameRenderWorld->DebugCircle( colorBlue, mid - minLength * 0.5f * dir, dir, 2.0f, 10 );
2922 if ( maxLength > 0.0f ) {
2924 gameRenderWorld->DebugCircle( colorRed, mid + maxLength * 0.5f * dir, dir, 2.0f, 10 );
2925 gameRenderWorld->DebugCircle( colorRed, mid - maxLength * 0.5f * dir, dir, 2.0f, 10 );
2931 idAFConstraint_Spring::Save
2934 void idAFConstraint_Spring::Save( idSaveGame *saveFile ) const {
2935 idAFConstraint::Save( saveFile );
2936 saveFile->WriteVec3( anchor1 );
2937 saveFile->WriteVec3( anchor2 );
2938 saveFile->WriteFloat( kstretch );
2939 saveFile->WriteFloat( kcompress );
2940 saveFile->WriteFloat( damping );
2941 saveFile->WriteFloat( restLength );
2942 saveFile->WriteFloat( minLength );
2943 saveFile->WriteFloat( maxLength );
2948 idAFConstraint_Spring::Restore
2951 void idAFConstraint_Spring::Restore( idRestoreGame *saveFile ) {
2952 idAFConstraint::Restore( saveFile );
2953 saveFile->ReadVec3( anchor1 );
2954 saveFile->ReadVec3( anchor2 );
2955 saveFile->ReadFloat( kstretch );
2956 saveFile->ReadFloat( kcompress );
2957 saveFile->ReadFloat( damping );
2958 saveFile->ReadFloat( restLength );
2959 saveFile->ReadFloat( minLength );
2960 saveFile->ReadFloat( maxLength );
2964 //===============================================================
2966 // idAFConstraint_Contact
2968 //===============================================================
2972 idAFConstraint_Contact::idAFConstraint_Contact
2975 idAFConstraint_Contact::idAFConstraint_Contact( void ) {
2977 type = CONSTRAINT_CONTACT;
2980 fl.allowPrimary = false;
2981 fl.frameConstraint = true;
2986 idAFConstraint_Contact::~idAFConstraint_Contact
2989 idAFConstraint_Contact::~idAFConstraint_Contact( void ) {
2997 idAFConstraint_Contact::Setup
3000 void idAFConstraint_Contact::Setup( idAFBody *b1, idAFBody *b2, contactInfo_t &c ) {
3004 float minBounceVelocity = 2.0f;
3012 p = c.point - body1->GetWorldOrigin();
3013 v.SubVec3(0) = c.normal;
3014 v.SubVec3(1) = p.Cross( c.normal );
3015 J1.Set( 1, 6, v.ToFloatPtr() );
3016 vel = v.SubVec3(0) * body1->GetLinearVelocity() + v.SubVec3(1) * body1->GetAngularVelocity();
3019 p = c.point - body2->GetWorldOrigin();
3020 v.SubVec3(0) = -c.normal;
3021 v.SubVec3(1) = p.Cross( -c.normal );
3022 J2.Set( 1, 6, v.ToFloatPtr() );
3023 vel += v.SubVec3(0) * body2->GetLinearVelocity() + v.SubVec3(1) * body2->GetAngularVelocity();
3027 if ( body1->GetBouncyness() > 0.0f && -vel > minBounceVelocity ) {
3028 c1[0] = body1->GetBouncyness() * vel;
3033 e[0] = CONTACT_LCP_EPSILON;
3035 hi[0] = idMath::INFINITY;
3036 boxConstraint = NULL;
3042 idAFConstraint_Contact::Evaluate
3045 void idAFConstraint_Contact::Evaluate( float invTimeStep ) {
3051 idAFConstraint_Contact::ApplyFriction
3054 void idAFConstraint_Contact::ApplyFriction( float invTimeStep ) {
3055 idVec3 r, velocity, normal, dir1, dir2;
3056 float friction, magnitude, forceNumerator, forceDenominator;
3059 friction = body1->GetContactFriction();
3060 if ( body2 && body2->GetContactFriction() < friction ) {
3061 friction = body2->GetContactFriction();
3064 friction *= physics->GetContactFrictionScale();
3066 if ( friction <= 0.0f ) {
3070 // seperate friction per contact is silly but it's fast and often looks close enough
3071 if ( af_useImpulseFriction.GetBool() ) {
3073 impulse.SetData( 6, VECX_ALLOCA( 6 ) );
3074 dv.SetData( 6, VECX_ALLOCA( 6 ) );
3076 // calculate velocity in the contact plane
3077 r = contact.point - body1->GetWorldOrigin();
3078 velocity = body1->GetLinearVelocity() + body1->GetAngularVelocity().Cross( r );
3079 velocity -= contact.normal * velocity * contact.normal;
3081 // get normalized direction of friction and magnitude of velocity
3083 magnitude = normal.Normalize();
3085 forceNumerator = friction * magnitude;
3086 forceDenominator = body1->GetInverseMass() + ( ( body1->GetInverseWorldInertia() * r.Cross( normal ) ).Cross( r ) * normal );
3087 impulse.SubVec3(0) = (forceNumerator / forceDenominator) * normal;
3088 impulse.SubVec3(1) = r.Cross( impulse.SubVec3(0) );
3089 body1->InverseWorldSpatialInertiaMultiply( dv, impulse.ToFloatPtr() );
3091 // modify velocity with friction force
3092 body1->SetLinearVelocity( body1->GetLinearVelocity() + dv.SubVec3(0) );
3093 body1->SetAngularVelocity( body1->GetAngularVelocity() + dv.SubVec3(1) );
3098 fc = new idAFConstraint_ContactFriction;
3100 // call setup each frame because contact constraints are re-used for different bodies
3102 fc->Add( physics, invTimeStep );
3108 idAFConstraint_Contact::Translate
3111 void idAFConstraint_Contact::Translate( const idVec3 &translation ) {
3112 assert( 0 ); // contact should never be translated
3117 idAFConstraint_Contact::Rotate
3120 void idAFConstraint_Contact::Rotate( const idRotation &rotation ) {
3121 assert( 0 ); // contact should never be rotated
3126 idAFConstraint_Contact::GetCenter
3129 void idAFConstraint_Contact::GetCenter( idVec3 ¢er ) {
3130 center = contact.point;
3135 idAFConstraint_Contact::DebugDraw
3138 void idAFConstraint_Contact::DebugDraw( void ) {
3140 contact.normal.NormalVectors( x, y );
3141 gameRenderWorld->DebugLine( colorWhite, contact.point, contact.point + 6.0f * contact.normal );
3142 gameRenderWorld->DebugLine( colorWhite, contact.point - 2.0f * x, contact.point + 2.0f * x );
3143 gameRenderWorld->DebugLine( colorWhite, contact.point - 2.0f * y, contact.point + 2.0f * y );
3147 //===============================================================
3149 // idAFConstraint_ContactFriction
3151 //===============================================================
3155 idAFConstraint_ContactFriction::idAFConstraint_ContactFriction
3158 idAFConstraint_ContactFriction::idAFConstraint_ContactFriction( void ) {
3159 type = CONSTRAINT_FRICTION;
3160 name = "contactFriction";
3163 fl.allowPrimary = false;
3164 fl.frameConstraint = true;
3169 idAFConstraint_ContactFriction::Setup
3172 void idAFConstraint_ContactFriction::Setup( idAFConstraint_Contact *cc ) {
3174 body1 = cc->GetBody1();
3175 body2 = cc->GetBody2();
3180 idAFConstraint_ContactFriction::Evaluate
3183 void idAFConstraint_ContactFriction::Evaluate( float invTimeStep ) {
3189 idAFConstraint_ContactFriction::ApplyFriction
3192 void idAFConstraint_ContactFriction::ApplyFriction( float invTimeStep ) {
3198 idAFConstraint_ContactFriction::Add
3201 bool idAFConstraint_ContactFriction::Add( idPhysics_AF *phys, float invTimeStep ) {
3202 idVec3 r, dir1, dir2;
3208 friction = body1->GetContactFriction() * physics->GetContactFrictionScale();
3210 // if the body only has friction in one direction
3211 if ( body1->GetFrictionDirection( dir1 ) ) {
3212 // project the friction direction into the contact plane
3213 dir1 -= dir1 * cc->GetContact().normal * dir1;
3216 r = cc->GetContact().point - body1->GetWorldOrigin();
3219 J1.SubVec6(0).SubVec3(0) = dir1;
3220 J1.SubVec6(0).SubVec3(1) = r.Cross( dir1 );
3225 r = cc->GetContact().point - body2->GetWorldOrigin();
3228 J2.SubVec6(0).SubVec3(0) = -dir1;
3229 J2.SubVec6(0).SubVec3(1) = r.Cross( -dir1 );
3240 // get two friction directions orthogonal to contact normal
3241 cc->GetContact().normal.NormalVectors( dir1, dir2 );
3243 r = cc->GetContact().point - body1->GetWorldOrigin();
3246 J1.SubVec6(0).SubVec3(0) = dir1;
3247 J1.SubVec6(0).SubVec3(1) = r.Cross( dir1 );
3248 J1.SubVec6(1).SubVec3(0) = dir2;
3249 J1.SubVec6(1).SubVec3(1) = r.Cross( dir2 );
3251 c1[0] = c1[1] = 0.0f;
3254 r = cc->GetContact().point - body2->GetWorldOrigin();
3257 J2.SubVec6(0).SubVec3(0) = -dir1;
3258 J2.SubVec6(0).SubVec3(1) = r.Cross( -dir1 );
3259 J2.SubVec6(1).SubVec3(0) = -dir2;
3260 J2.SubVec6(1).SubVec3(1) = r.Cross( -dir2 );
3262 c2[0] = c2[1] = 0.0f;
3264 if ( body2->GetContactFriction() < friction ) {
3265 friction = body2->GetContactFriction();
3278 if ( body1->GetContactMotorDirection( dir1 ) && body1->GetContactMotorForce() > 0.0f ) {
3279 // project the motor force direction into the contact plane
3280 dir1 -= dir1 * cc->GetContact().normal * dir1;
3283 r = cc->GetContact().point - body1->GetWorldOrigin();
3285 newRow = J1.GetNumRows();
3286 J1.ChangeSize( newRow+1, J1.GetNumColumns() );
3287 J1.SubVec6(newRow).SubVec3(0) = -dir1;
3288 J1.SubVec6(newRow).SubVec3(1) = r.Cross( -dir1 );
3289 c1.ChangeSize( newRow+1 );
3290 c1[newRow] = body1->GetContactMotorVelocity();
3293 r = cc->GetContact().point - body2->GetWorldOrigin();
3295 J2.ChangeSize( newRow+1, J2.GetNumColumns() );
3296 J2.SubVec6(newRow).SubVec3(0) = -dir1;
3297 J2.SubVec6(newRow).SubVec3(1) = r.Cross( -dir1 );
3298 c2.ChangeSize( newRow+1 );
3302 lo[newRow] = -body1->GetContactMotorForce();
3303 hi[newRow] = body1->GetContactMotorForce();
3304 boxIndex[newRow] = -1;
3307 physics->AddFrameConstraint( this );
3314 idAFConstraint_ContactFriction::Translate
3317 void idAFConstraint_ContactFriction::Translate( const idVec3 &translation ) {
3322 idAFConstraint_ContactFriction::Rotate
3325 void idAFConstraint_ContactFriction::Rotate( const idRotation &rotation ) {
3330 idAFConstraint_ContactFriction::DebugDraw
3333 void idAFConstraint_ContactFriction::DebugDraw( void ) {
3337 //===============================================================
3339 // idAFConstraint_ConeLimit
3341 //===============================================================
3345 idAFConstraint_ConeLimit::idAFConstraint_ConeLimit
3348 idAFConstraint_ConeLimit::idAFConstraint_ConeLimit( void ) {
3349 type = CONSTRAINT_CONELIMIT;
3352 fl.allowPrimary = false;
3353 fl.frameConstraint = true;
3358 idAFConstraint_ConeLimit::Setup
3360 the coneAnchor is the top of the cone in body2 space
3361 the coneAxis is the axis of the cone in body2 space
3362 the coneAngle is the angle the cone hull makes at the top
3363 the body1Axis is the axis in body1 space that should stay within the cone
3366 void idAFConstraint_ConeLimit::Setup( idAFBody *b1, idAFBody *b2, const idVec3 &coneAnchor, const idVec3 &coneAxis, const float coneAngle, const idVec3 &body1Axis ) {
3369 this->coneAxis = coneAxis;
3370 this->coneAxis.Normalize();
3371 this->coneAnchor = coneAnchor;
3372 this->body1Axis = body1Axis;
3373 this->body1Axis.Normalize();
3374 this->cosAngle = (float) cos( DEG2RAD( coneAngle * 0.5f ) );
3375 this->sinHalfAngle = (float) sin( DEG2RAD( coneAngle * 0.25f ) );
3376 this->cosHalfAngle = (float) cos( DEG2RAD( coneAngle * 0.25f ) );
3381 idAFConstraint_ConeLimit::SetAnchor
3384 void idAFConstraint_ConeLimit::SetAnchor( const idVec3 &coneAnchor ) {
3385 this->coneAnchor = coneAnchor;
3390 idAFConstraint_ConeLimit::SetBody1Axis
3393 void idAFConstraint_ConeLimit::SetBody1Axis( const idVec3 &body1Axis ) {
3394 this->body1Axis = body1Axis;
3399 idAFConstraint_ConeLimit::Evaluate
3402 void idAFConstraint_ConeLimit::Evaluate( float invTimeStep ) {
3408 idAFConstraint_ConeLimit::ApplyFriction
3411 void idAFConstraint_ConeLimit::ApplyFriction( float invTimeStep ) {
3416 idAFConstraint_ConeLimit::Add
3419 bool idAFConstraint_ConeLimit::Add( idPhysics_AF *phys, float invTimeStep ) {
3421 idVec6 J1row, J2row;
3422 idVec3 ax, anchor, body1ax, normal, coneVector, p1, p2;
3426 if ( af_skipLimits.GetBool() ) {
3427 lm.Zero(); // constraint exerts no force
3433 master = body2 ? body2 : physics->GetMasterBody();
3436 ax = coneAxis * master->GetWorldAxis();
3437 anchor = master->GetWorldOrigin() + coneAnchor * master->GetWorldAxis();
3441 anchor = coneAnchor;
3444 body1ax = body1Axis * body1->GetWorldAxis();
3448 // if the body1 axis is inside the cone
3449 if ( a > cosAngle ) {
3450 lm.Zero(); // constraint exerts no force
3454 // calculate the inward cone normal for the position the body1 axis went outside the cone
3455 normal = body1ax.Cross( ax );
3457 q.x = normal.x * sinHalfAngle;
3458 q.y = normal.y * sinHalfAngle;
3459 q.z = normal.z * sinHalfAngle;
3461 coneVector = ax * q.ToMat3();
3462 normal = coneVector.Cross( ax ).Cross( coneVector );
3465 p1 = anchor + 32.0f * coneVector - body1->GetWorldOrigin();
3467 J1row.SubVec3(0) = normal;
3468 J1row.SubVec3(1) = p1.Cross( normal );
3469 J1.Set( 1, 6, J1row.ToFloatPtr() );
3471 c1[0] = (invTimeStep * LIMIT_ERROR_REDUCTION) * ( normal * (32.0f * body1ax) );
3475 p2 = anchor + 32.0f * coneVector - master->GetWorldOrigin();
3477 J2row.SubVec3(0) = -normal;
3478 J2row.SubVec3(1) = p2.Cross( -normal );
3479 J2.Set( 1, 6, J2row.ToFloatPtr() );
3485 e[0] = LIMIT_LCP_EPSILON;
3487 physics->AddFrameConstraint( this );
3494 idAFConstraint_ConeLimit::Translate
3497 void idAFConstraint_ConeLimit::Translate( const idVec3 &translation ) {
3499 coneAnchor += translation;
3505 idAFConstraint_ConeLimit::Rotate
3508 void idAFConstraint_ConeLimit::Rotate( const idRotation &rotation ) {
3510 coneAnchor *= rotation;
3511 coneAxis *= rotation.ToMat3();
3517 idAFConstraint_ConeLimit::DebugDraw
3520 void idAFConstraint_ConeLimit::DebugDraw( void ) {
3521 idVec3 ax, anchor, x, y, z, start, end;
3522 float sinAngle, a, size = 10.0f;
3525 master = body2 ? body2 : physics->GetMasterBody();
3528 ax = coneAxis * master->GetWorldAxis();
3529 anchor = master->GetWorldOrigin() + coneAnchor * master->GetWorldAxis();
3533 anchor = coneAnchor;
3537 gameRenderWorld->DebugLine( colorGreen, anchor, anchor + size * (body1Axis * body1->GetWorldAxis()) );
3540 ax.NormalVectors( x, y );
3541 sinAngle = idMath::Sqrt( 1.0f - cosAngle * cosAngle );
3542 x *= size * sinAngle;
3543 y *= size * sinAngle;
3544 z = anchor + ax * size * cosAngle;
3546 for ( a = 0.0f; a < 360.0f; a += 45.0f ) {
3547 end = x * (float) cos( DEG2RAD(a + 45.0f) ) + y * (float) sin( DEG2RAD(a + 45.0f) ) + z;
3548 gameRenderWorld->DebugLine( colorMagenta, anchor, start );
3549 gameRenderWorld->DebugLine( colorMagenta, start, end );
3556 idAFConstraint_ConeLimit::Save
3559 void idAFConstraint_ConeLimit::Save( idSaveGame *saveFile ) const {
3560 idAFConstraint::Save( saveFile );
3561 saveFile->WriteVec3( coneAnchor );
3562 saveFile->WriteVec3( coneAxis );
3563 saveFile->WriteVec3( body1Axis );
3564 saveFile->WriteFloat( cosAngle );
3565 saveFile->WriteFloat( sinHalfAngle );
3566 saveFile->WriteFloat( cosHalfAngle );
3567 saveFile->WriteFloat( epsilon );
3572 idAFConstraint_ConeLimit::Restore
3575 void idAFConstraint_ConeLimit::Restore( idRestoreGame *saveFile ) {
3576 idAFConstraint::Restore( saveFile );
3577 saveFile->ReadVec3( coneAnchor );
3578 saveFile->ReadVec3( coneAxis );
3579 saveFile->ReadVec3( body1Axis );
3580 saveFile->ReadFloat( cosAngle );
3581 saveFile->ReadFloat( sinHalfAngle );
3582 saveFile->ReadFloat( cosHalfAngle );
3583 saveFile->ReadFloat( epsilon );
3587 //===============================================================
3589 // idAFConstraint_PyramidLimit
3591 //===============================================================
3595 idAFConstraint_PyramidLimit::idAFConstraint_PyramidLimit
3598 idAFConstraint_PyramidLimit::idAFConstraint_PyramidLimit( void ) {
3599 type = CONSTRAINT_PYRAMIDLIMIT;
3600 name = "pyramidLimit";
3602 fl.allowPrimary = false;
3603 fl.frameConstraint = true;
3608 idAFConstraint_PyramidLimit::Setup
3611 void idAFConstraint_PyramidLimit::Setup( idAFBody *b1, idAFBody *b2, const idVec3 &pyramidAnchor,
3612 const idVec3 &pyramidAxis, const idVec3 &baseAxis,
3613 const float pyramidAngle1, const float pyramidAngle2, const idVec3 &body1Axis ) {
3616 // setup the base and make sure the basis is orthonormal
3617 pyramidBasis[2] = pyramidAxis;
3618 pyramidBasis[2].Normalize();
3619 pyramidBasis[0] = baseAxis;
3620 pyramidBasis[0] -= pyramidBasis[2] * baseAxis * pyramidBasis[2];
3621 pyramidBasis[0].Normalize();
3622 pyramidBasis[1] = pyramidBasis[0].Cross( pyramidBasis[2] );
3624 this->pyramidAnchor = pyramidAnchor;
3626 cosAngle[0] = (float) cos( DEG2RAD( pyramidAngle1 * 0.5f ) );
3627 cosAngle[1] = (float) cos( DEG2RAD( pyramidAngle2 * 0.5f ) );
3628 sinHalfAngle[0] = (float) sin( DEG2RAD( pyramidAngle1 * 0.25f ) );
3629 sinHalfAngle[1] = (float) sin( DEG2RAD( pyramidAngle2 * 0.25f ) );
3630 cosHalfAngle[0] = (float) cos( DEG2RAD( pyramidAngle1 * 0.25f ) );
3631 cosHalfAngle[1] = (float) cos( DEG2RAD( pyramidAngle2 * 0.25f ) );
3633 this->body1Axis = body1Axis;
3638 idAFConstraint_PyramidLimit::SetAnchor
3641 void idAFConstraint_PyramidLimit::SetAnchor( const idVec3 &pyramidAnchor ) {
3642 this->pyramidAnchor = pyramidAnchor;
3647 idAFConstraint_PyramidLimit::SetBody1Axis
3650 void idAFConstraint_PyramidLimit::SetBody1Axis( const idVec3 &body1Axis ) {
3651 this->body1Axis = body1Axis;
3656 idAFConstraint_PyramidLimit::Evaluate
3659 void idAFConstraint_PyramidLimit::Evaluate( float invTimeStep ) {
3665 idAFConstraint_PyramidLimit::ApplyFriction
3668 void idAFConstraint_PyramidLimit::ApplyFriction( float invTimeStep ) {
3673 idAFConstraint_PyramidLimit::Add
3676 bool idAFConstraint_PyramidLimit::Add( idPhysics_AF *phys, float invTimeStep ) {
3679 idVec6 J1row, J2row;
3681 idVec3 anchor, body1ax, ax[2], v, normal, pyramidVector, p1, p2;
3685 if ( af_skipLimits.GetBool() ) {
3686 lm.Zero(); // constraint exerts no force
3691 master = body2 ? body2 : physics->GetMasterBody();
3694 worldBase[0] = pyramidBasis[0] * master->GetWorldAxis();
3695 worldBase[1] = pyramidBasis[1] * master->GetWorldAxis();
3696 worldBase[2] = pyramidBasis[2] * master->GetWorldAxis();
3697 anchor = master->GetWorldOrigin() + pyramidAnchor * master->GetWorldAxis();
3700 worldBase = pyramidBasis;
3701 anchor = pyramidAnchor;
3704 body1ax = body1Axis * body1->GetWorldAxis();
3706 for ( i = 0; i < 2; i++ ) {
3707 ax[i] = body1ax - worldBase[!i] * body1ax * worldBase[!i];
3709 a[i] = worldBase[2] * ax[i];
3712 // if the body1 axis is inside the pyramid
3713 if ( a[0] > cosAngle[0] && a[1] > cosAngle[1] ) {
3714 lm.Zero(); // constraint exerts no force
3718 // calculate the inward pyramid normal for the position the body1 axis went outside the pyramid
3719 pyramidVector = worldBase[2];
3720 for ( i = 0; i < 2; i++ ) {
3721 if ( a[i] <= cosAngle[i] ) {
3722 v = ax[i].Cross( worldBase[2] );
3724 q.x = v.x * sinHalfAngle[i];
3725 q.y = v.y * sinHalfAngle[i];
3726 q.z = v.z * sinHalfAngle[i];
3727 q.w = cosHalfAngle[i];
3728 pyramidVector *= q.ToMat3();
3731 normal = pyramidVector.Cross( worldBase[2] ).Cross( pyramidVector );
3734 p1 = anchor + 32.0f * pyramidVector - body1->GetWorldOrigin();
3736 J1row.SubVec3(0) = normal;
3737 J1row.SubVec3(1) = p1.Cross( normal );
3738 J1.Set( 1, 6, J1row.ToFloatPtr() );
3740 c1[0] = (invTimeStep * LIMIT_ERROR_REDUCTION) * ( normal * (32.0f * body1ax) );
3744 p2 = anchor + 32.0f * pyramidVector - master->GetWorldOrigin();
3746 J2row.SubVec3(0) = -normal;
3747 J2row.SubVec3(1) = p2.Cross( -normal );
3748 J2.Set( 1, 6, J2row.ToFloatPtr() );
3754 e[0] = LIMIT_LCP_EPSILON;
3756 physics->AddFrameConstraint( this );
3763 idAFConstraint_PyramidLimit::Translate
3766 void idAFConstraint_PyramidLimit::Translate( const idVec3 &translation ) {
3768 pyramidAnchor += translation;
3774 idAFConstraint_PyramidLimit::Rotate
3777 void idAFConstraint_PyramidLimit::Rotate( const idRotation &rotation ) {
3779 pyramidAnchor *= rotation;
3780 pyramidBasis[0] *= rotation.ToMat3();
3781 pyramidBasis[1] *= rotation.ToMat3();
3782 pyramidBasis[2] *= rotation.ToMat3();
3788 idAFConstraint_PyramidLimit::DebugDraw
3791 void idAFConstraint_PyramidLimit::DebugDraw( void ) {
3794 idVec3 anchor, dir, p[4];
3795 idMat3 worldBase, m[2];
3799 master = body2 ? body2 : physics->GetMasterBody();
3802 worldBase[0] = pyramidBasis[0] * master->GetWorldAxis();
3803 worldBase[1] = pyramidBasis[1] * master->GetWorldAxis();
3804 worldBase[2] = pyramidBasis[2] * master->GetWorldAxis();
3805 anchor = master->GetWorldOrigin() + pyramidAnchor * master->GetWorldAxis();
3808 worldBase = pyramidBasis;
3809 anchor = pyramidAnchor;
3813 gameRenderWorld->DebugLine( colorGreen, anchor, anchor + size * (body1Axis * body1->GetWorldAxis()) );
3816 for ( i = 0; i < 2; i++ ) {
3817 q.x = worldBase[!i].x * sinHalfAngle[i];
3818 q.y = worldBase[!i].y * sinHalfAngle[i];
3819 q.z = worldBase[!i].z * sinHalfAngle[i];
3820 q.w = cosHalfAngle[i];
3824 dir = worldBase[2] * size;
3825 p[0] = anchor + m[0] * (m[1] * dir);
3826 p[1] = anchor + m[0] * (m[1].Transpose() * dir);
3827 p[2] = anchor + m[0].Transpose() * (m[1].Transpose() * dir);
3828 p[3] = anchor + m[0].Transpose() * (m[1] * dir);
3830 for ( i = 0; i < 4; i++ ) {
3831 gameRenderWorld->DebugLine( colorMagenta, anchor, p[i] );
3832 gameRenderWorld->DebugLine( colorMagenta, p[i], p[(i+1)&3] );
3838 idAFConstraint_PyramidLimit::Save
3841 void idAFConstraint_PyramidLimit::Save( idSaveGame *saveFile ) const {
3842 idAFConstraint::Save( saveFile );
3843 saveFile->WriteVec3( pyramidAnchor );
3844 saveFile->WriteMat3( pyramidBasis );
3845 saveFile->WriteVec3( body1Axis );
3846 saveFile->WriteFloat( cosAngle[0] );
3847 saveFile->WriteFloat( cosAngle[1] );
3848 saveFile->WriteFloat( sinHalfAngle[0] );
3849 saveFile->WriteFloat( sinHalfAngle[1] );
3850 saveFile->WriteFloat( cosHalfAngle[0] );
3851 saveFile->WriteFloat( cosHalfAngle[1] );
3852 saveFile->WriteFloat( epsilon );
3857 idAFConstraint_PyramidLimit::Restore
3860 void idAFConstraint_PyramidLimit::Restore( idRestoreGame *saveFile ) {
3861 idAFConstraint::Restore( saveFile );
3862 saveFile->ReadVec3( pyramidAnchor );
3863 saveFile->ReadMat3( pyramidBasis );
3864 saveFile->ReadVec3( body1Axis );
3865 saveFile->ReadFloat( cosAngle[0] );
3866 saveFile->ReadFloat( cosAngle[1] );
3867 saveFile->ReadFloat( sinHalfAngle[0] );
3868 saveFile->ReadFloat( sinHalfAngle[1] );
3869 saveFile->ReadFloat( cosHalfAngle[0] );
3870 saveFile->ReadFloat( cosHalfAngle[1] );
3871 saveFile->ReadFloat( epsilon );
3875 //===============================================================
3877 // idAFConstraint_Suspension
3879 //===============================================================
3883 idAFConstraint_Suspension::idAFConstraint_Suspension
3886 idAFConstraint_Suspension::idAFConstraint_Suspension( void ) {
3887 type = CONSTRAINT_SUSPENSION;
3888 name = "suspension";
3890 fl.allowPrimary = false;
3891 fl.frameConstraint = true;
3894 localAxis.Identity();
3895 suspensionUp = 0.0f;
3896 suspensionDown = 0.0f;
3897 suspensionKCompress = 0.0f;
3898 suspensionDamping = 0.0f;
3901 motorEnabled = false;
3903 motorVelocity = 0.0f;
3905 memset( &trace, 0, sizeof( trace ) );
3906 epsilon = LCP_EPSILON;
3911 idAFConstraint_Suspension::Setup
3914 void idAFConstraint_Suspension::Setup( const char *name, idAFBody *body, const idVec3 &origin, const idMat3 &axis, idClipModel *clipModel ) {
3918 localOrigin = ( origin - body->GetWorldOrigin() ) * body->GetWorldAxis().Transpose();
3919 localAxis = axis * body->GetWorldAxis().Transpose();
3920 wheelModel = clipModel;
3925 idAFConstraint_Suspension::SetSuspension
3928 void idAFConstraint_Suspension::SetSuspension( const float up, const float down, const float k, const float d, const float f ) {
3930 suspensionDown = down;
3931 suspensionKCompress = k;
3932 suspensionDamping = d;
3938 idAFConstraint_Suspension::GetWheelOrigin
3941 const idVec3 idAFConstraint_Suspension::GetWheelOrigin( void ) const {
3942 return body1->GetWorldOrigin() + wheelOffset * body1->GetWorldAxis();
3947 idAFConstraint_Suspension::Evaluate
3950 void idAFConstraint_Suspension::Evaluate( float invTimeStep ) {
3951 float velocity, suspensionLength, springLength, compression, dampingForce, springForce;
3952 idVec3 origin, start, end, vel1, vel2, springDir, r, frictionDir, motorDir;
3954 idRotation rotation;
3956 axis = localAxis * body1->GetWorldAxis();
3957 origin = body1->GetWorldOrigin() + localOrigin * body1->GetWorldAxis();
3958 start = origin + suspensionUp * axis[2];
3959 end = origin - suspensionDown * axis[2];
3961 rotation.SetVec( axis[2] );
3962 rotation.SetAngle( steerAngle );
3964 axis *= rotation.ToMat3();
3966 gameLocal.clip.Translation( trace, start, end, wheelModel, axis, MASK_SOLID, NULL );
3968 wheelOffset = ( trace.endpos - body1->GetWorldOrigin() ) * body1->GetWorldAxis().Transpose();
3970 if ( trace.fraction >= 1.0f ) {
3978 // calculate and add spring force
3979 vel1 = body1->GetPointVelocity( start );
3981 vel2 = body2->GetPointVelocity( trace.c.point );
3986 suspensionLength = suspensionUp + suspensionDown;
3987 springDir = trace.endpos - start;
3988 springLength = trace.fraction * suspensionLength;
3989 dampingForce = suspensionDamping * idMath::Fabs( ( vel2 - vel1 ) * springDir ) / ( 1.0f + springLength * springLength );
3990 compression = suspensionLength - springLength;
3991 springForce = compression * compression * suspensionKCompress - dampingForce;
3993 r = trace.c.point - body1->GetWorldOrigin();
3995 J1.SubVec6(0).SubVec3(0) = trace.c.normal;
3996 J1.SubVec6(0).SubVec3(1) = r.Cross( trace.c.normal );
3999 velocity = J1.SubVec6(0).SubVec3(0) * body1->GetLinearVelocity() + J1.SubVec6(0).SubVec3(1) * body1->GetAngularVelocity();
4002 r = trace.c.point - body2->GetWorldOrigin();
4004 J2.SubVec6(0).SubVec3(0) = -trace.c.normal;
4005 J2.SubVec6(0).SubVec3(1) = r.Cross( -trace.c.normal );
4008 velocity += J2.SubVec6(0).SubVec3(0) * body2->GetLinearVelocity() + J2.SubVec6(0).SubVec3(1) * body2->GetAngularVelocity();
4011 c1[0] = -compression; // + 0.5f * -velocity;
4015 hi[0] = springForce;
4016 boxConstraint = NULL;
4019 // project the friction direction into the contact plane
4020 frictionDir = axis[1] - axis[1] * trace.c.normal * axis[1];
4021 frictionDir.Normalize();
4023 r = trace.c.point - body1->GetWorldOrigin();
4025 J1.SubVec6(1).SubVec3(0) = frictionDir;
4026 J1.SubVec6(1).SubVec3(1) = r.Cross( frictionDir );
4030 r = trace.c.point - body2->GetWorldOrigin();
4032 J2.SubVec6(1).SubVec3(0) = -frictionDir;
4033 J2.SubVec6(1).SubVec3(1) = r.Cross( -frictionDir );
4037 lo[1] = -friction * physics->GetContactFrictionScale();
4038 hi[1] = friction * physics->GetContactFrictionScale();
4040 boxConstraint = this;
4044 if ( motorEnabled ) {
4045 // project the motor force direction into the contact plane
4046 motorDir = axis[0] - axis[0] * trace.c.normal * axis[0];
4047 motorDir.Normalize();
4049 r = trace.c.point - body1->GetWorldOrigin();
4051 J1.ChangeSize( 3, J1.GetNumColumns() );
4052 J1.SubVec6(2).SubVec3(0) = -motorDir;
4053 J1.SubVec6(2).SubVec3(1) = r.Cross( -motorDir );
4055 c1[2] = motorVelocity;
4058 r = trace.c.point - body2->GetWorldOrigin();
4060 J2.ChangeSize( 3, J2.GetNumColumns() );
4061 J2.SubVec6(2).SubVec3(0) = -motorDir;
4062 J2.SubVec6(2).SubVec3(1) = r.Cross( -motorDir );
4067 lo[2] = -motorForce;
4075 idAFConstraint_Suspension::ApplyFriction
4078 void idAFConstraint_Suspension::ApplyFriction( float invTimeStep ) {
4084 idAFConstraint_Suspension::Translate
4087 void idAFConstraint_Suspension::Translate( const idVec3 &translation ) {
4092 idAFConstraint_Suspension::Rotate
4095 void idAFConstraint_Suspension::Rotate( const idRotation &rotation ) {
4100 idAFConstraint_Suspension::DebugDraw
4103 void idAFConstraint_Suspension::DebugDraw( void ) {
4106 idRotation rotation;
4108 axis = localAxis * body1->GetWorldAxis();
4110 rotation.SetVec( axis[2] );
4111 rotation.SetAngle( steerAngle );
4113 axis *= rotation.ToMat3();
4115 if ( trace.fraction < 1.0f ) {
4116 origin = trace.c.point;
4118 gameRenderWorld->DebugLine( colorWhite, origin, origin + 6.0f * axis[2] );
4119 gameRenderWorld->DebugLine( colorWhite, origin - 4.0f * axis[0], origin + 4.0f * axis[0] );
4120 gameRenderWorld->DebugLine( colorWhite, origin - 2.0f * axis[1], origin + 2.0f * axis[1] );
4125 //===============================================================
4129 //===============================================================
4136 idAFBody::idAFBody( void ) {
4145 idAFBody::idAFBody( const idStr &name, idClipModel *clipModel, float density ) {
4147 assert( clipModel );
4148 assert( clipModel->IsTraceModel() );
4153 this->clipModel = NULL;
4155 SetClipModel( clipModel );
4156 SetDensity( density );
4158 current->worldOrigin = clipModel->GetOrigin();
4159 current->worldAxis = clipModel->GetAxis();
4169 idAFBody::~idAFBody( void ) {
4178 void idAFBody::Init( void ) {
4182 primaryConstraint = NULL;
4185 linearFriction = -1.0f;
4186 angularFriction = -1.0f;
4187 contactFriction = -1.0f;
4191 frictionDir = vec3_zero;
4192 contactMotorDir = vec3_zero;
4193 contactMotorVelocity = 0.0f;
4194 contactMotorForce = 0.0f;
4198 centerOfMass = vec3_zero;
4199 inertiaTensor = mat3_identity;
4200 inverseInertiaTensor = mat3_identity;
4202 current = &state[0];
4204 current->worldOrigin = vec3_zero;
4205 current->worldAxis = mat3_identity;
4206 current->spatialVelocity = vec6_zero;
4207 current->externalForce = vec6_zero;
4210 atRestOrigin = vec3_zero;
4211 atRestAxis = mat3_identity;
4214 totalForce.Zero( 6 );
4216 acceleration.Zero( 6 );
4219 responseIndex = NULL;
4221 maxAuxiliaryIndex = 0;
4222 maxSubTreeAuxiliaryIndex = 0;
4224 memset( &fl, 0, sizeof( fl ) );
4226 fl.selfCollision = true;
4232 idAFBody::SetClipModel
4235 void idAFBody::SetClipModel( idClipModel *clipModel ) {
4236 if ( this->clipModel && this->clipModel != clipModel ) {
4237 delete this->clipModel;
4239 this->clipModel = clipModel;
4244 idAFBody::SetFriction
4247 void idAFBody::SetFriction( float linear, float angular, float contact ) {
4248 if ( linear < 0.0f || linear > 1.0f ||
4249 angular < 0.0f || angular > 1.0f ||
4251 gameLocal.Warning( "idAFBody::SetFriction: friction out of range, linear = %.1f, angular = %.1f, contact = %.1f", linear, angular, contact );
4254 linearFriction = linear;
4255 angularFriction = angular;
4256 contactFriction = contact;
4261 idAFBody::SetBouncyness
4264 void idAFBody::SetBouncyness( float bounce ) {
4265 if ( bounce < 0.0f || bounce > 1.0f ) {
4266 gameLocal.Warning( "idAFBody::SetBouncyness: bouncyness out of range, bounce = %.1f", bounce );
4269 bouncyness = bounce;
4274 idAFBody::SetDensity
4277 void idAFBody::SetDensity( float density, const idMat3 &inertiaScale ) {
4279 // get the body mass properties
4280 clipModel->GetMassProperties( density, mass, centerOfMass, inertiaTensor );
4282 // make sure we have a valid mass
4283 if ( mass <= 0.0f || FLOAT_IS_NAN( mass ) ) {
4284 gameLocal.Warning( "idAFBody::SetDensity: invalid mass for body '%s'", name.c_str() );
4286 centerOfMass.Zero();
4287 inertiaTensor.Identity();
4290 // make sure the center of mass is at the body origin
4291 if ( !centerOfMass.Compare( vec3_origin, CENTER_OF_MASS_EPSILON ) ) {
4292 gameLocal.Warning( "idAFBody::SetDentity: center of mass not at origin for body '%s'", name.c_str() );
4294 centerOfMass.Zero();
4296 // calculate the inverse mass and inverse inertia tensor
4297 invMass = 1.0f / mass;
4298 if ( inertiaScale != mat3_identity ) {
4299 inertiaTensor *= inertiaScale;
4301 if ( inertiaTensor.IsDiagonal( 1e-3f ) ) {
4302 inertiaTensor[0][1] = inertiaTensor[0][2] = 0.0f;
4303 inertiaTensor[1][0] = inertiaTensor[1][2] = 0.0f;
4304 inertiaTensor[2][0] = inertiaTensor[2][1] = 0.0f;
4305 inverseInertiaTensor.Identity();
4306 inverseInertiaTensor[0][0] = 1.0f / inertiaTensor[0][0];
4307 inverseInertiaTensor[1][1] = 1.0f / inertiaTensor[1][1];
4308 inverseInertiaTensor[2][2] = 1.0f / inertiaTensor[2][2];
4311 inverseInertiaTensor = inertiaTensor.Inverse();
4317 idAFBody::SetFrictionDirection
4320 void idAFBody::SetFrictionDirection( const idVec3 &dir ) {
4321 frictionDir = dir * current->worldAxis.Transpose();
4322 fl.useFrictionDir = true;
4327 idAFBody::GetFrictionDirection
4330 bool idAFBody::GetFrictionDirection( idVec3 &dir ) const {
4331 if ( fl.useFrictionDir ) {
4332 dir = frictionDir * current->worldAxis;
4340 idAFBody::SetContactMotorDirection
4343 void idAFBody::SetContactMotorDirection( const idVec3 &dir ) {
4344 contactMotorDir = dir * current->worldAxis.Transpose();
4345 fl.useContactMotorDir = true;
4350 idAFBody::GetContactMotorDirection
4353 bool idAFBody::GetContactMotorDirection( idVec3 &dir ) const {
4354 if ( fl.useContactMotorDir ) {
4355 dir = contactMotorDir * current->worldAxis;
4363 idAFBody::GetPointVelocity
4366 idVec3 idAFBody::GetPointVelocity( const idVec3 &point ) const {
4367 idVec3 r = point - current->worldOrigin;
4368 return current->spatialVelocity.SubVec3(0) + current->spatialVelocity.SubVec3(1).Cross( r );
4376 void idAFBody::AddForce( const idVec3 &point, const idVec3 &force ) {
4377 current->externalForce.SubVec3(0) += force;
4378 current->externalForce.SubVec3(1) += (point - current->worldOrigin).Cross( force );
4383 idAFBody::InverseWorldSpatialInertiaMultiply
4385 dst = this->inverseWorldSpatialInertia * v;
4388 ID_INLINE void idAFBody::InverseWorldSpatialInertiaMultiply( idVecX &dst, const float *v ) const {
4389 const float *mPtr = inverseWorldSpatialInertia.ToFloatPtr();
4390 const float *vPtr = v;
4391 float *dstPtr = dst.ToFloatPtr();
4393 if ( fl.spatialInertiaSparse ) {
4394 dstPtr[0] = mPtr[0*6+0] * vPtr[0];
4395 dstPtr[1] = mPtr[1*6+1] * vPtr[1];
4396 dstPtr[2] = mPtr[2*6+2] * vPtr[2];
4397 dstPtr[3] = mPtr[3*6+3] * vPtr[3] + mPtr[3*6+4] * vPtr[4] + mPtr[3*6+5] * vPtr[5];
4398 dstPtr[4] = mPtr[4*6+3] * vPtr[3] + mPtr[4*6+4] * vPtr[4] + mPtr[4*6+5] * vPtr[5];
4399 dstPtr[5] = mPtr[5*6+3] * vPtr[3] + mPtr[5*6+4] * vPtr[4] + mPtr[5*6+5] * vPtr[5];
4401 gameLocal.Warning( "spatial inertia is not sparse for body %s", name.c_str() );
4410 void idAFBody::Save( idSaveGame *saveFile ) {
4411 saveFile->WriteFloat( linearFriction );
4412 saveFile->WriteFloat( angularFriction );
4413 saveFile->WriteFloat( contactFriction );
4414 saveFile->WriteFloat( bouncyness );
4415 saveFile->WriteInt( clipMask );
4416 saveFile->WriteVec3( frictionDir );
4417 saveFile->WriteVec3( contactMotorDir );
4418 saveFile->WriteFloat( contactMotorVelocity );
4419 saveFile->WriteFloat( contactMotorForce );
4421 saveFile->WriteFloat( mass );
4422 saveFile->WriteFloat( invMass );
4423 saveFile->WriteVec3( centerOfMass );
4424 saveFile->WriteMat3( inertiaTensor );
4425 saveFile->WriteMat3( inverseInertiaTensor );
4427 saveFile->WriteVec3( current->worldOrigin );
4428 saveFile->WriteMat3( current->worldAxis );
4429 saveFile->WriteVec6( current->spatialVelocity );
4430 saveFile->WriteVec6( current->externalForce );
4431 saveFile->WriteVec3( atRestOrigin );
4432 saveFile->WriteMat3( atRestAxis );
4440 void idAFBody::Restore( idRestoreGame *saveFile ) {
4441 saveFile->ReadFloat( linearFriction );
4442 saveFile->ReadFloat( angularFriction );
4443 saveFile->ReadFloat( contactFriction );
4444 saveFile->ReadFloat( bouncyness );
4445 saveFile->ReadInt( clipMask );
4446 saveFile->ReadVec3( frictionDir );
4447 saveFile->ReadVec3( contactMotorDir );
4448 saveFile->ReadFloat( contactMotorVelocity );
4449 saveFile->ReadFloat( contactMotorForce );
4451 saveFile->ReadFloat( mass );
4452 saveFile->ReadFloat( invMass );
4453 saveFile->ReadVec3( centerOfMass );
4454 saveFile->ReadMat3( inertiaTensor );
4455 saveFile->ReadMat3( inverseInertiaTensor );
4457 saveFile->ReadVec3( current->worldOrigin );
4458 saveFile->ReadMat3( current->worldAxis );
4459 saveFile->ReadVec6( current->spatialVelocity );
4460 saveFile->ReadVec6( current->externalForce );
4461 saveFile->ReadVec3( atRestOrigin );
4462 saveFile->ReadMat3( atRestAxis );
4467 //===============================================================
4471 //===============================================================
4477 factor matrix for the primary constraints in the tree
4480 void idAFTree::Factor( void ) const {
4483 idAFConstraint *child;
4486 childI.SetData( 6, 6, MATX_ALLOCA( 6 * 6 ) );
4488 // from the leaves up towards the root
4489 for ( i = sortedBodies.Num() - 1; i >= 0; i-- ) {
4490 body = sortedBodies[i];
4492 if ( body->children.Num() ) {
4494 for ( j = 0; j < body->children.Num(); j++ ) {
4496 child = body->children[j]->primaryConstraint;
4498 // child->I = - child->body1->J.Transpose() * child->body1->I * child->body1->J;
4499 childI.SetSize( child->J1.GetNumRows(), child->J1.GetNumRows() );
4500 child->body1->J.TransposeMultiply( child->body1->I ).Multiply( childI, child->body1->J );
4503 child->invI = childI;
4504 if ( !child->invI.InverseFastSelf() ) {
4505 gameLocal.Warning( "idAFTree::Factor: couldn't invert %dx%d matrix for constraint '%s'",
4506 child->invI.GetNumRows(), child->invI.GetNumColumns(), child->GetName().c_str() );
4508 child->J = child->invI * child->J;
4510 body->I -= child->J.TransposeMultiply( childI ) * child->J;
4513 body->invI = body->I;
4514 if ( !body->invI.InverseFastSelf() ) {
4515 gameLocal.Warning( "idAFTree::Factor: couldn't invert %dx%d matrix for body %s",
4516 child->invI.GetNumRows(), child->invI.GetNumColumns(), body->GetName().c_str() );
4518 if ( body->primaryConstraint ) {
4519 body->J = body->invI * body->J;
4522 else if ( body->primaryConstraint ) {
4523 body->J = body->inverseWorldSpatialInertia * body->J;
4532 solve for primary constraints in the tree
4535 void idAFTree::Solve( int auxiliaryIndex ) const {
4537 idAFBody *body, *child;
4538 idAFConstraint *primaryConstraint;
4540 // from the leaves up towards the root
4541 for ( i = sortedBodies.Num() - 1; i >= 0; i-- ) {
4542 body = sortedBodies[i];
4544 for ( j = 0; j < body->children.Num(); j++ ) {
4545 child = body->children[j];
4546 primaryConstraint = child->primaryConstraint;
4548 if ( !child->fl.isZero ) {
4549 child->J.TransposeMultiplySub( primaryConstraint->s, child->s );
4550 primaryConstraint->fl.isZero = false;
4552 if ( !primaryConstraint->fl.isZero ) {
4553 primaryConstraint->J.TransposeMultiplySub( body->s, primaryConstraint->s );
4554 body->fl.isZero = false;
4559 bool useSymmetry = af_useSymmetry.GetBool();
4561 // from the root down towards the leaves
4562 for ( i = 0; i < sortedBodies.Num(); i++ ) {
4563 body = sortedBodies[i];
4564 primaryConstraint = body->primaryConstraint;
4566 if ( primaryConstraint ) {
4568 if ( useSymmetry && body->parent->maxSubTreeAuxiliaryIndex < auxiliaryIndex ) {
4572 if ( !primaryConstraint->fl.isZero ) {
4573 primaryConstraint->s = primaryConstraint->invI * primaryConstraint->s;
4575 primaryConstraint->J.MultiplySub( primaryConstraint->s, primaryConstraint->body2->s );
4577 primaryConstraint->lm = primaryConstraint->s;
4579 if ( useSymmetry && body->maxSubTreeAuxiliaryIndex < auxiliaryIndex ) {
4583 if ( body->children.Num() ) {
4584 if ( !body->fl.isZero ) {
4585 body->s = body->invI * body->s;
4587 body->J.MultiplySub( body->s, primaryConstraint->s );
4589 } else if ( body->children.Num() ) {
4590 body->s = body->invI * body->s;
4599 calculate body forces in the tree in response to a constraint force
4602 void idAFTree::Response( const idAFConstraint *constraint, int row, int auxiliaryIndex ) const {
4605 idAFConstraint *child, *primaryConstraint;
4608 // if a single body don't waste time because there aren't any primary constraints
4609 if ( sortedBodies.Num() == 1 ) {
4610 body = constraint->body1;
4611 if ( body->tree == this ) {
4612 body->GetResponseForce( body->numResponses ) = constraint->J1.SubVec6( row );
4613 body->responseIndex[body->numResponses++] = auxiliaryIndex;
4616 body = constraint->body2;
4617 body->GetResponseForce( body->numResponses ) = constraint->J2.SubVec6( row );
4618 body->responseIndex[body->numResponses++] = auxiliaryIndex;
4623 v.SetData( 6, VECX_ALLOCA( 6 ) );
4625 // initialize right hand side to zero
4626 for ( i = 0; i < sortedBodies.Num(); i++ ) {
4627 body = sortedBodies[i];
4628 primaryConstraint = body->primaryConstraint;
4629 if ( primaryConstraint ) {
4630 primaryConstraint->s.Zero();
4631 primaryConstraint->fl.isZero = true;
4634 body->fl.isZero = true;
4635 body->GetResponseForce( body->numResponses ).Zero();
4638 // set right hand side for first constrained body
4639 body = constraint->body1;
4640 if ( body->tree == this ) {
4641 body->InverseWorldSpatialInertiaMultiply( v, constraint->J1[row] );
4642 primaryConstraint = body->primaryConstraint;
4643 if ( primaryConstraint ) {
4644 primaryConstraint->J1.Multiply( primaryConstraint->s, v );
4645 primaryConstraint->fl.isZero = false;
4647 for ( i = 0; i < body->children.Num(); i++ ) {
4648 child = body->children[i]->primaryConstraint;
4649 child->J2.Multiply( child->s, v );
4650 child->fl.isZero = false;
4652 body->GetResponseForce( body->numResponses ) = constraint->J1.SubVec6( row );
4655 // set right hand side for second constrained body
4656 body = constraint->body2;
4657 if ( body && body->tree == this ) {
4658 body->InverseWorldSpatialInertiaMultiply( v, constraint->J2[row] );
4659 primaryConstraint = body->primaryConstraint;
4660 if ( primaryConstraint ) {
4661 primaryConstraint->J1.MultiplyAdd( primaryConstraint->s, v );
4662 primaryConstraint->fl.isZero = false;
4664 for ( i = 0; i < body->children.Num(); i++ ) {
4665 child = body->children[i]->primaryConstraint;
4666 child->J2.MultiplyAdd( child->s, v );
4667 child->fl.isZero = false;
4669 body->GetResponseForce( body->numResponses ) = constraint->J2.SubVec6( row );
4673 // solve for primary constraints
4674 Solve( auxiliaryIndex );
4676 bool useSymmetry = af_useSymmetry.GetBool();
4678 // store body forces in response to the constraint force
4680 for ( i = 0; i < sortedBodies.Num(); i++ ) {
4681 body = sortedBodies[i];
4683 if ( useSymmetry && body->maxAuxiliaryIndex < auxiliaryIndex ) {
4687 force.SetData( 6, body->response + body->numResponses * 8 );
4689 // add forces of all primary constraints acting on this body
4690 primaryConstraint = body->primaryConstraint;
4691 if ( primaryConstraint ) {
4692 primaryConstraint->J1.TransposeMultiplyAdd( force, primaryConstraint->lm );
4694 for ( j = 0; j < body->children.Num(); j++ ) {
4695 child = body->children[j]->primaryConstraint;
4696 child->J2.TransposeMultiplyAdd( force, child->lm );
4699 body->responseIndex[body->numResponses++] = auxiliaryIndex;
4705 idAFTree::CalculateForces
4707 calculate forces on the bodies in the tree
4710 void idAFTree::CalculateForces( float timeStep ) const {
4714 idAFConstraint *child, *c, *primaryConstraint;
4717 for ( i = 0; i < sortedBodies.Num(); i++ ) {
4718 body = sortedBodies[i];
4720 body->totalForce.SubVec6(0) = body->current->externalForce + body->auxForce.SubVec6(0);
4723 // if a single body don't waste time because there aren't any primary constraints
4724 if ( sortedBodies.Num() == 1 ) {
4728 invStep = 1.0f / timeStep;
4730 // initialize right hand side
4731 for ( i = 0; i < sortedBodies.Num(); i++ ) {
4732 body = sortedBodies[i];
4734 body->InverseWorldSpatialInertiaMultiply( body->acceleration, body->totalForce.ToFloatPtr() );
4735 body->acceleration.SubVec6(0) += body->current->spatialVelocity * invStep;
4736 primaryConstraint = body->primaryConstraint;
4737 if ( primaryConstraint ) {
4738 // b = ( J * acc + c )
4739 c = primaryConstraint;
4740 c->s = c->J1 * c->body1->acceleration + c->J2 * c->body2->acceleration + invStep * ( c->c1 + c->c2 );
4741 c->fl.isZero = false;
4744 body->fl.isZero = true;
4747 // solve for primary constraints
4750 // calculate forces on bodies after applying primary constraints
4751 for ( i = 0; i < sortedBodies.Num(); i++ ) {
4752 body = sortedBodies[i];
4754 // add forces of all primary constraints acting on this body
4755 primaryConstraint = body->primaryConstraint;
4756 if ( primaryConstraint ) {
4757 primaryConstraint->J1.TransposeMultiplyAdd( body->totalForce, primaryConstraint->lm );
4759 for ( j = 0; j < body->children.Num(); j++ ) {
4760 child = body->children[j]->primaryConstraint;
4761 child->J2.TransposeMultiplyAdd( body->totalForce, child->lm );
4768 idAFTree::SetMaxSubTreeAuxiliaryIndex
4771 void idAFTree::SetMaxSubTreeAuxiliaryIndex( void ) {
4773 idAFBody *body, *child;
4775 // from the leaves up towards the root
4776 for ( i = sortedBodies.Num() - 1; i >= 0; i-- ) {
4777 body = sortedBodies[i];
4779 body->maxSubTreeAuxiliaryIndex = body->maxAuxiliaryIndex;
4780 for ( j = 0; j < body->children.Num(); j++ ) {
4781 child = body->children[j];
4782 if ( child->maxSubTreeAuxiliaryIndex > body->maxSubTreeAuxiliaryIndex ) {
4783 body->maxSubTreeAuxiliaryIndex = child->maxSubTreeAuxiliaryIndex;
4791 idAFTree::SortBodies_r
4794 void idAFTree::SortBodies_r( idList<idAFBody*>&sortedList, idAFBody *body ) {
4797 for ( i = 0; i < body->children.Num(); i++ ) {
4798 sortedList.Append( body->children[i] );
4800 for ( i = 0; i < body->children.Num(); i++ ) {
4801 SortBodies_r( sortedList, body->children[i] );
4807 idAFTree::SortBodies
4809 sort body list to make sure parents come first
4812 void idAFTree::SortBodies( void ) {
4817 for ( i = 0; i < sortedBodies.Num(); i++ ) {
4818 if ( !sortedBodies[i]->parent ) {
4823 if ( i >= sortedBodies.Num() ) {
4824 gameLocal.Error( "Articulated figure tree has no root." );
4827 body = sortedBodies[i];
4828 sortedBodies.Clear();
4829 sortedBodies.Append( body );
4830 SortBodies_r( sortedBodies, body );
4838 void idAFTree::DebugDraw( const idVec4 &color ) const {
4842 for ( i = 1; i < sortedBodies.Num(); i++ ) {
4843 body = sortedBodies[i];
4844 gameRenderWorld->DebugArrow( color, body->parent->current->worldOrigin, body->current->worldOrigin, 1 );
4849 //===============================================================
4853 //===============================================================
4857 idPhysics_AF::EvaluateConstraints
4860 void idPhysics_AF::EvaluateConstraints( float timeStep ) {
4866 invTimeStep = 1.0f / timeStep;
4868 // setup the constraint equations for the current position and orientation of the bodies
4869 for ( i = 0; i < primaryConstraints.Num(); i++ ) {
4870 c = primaryConstraints[i];
4871 c->Evaluate( invTimeStep );
4874 for ( i = 0; i < auxiliaryConstraints.Num(); i++ ) {
4875 auxiliaryConstraints[i]->Evaluate( invTimeStep );
4878 // add contact constraints to the list with frame constraints
4879 for ( i = 0; i < contactConstraints.Num(); i++ ) {
4880 AddFrameConstraint( contactConstraints[i] );
4883 // setup body primary constraint matrix
4884 for ( i = 0; i < bodies.Num(); i++ ) {
4887 if ( body->primaryConstraint ) {
4888 body->J = body->primaryConstraint->J1.Transpose();
4895 idPhysics_AF::EvaluateBodies
4898 void idPhysics_AF::EvaluateBodies( float timeStep ) {
4903 for ( i = 0; i < bodies.Num(); i++ ) {
4906 // we transpose the axis before using it because idMat3 is column-major
4907 axis = body->current->worldAxis.Transpose();
4909 // if the center of mass is at the body point of reference
4910 if ( body->centerOfMass.Compare( vec3_origin, CENTER_OF_MASS_EPSILON ) ) {
4912 // spatial inertia in world space
4913 body->I.Set( body->mass * mat3_identity, mat3_zero,
4914 mat3_zero, axis * body->inertiaTensor * axis.Transpose() );
4916 // inverse spatial inertia in world space
4917 body->inverseWorldSpatialInertia.Set( body->invMass * mat3_identity, mat3_zero,
4918 mat3_zero, axis * body->inverseInertiaTensor * axis.Transpose() );
4920 body->fl.spatialInertiaSparse = true;
4923 idMat3 massMoment = body->mass * SkewSymmetric( body->centerOfMass );
4925 // spatial inertia in world space
4926 body->I.Set( body->mass * mat3_identity, massMoment,
4927 massMoment.Transpose(), axis * body->inertiaTensor * axis.Transpose() );
4929 // inverse spatial inertia in world space
4930 body->inverseWorldSpatialInertia = body->I.InverseFast();
4932 body->fl.spatialInertiaSparse = false;
4935 // initialize auxiliary constraint force to zero
4936 body->auxForce.Zero();
4942 idPhysics_AF::AddFrameConstraints
4945 void idPhysics_AF::AddFrameConstraints( void ) {
4948 // add frame constraints to auxiliary constraints
4949 for ( i = 0; i < frameConstraints.Num(); i++ ) {
4950 auxiliaryConstraints.Append( frameConstraints[i] );
4956 idPhysics_AF::RemoveFrameConstraints
4959 void idPhysics_AF::RemoveFrameConstraints( void ) {
4960 // remove all the frame constraints from the auxiliary constraints
4961 auxiliaryConstraints.SetNum( auxiliaryConstraints.Num() - frameConstraints.Num(), false );
4962 frameConstraints.SetNum( 0, false );
4967 idPhysics_AF::ApplyFriction
4970 void idPhysics_AF::ApplyFriction( float timeStep, float endTimeMSec ) {
4974 if ( af_skipFriction.GetBool() ) {
4978 if ( jointFrictionDentStart < MS2SEC( endTimeMSec ) && jointFrictionDentEnd > MS2SEC( endTimeMSec ) ) {
4979 float halfTime = ( jointFrictionDentEnd - jointFrictionDentStart ) * 0.5f;
4980 if ( jointFrictionDentStart + halfTime > MS2SEC( endTimeMSec ) ) {
4981 jointFrictionDentScale = 1.0f - ( 1.0f - jointFrictionDent ) * ( MS2SEC( endTimeMSec ) - jointFrictionDentStart ) / halfTime;
4983 jointFrictionDentScale = jointFrictionDent + ( 1.0f - jointFrictionDent ) * ( MS2SEC( endTimeMSec ) - jointFrictionDentStart - halfTime ) / halfTime;
4986 jointFrictionDentScale = 0.0f;
4989 if ( contactFrictionDentStart < MS2SEC( endTimeMSec ) && contactFrictionDentEnd > MS2SEC( endTimeMSec ) ) {
4990 float halfTime = ( contactFrictionDentEnd - contactFrictionDentStart ) * 0.5f;
4991 if ( contactFrictionDentStart + halfTime > MS2SEC( endTimeMSec ) ) {
4992 contactFrictionDentScale = 1.0f - ( 1.0f - contactFrictionDent ) * ( MS2SEC( endTimeMSec ) - contactFrictionDentStart ) / halfTime;
4994 contactFrictionDentScale = contactFrictionDent + ( 1.0f - contactFrictionDent ) * ( MS2SEC( endTimeMSec ) - contactFrictionDentStart - halfTime ) / halfTime;
4997 contactFrictionDentScale = 0.0f;
5000 invTimeStep = 1.0f / timeStep;
5002 for ( i = 0; i < primaryConstraints.Num(); i++ ) {
5003 primaryConstraints[i]->ApplyFriction( invTimeStep );
5005 for ( i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5006 auxiliaryConstraints[i]->ApplyFriction( invTimeStep );
5008 for ( i = 0; i < frameConstraints.Num(); i++ ) {
5009 frameConstraints[i]->ApplyFriction( invTimeStep );
5015 idPhysics_AF::PrimaryFactor
5018 void idPhysics_AF::PrimaryFactor( void ) {
5021 for ( i = 0; i < trees.Num(); i++ ) {
5028 idPhysics_AF::PrimaryForces
5031 void idPhysics_AF::PrimaryForces( float timeStep ) {
5034 for ( i = 0; i < trees.Num(); i++ ) {
5035 trees[i]->CalculateForces( timeStep );
5041 idPhysics_AF::AuxiliaryForces
5044 void idPhysics_AF::AuxiliaryForces( float timeStep ) {
5045 int i, j, k, l, n, m, s, numAuxConstraints, *index, *boxIndex;
5046 float *ptr, *j1, *j2, *dstPtr, *forcePtr;
5049 idAFConstraint *constraint;
5052 idVecX rhs, w, lm, lo, hi;
5054 // get the number of one dimensional auxiliary constraints
5055 for ( numAuxConstraints = 0, i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5056 numAuxConstraints += auxiliaryConstraints[i]->J1.GetNumRows();
5059 if ( numAuxConstraints == 0 ) {
5063 // allocate memory to store the body response to auxiliary constraint forces
5064 forcePtr = (float *) _alloca16( bodies.Num() * numAuxConstraints * 8 * sizeof( float ) );
5065 index = (int *) _alloca16( bodies.Num() * numAuxConstraints * sizeof( int ) );
5066 for ( i = 0; i < bodies.Num(); i++ ) {
5068 body->response = forcePtr;
5069 body->responseIndex = index;
5070 body->numResponses = 0;
5071 body->maxAuxiliaryIndex = 0;
5072 forcePtr += numAuxConstraints * 8;
5073 index += numAuxConstraints;
5076 // set on each body the largest index of an auxiliary constraint constraining the body
5077 if ( af_useSymmetry.GetBool() ) {
5078 for ( k = 0, i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5079 constraint = auxiliaryConstraints[i];
5080 for ( j = 0; j < constraint->J1.GetNumRows(); j++, k++ ) {
5081 if ( k > constraint->body1->maxAuxiliaryIndex ) {
5082 constraint->body1->maxAuxiliaryIndex = k;
5084 if ( constraint->body2 && k > constraint->body2->maxAuxiliaryIndex ) {
5085 constraint->body2->maxAuxiliaryIndex = k;
5089 for ( i = 0; i < trees.Num(); i++ ) {
5090 trees[i]->SetMaxSubTreeAuxiliaryIndex();
5094 // calculate forces of primary constraints in response to the auxiliary constraint forces
5095 for ( k = 0, i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5096 constraint = auxiliaryConstraints[i];
5098 for ( j = 0; j < constraint->J1.GetNumRows(); j++, k++ ) {
5100 // calculate body forces in the tree in response to the constraint force
5101 constraint->body1->tree->Response( constraint, j, k );
5102 // if there is a second body which is part of a different tree
5103 if ( constraint->body2 && constraint->body2->tree != constraint->body1->tree ) {
5104 // calculate body forces in the second tree in response to the constraint force
5105 constraint->body2->tree->Response( constraint, j, k );
5110 // NOTE: the rows are 16 byte padded
5111 jmk.SetData( numAuxConstraints, ((numAuxConstraints+3)&~3), MATX_ALLOCA( numAuxConstraints * ((numAuxConstraints+3)&~3) ) );
5112 tmp.SetData( 6, VECX_ALLOCA( 6 ) );
5114 // create constraint matrix for auxiliary constraints using a mass matrix adjusted for the primary constraints
5115 for ( k = 0, i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5116 constraint = auxiliaryConstraints[i];
5118 for ( j = 0; j < constraint->J1.GetNumRows(); j++, k++ ) {
5119 constraint->body1->InverseWorldSpatialInertiaMultiply( tmp, constraint->J1[j] );
5120 j1 = tmp.ToFloatPtr();
5121 ptr = constraint->body1->response;
5122 index = constraint->body1->responseIndex;
5124 s = af_useSymmetry.GetBool() ? k + 1 : numAuxConstraints;
5125 for ( l = n = 0, m = index[n]; n < constraint->body1->numResponses && m < s; n++, m = index[n] ) {
5129 dstPtr[l++] = j1[0] * ptr[0] + j1[1] * ptr[1] + j1[2] * ptr[2] +
5130 j1[3] * ptr[3] + j1[4] * ptr[4] + j1[5] * ptr[5];
5138 if ( constraint->body2 ) {
5139 constraint->body2->InverseWorldSpatialInertiaMultiply( tmp, constraint->J2[j] );
5140 j2 = tmp.ToFloatPtr();
5141 ptr = constraint->body2->response;
5142 index = constraint->body2->responseIndex;
5143 for ( n = 0, m = index[n]; n < constraint->body2->numResponses && m < s; n++, m = index[n] ) {
5144 dstPtr[m] += j2[0] * ptr[0] + j2[1] * ptr[1] + j2[2] * ptr[2] +
5145 j2[3] * ptr[3] + j2[4] * ptr[4] + j2[5] * ptr[5];
5152 if ( af_useSymmetry.GetBool() ) {
5153 n = jmk.GetNumColumns();
5154 for ( i = 0; i < numAuxConstraints; i++ ) {
5155 ptr = jmk.ToFloatPtr() + ( i + 1 ) * n + i;
5156 dstPtr = jmk.ToFloatPtr() + i * n + i + 1;
5157 for ( j = i+1; j < numAuxConstraints; j++ ) {
5164 invStep = 1.0f / timeStep;
5166 // calculate body acceleration
5167 for ( i = 0; i < bodies.Num(); i++ ) {
5169 body->InverseWorldSpatialInertiaMultiply( body->acceleration, body->totalForce.ToFloatPtr() );
5170 body->acceleration.SubVec6(0) += body->current->spatialVelocity * invStep;
5173 rhs.SetData( numAuxConstraints, VECX_ALLOCA( numAuxConstraints ) );
5174 lo.SetData( numAuxConstraints, VECX_ALLOCA( numAuxConstraints ) );
5175 hi.SetData( numAuxConstraints, VECX_ALLOCA( numAuxConstraints ) );
5176 lm.SetData( numAuxConstraints, VECX_ALLOCA( numAuxConstraints ) );
5177 boxIndex = (int *) _alloca16( numAuxConstraints * sizeof( int ) );
5179 // set first index for special box constrained variables
5180 for ( k = 0, i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5181 auxiliaryConstraints[i]->firstIndex = k;
5182 k += auxiliaryConstraints[i]->J1.GetNumRows();
5185 // initialize right hand side and low and high bounds for auxiliary constraints
5186 for ( k = 0, i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5187 constraint = auxiliaryConstraints[i];
5190 for ( j = 0; j < constraint->J1.GetNumRows(); j++, k++ ) {
5192 j1 = constraint->J1[j];
5193 ptr = constraint->body1->acceleration.ToFloatPtr();
5194 rhs[k] = j1[0] * ptr[0] + j1[1] * ptr[1] + j1[2] * ptr[2] + j1[3] * ptr[3] + j1[4] * ptr[4] + j1[5] * ptr[5];
5195 rhs[k] += constraint->c1[j] * invStep;
5197 if ( constraint->body2 ) {
5198 j2 = constraint->J2[j];
5199 ptr = constraint->body2->acceleration.ToFloatPtr();
5200 rhs[k] += j2[0] * ptr[0] + j2[1] * ptr[1] + j2[2] * ptr[2] + j2[3] * ptr[3] + j2[4] * ptr[4] + j2[5] * ptr[5];
5201 rhs[k] += constraint->c2[j] * invStep;
5205 lo[k] = constraint->lo[j];
5206 hi[k] = constraint->hi[j];
5208 if ( constraint->boxIndex[j] >= 0 ) {
5209 if ( constraint->boxConstraint->fl.isPrimary ) {
5210 gameLocal.Error( "cannot reference primary constraints for the box index" );
5212 boxIndex[k] = constraint->boxConstraint->firstIndex + constraint->boxIndex[j];
5217 jmk[k][k] += constraint->e[j] * invStep;
5225 // calculate lagrange multipliers for auxiliary constraints
5226 if ( !lcp->Solve( jmk, lm, rhs, lo, hi, boxIndex ) ) {
5227 return; // bad monkey!
5234 // calculate auxiliary constraint forces
5235 for ( k = 0, i = 0; i < auxiliaryConstraints.Num(); i++ ) {
5236 constraint = auxiliaryConstraints[i];
5238 for ( j = 0; j < constraint->J1.GetNumRows(); j++, k++ ) {
5239 constraint->lm[j] = u = lm[k];
5241 j1 = constraint->J1[j];
5242 ptr = constraint->body1->auxForce.ToFloatPtr();
5243 ptr[0] += j1[0] * u; ptr[1] += j1[1] * u; ptr[2] += j1[2] * u;
5244 ptr[3] += j1[3] * u; ptr[4] += j1[4] * u; ptr[5] += j1[5] * u;
5246 if ( constraint->body2 ) {
5247 j2 = constraint->J2[j];
5248 ptr = constraint->body2->auxForce.ToFloatPtr();
5249 ptr[0] += j2[0] * u; ptr[1] += j2[1] * u; ptr[2] += j2[2] * u;
5250 ptr[3] += j2[3] * u; ptr[4] += j2[4] * u; ptr[5] += j2[5] * u;
5255 // recalculate primary constraint forces in response to auxiliary constraint forces
5256 PrimaryForces( timeStep );
5258 // clear pointers pointing to stack space so tools don't get confused
5259 for ( i = 0; i < bodies.Num(); i++ ) {
5261 body->response = NULL;
5262 body->responseIndex = NULL;
5268 idPhysics_AF::VerifyContactConstraints
5271 void idPhysics_AF::VerifyContactConstraints( void ) {
5274 float impulseNumerator, impulseDenominator;
5275 idVec3 r, velocity, normalVelocity, normal, impulse;
5278 for ( i = 0; i < contactConstraints.Num(); i++ ) {
5279 body = contactConstraints[i]->body1;
5280 const contactInfo_t &contact = contactConstraints[i]->GetContact();
5282 r = contact.point - body->GetCenterOfMass();
5284 // calculate velocity at contact point
5285 velocity = body->GetLinearVelocity() + body->GetAngularVelocity().Cross( r );
5287 // velocity along normal vector
5288 normalVelocity = ( velocity * contact.normal ) * contact.normal;
5290 // if moving towards the surface at the contact point
5291 if ( normalVelocity * contact.normal < 0.0f ) {
5292 // calculate impulse
5293 normal = -normalVelocity;
5294 impulseNumerator = normal.Normalize();
5295 impulseDenominator = body->GetInverseMass() + ( ( body->GetInverseWorldInertia() * r.Cross( normal ) ).Cross( r ) * normal );
5296 impulse = (impulseNumerator / impulseDenominator) * normal * 1.0001f;
5299 body->SetLinearVelocity( body->GetLinearVelocity() + impulse );
5300 body->SetAngularVelocity( body->GetAngularVelocity() + r.Cross( impulse ) );
5308 for ( i = 0; i < contactConstraints.Num(); i++ ) {
5309 body = contactConstraints[i]->body1;
5310 normal = contactConstraints[i]->GetContact().normal;
5311 if ( normal * body->next->spatialVelocity.SubVec3(0) <= 0.0f ) {
5312 body->next->spatialVelocity.SubVec3(0) -= 1.0001f * (normal * body->next->spatialVelocity.SubVec3(0)) * normal;
5314 body = contactConstraints[i]->body2;
5319 if ( normal * body->next->spatialVelocity.SubVec3(0) <= 0.0f ) {
5320 body->next->spatialVelocity.SubVec3(0) -= 1.0001f * (normal * body->next->spatialVelocity.SubVec3(0)) * normal;
5328 idPhysics_AF::Evolve
5331 void idPhysics_AF::Evolve( float timeStep ) {
5337 idRotation rotation;
5338 float vSqr, maxLinearVelocity, maxAngularVelocity;
5340 maxLinearVelocity = af_maxLinearVelocity.GetFloat() / timeStep;
5341 maxAngularVelocity = af_maxAngularVelocity.GetFloat() / timeStep;
5343 for ( i = 0; i < bodies.Num(); i++ ) {
5346 // calculate the spatial velocity for the next physics state
5347 body->InverseWorldSpatialInertiaMultiply( body->acceleration, body->totalForce.ToFloatPtr() );
5348 body->next->spatialVelocity = body->current->spatialVelocity + timeStep * body->acceleration.SubVec6(0);
5350 if ( maxLinearVelocity > 0.0f ) {
5351 // cap the linear velocity
5352 vSqr = body->next->spatialVelocity.SubVec3(0).LengthSqr();
5353 if ( vSqr > Square( maxLinearVelocity ) ) {
5354 body->next->spatialVelocity.SubVec3(0) *= idMath::InvSqrt( vSqr ) * maxLinearVelocity;
5358 if ( maxAngularVelocity > 0.0f ) {
5359 // cap the angular velocity
5360 vSqr = body->next->spatialVelocity.SubVec3(1).LengthSqr();
5361 if ( vSqr > Square( maxAngularVelocity ) ) {
5362 body->next->spatialVelocity.SubVec3(1) *= idMath::InvSqrt( vSqr ) * maxAngularVelocity;
5367 // make absolutely sure all contact constraints are satisfied
5368 VerifyContactConstraints();
5370 // calculate the position of the bodies for the next physics state
5371 for ( i = 0; i < bodies.Num(); i++ ) {
5374 // translate world origin
5375 body->next->worldOrigin = body->current->worldOrigin + timeStep * body->next->spatialVelocity.SubVec3( 0 );
5377 // convert angular velocity to a rotation matrix
5378 vec = body->next->spatialVelocity.SubVec3( 1 );
5379 angle = -timeStep * (float) RAD2DEG( vec.Normalize() );
5380 rotation = idRotation( vec3_origin, vec, angle );
5381 rotation.Normalize180();
5383 // rotate world axis
5384 body->next->worldAxis = body->current->worldAxis * rotation.ToMat3();
5385 body->next->worldAxis.OrthoNormalizeSelf();
5387 // linear and angular friction
5388 body->next->spatialVelocity.SubVec3(0) -= body->linearFriction * body->next->spatialVelocity.SubVec3(0);
5389 body->next->spatialVelocity.SubVec3(1) -= body->angularFriction * body->next->spatialVelocity.SubVec3(1);
5395 idPhysics_AF::CollisionImpulse
5397 apply impulse to the colliding bodies
5398 the current state of the body should be set to the moment of impact
5399 this is silly as it doesn't take the AF structure into account
5402 bool idPhysics_AF::CollisionImpulse( float timeStep, idAFBody *body, trace_t &collision ) {
5403 idVec3 r, velocity, impulse;
5404 idMat3 inverseWorldInertiaTensor;
5405 float impulseNumerator, impulseDenominator;
5409 ent = gameLocal.entities[collision.c.entityNum];
5410 if ( ent == self ) {
5414 // get info from other entity involved
5415 ent->GetImpactInfo( self, collision.c.id, collision.c.point, &info );
5416 // collision point relative to the body center of mass
5417 r = collision.c.point - (body->current->worldOrigin + body->centerOfMass * body->current->worldAxis);
5418 // the velocity at the collision point
5419 velocity = body->current->spatialVelocity.SubVec3(0) + body->current->spatialVelocity.SubVec3(1).Cross(r);
5420 // subtract velocity of other entity
5421 velocity -= info.velocity;
5423 if ( velocity * collision.c.normal > 0.0f ) {
5424 velocity = collision.c.normal;
5426 inverseWorldInertiaTensor = body->current->worldAxis.Transpose() * body->inverseInertiaTensor * body->current->worldAxis;
5427 impulseNumerator = -( 1.0f + body->bouncyness ) * ( velocity * collision.c.normal );
5428 impulseDenominator = body->invMass + ( ( inverseWorldInertiaTensor * r.Cross( collision.c.normal ) ).Cross( r ) * collision.c.normal );
5429 if ( info.invMass ) {
5430 impulseDenominator += info.invMass + ( ( info.invInertiaTensor * info.position.Cross( collision.c.normal ) ).Cross( info.position ) * collision.c.normal );
5432 impulse = (impulseNumerator / impulseDenominator) * collision.c.normal;
5434 // apply impact to other entity
5435 ent->ApplyImpulse( self, collision.c.id, collision.c.point, -impulse );
5437 // callback to self to let the entity know about the impact
5438 return self->Collide( collision, velocity );
5443 idPhysics_AF::ApplyCollisions
5446 bool idPhysics_AF::ApplyCollisions( float timeStep ) {
5449 for ( i = 0; i < collisions.Num(); i++ ) {
5450 if ( CollisionImpulse( timeStep, collisions[i].body, collisions[i].trace ) ) {
5459 idPhysics_AF::SetupCollisionForBody
5462 idEntity *idPhysics_AF::SetupCollisionForBody( idAFBody *body ) const {
5465 idEntity *passEntity;
5469 if ( !selfCollision || !body->fl.selfCollision || af_skipSelfCollision.GetBool() ) {
5471 // disable all bodies
5472 for ( i = 0; i < bodies.Num(); i++ ) {
5473 bodies[i]->clipModel->Disable();
5476 // don't collide with world collision model if attached to the world
5477 for ( i = 0; i < body->constraints.Num(); i++ ) {
5478 if ( !body->constraints[i]->fl.noCollision ) {
5481 // if this constraint attaches the body to the world
5482 if ( body->constraints[i]->body2 == NULL ) {
5483 // don't collide with the world collision model
5484 passEntity = gameLocal.world;
5490 // enable all bodies that have self collision
5491 for ( i = 0; i < bodies.Num(); i++ ) {
5492 if ( bodies[i]->fl.selfCollision ) {
5493 bodies[i]->clipModel->Enable();
5495 bodies[i]->clipModel->Disable();
5499 // don't let the body collide with itself
5500 body->clipModel->Disable();
5502 // disable any bodies attached with constraints
5503 for ( i = 0; i < body->constraints.Num(); i++ ) {
5504 if ( !body->constraints[i]->fl.noCollision ) {
5507 // if this constraint attaches the body to the world
5508 if ( body->constraints[i]->body2 == NULL ) {
5509 // don't collide with the world collision model
5510 passEntity = gameLocal.world;
5512 if ( body->constraints[i]->body1 == body ) {
5513 b = body->constraints[i]->body2;
5514 } else if ( body->constraints[i]->body2 == body ) {
5515 b = body->constraints[i]->body1;
5519 // don't collide with this body
5520 b->clipModel->Disable();
5530 idPhysics_AF::CheckForCollisions
5532 check for collisions between the current and next state
5533 if there is a collision the next state is set to the state at the moment of impact
5534 assumes all bodies are linked for collision detection and relinks all bodies after moving them
5537 void idPhysics_AF::CheckForCollisions( float timeStep ) {
5538 // #define TEST_COLLISION_DETECTION
5542 idRotation rotation;
5544 idEntity *passEntity;
5546 // clear list with collisions
5547 collisions.SetNum( 0, false );
5549 if ( !enableCollision ) {
5553 for ( i = 0; i < bodies.Num(); i++ ) {
5556 if ( body->clipMask != 0 ) {
5558 passEntity = SetupCollisionForBody( body );
5560 #ifdef TEST_COLLISION_DETECTION
5561 bool startsolid = false;
5562 if ( gameLocal.clip.Contents( body->current->worldOrigin, body->clipModel,
5563 body->current->worldAxis, body->clipMask, passEntity ) ) {
5568 TransposeMultiply( body->current->worldAxis, body->next->worldAxis, axis );
5569 rotation = axis.ToRotation();
5570 rotation.SetOrigin( body->current->worldOrigin );
5572 // if there was a collision
5573 if ( gameLocal.clip.Motion( collision, body->current->worldOrigin, body->next->worldOrigin, rotation,
5574 body->clipModel, body->current->worldAxis, body->clipMask, passEntity ) ) {
5576 // set the next state to the state at the moment of impact
5577 body->next->worldOrigin = collision.endpos;
5578 body->next->worldAxis = collision.endAxis;
5580 // add collision to the list
5581 index = collisions.Num();
5582 collisions.SetNum( index + 1, false );
5583 collisions[index].trace = collision;
5584 collisions[index].body = body;
5587 #ifdef TEST_COLLISION_DETECTION
5588 if ( gameLocal.clip.Contents( body->next->worldOrigin, body->clipModel,
5589 body->next->worldAxis, body->clipMask, passEntity ) ) {
5590 if ( !startsolid ) {
5597 body->clipModel->Link( gameLocal.clip, self, body->clipModel->GetId(), body->next->worldOrigin, body->next->worldAxis );
5603 idPhysics_AF::EvaluateContacts
5606 bool idPhysics_AF::EvaluateContacts( void ) {
5607 int i, j, k, numContacts, numBodyContacts;
5609 contactInfo_t contactInfo[10];
5610 idEntity *passEntity;
5611 idVecX dir( 6, VECX_ALLOCA( 6 ) );
5614 EvaluateBodies( current.lastTimeStep );
5616 // remove all existing contacts
5619 contactBodies.SetNum( 0, false );
5621 if ( !enableCollision ) {
5625 // find all the contacts
5626 for ( i = 0; i < bodies.Num(); i++ ) {
5629 if ( body->clipMask == 0 ) {
5633 passEntity = SetupCollisionForBody( body );
5635 body->InverseWorldSpatialInertiaMultiply( dir, body->current->externalForce.ToFloatPtr() );
5636 dir.SubVec6(0) = body->current->spatialVelocity + current.lastTimeStep * dir.SubVec6(0);
5637 dir.SubVec3(0).Normalize();
5638 dir.SubVec3(1).Normalize();
5640 numContacts = gameLocal.clip.Contacts( contactInfo, 10, body->current->worldOrigin, dir.SubVec6(0), 2.0f, //CONTACT_EPSILON,
5641 body->clipModel, body->current->worldAxis, body->clipMask, passEntity );
5644 // merge nearby contacts between the same bodies
5645 // and assure there are at most three planar contacts between any pair of bodies
5646 for ( j = 0; j < numContacts; j++ ) {
5648 numBodyContacts = 0;
5649 for ( k = 0; k < contacts.Num(); k++ ) {
5650 if ( contacts[k].entityNum == contactInfo[j].entityNum ) {
5651 if ( ( contacts[k].id == i && contactInfo[j].id == contactBodies[k] ) ||
5652 ( contactBodies[k] == i && contacts[k].id == contactInfo[j].id ) ) {
5654 if ( ( contacts[k].point - contactInfo[j].point ).LengthSqr() < Square( 2.0f ) ) {
5657 if ( idMath::Fabs( contacts[k].normal * contactInfo[j].normal ) > 0.9f ) {
5664 if ( k >= contacts.Num() && numBodyContacts < 3 ) {
5665 contacts.Append( contactInfo[j] );
5666 contactBodies.Append( i );
5672 for ( j = 0; j < numContacts; j++ ) {
5673 contacts.Append( contactInfo[j] );
5674 contactBodies.Append( i );
5680 AddContactEntitiesForContacts();
5682 return ( contacts.Num() != 0 );
5687 idPhysics_AF::SetupContactConstraints
5690 void idPhysics_AF::SetupContactConstraints( void ) {
5693 // make sure enough contact constraints are allocated
5694 contactConstraints.AssureSizeAlloc( contacts.Num(), idListNewElement<idAFConstraint_Contact> );
5695 contactConstraints.SetNum( contacts.Num(), false );
5697 // setup contact constraints
5698 for ( i = 0; i < contacts.Num(); i++ ) {
5699 // add contact constraint
5700 contactConstraints[i]->physics = this;
5701 if ( contacts[i].entityNum == self->entityNumber ) {
5702 contactConstraints[i]->Setup( bodies[contactBodies[i]], bodies[ contacts[i].id ], contacts[i] );
5705 contactConstraints[i]->Setup( bodies[contactBodies[i]], NULL, contacts[i] );
5712 idPhysics_AF::ApplyContactForces
5715 void idPhysics_AF::ApplyContactForces( void ) {
5721 for ( i = 0; i < contactConstraints.Num(); i++ ) {
5722 if ( contactConstraints[i]->body2 != NULL ) {
5725 const contactInfo_t &contact = contactConstraints[i]->GetContact();
5726 ent = gameLocal.entities[contact.entityNum];
5731 ent->AddForce( self, contact.id, contact.point, force );
5738 idPhysics_AF::ClearExternalForce
5741 void idPhysics_AF::ClearExternalForce( void ) {
5745 for ( i = 0; i < bodies.Num(); i++ ) {
5748 // clear external force
5749 body->current->externalForce.Zero();
5750 body->next->externalForce.Zero();
5756 idPhysics_AF::AddGravity
5759 void idPhysics_AF::AddGravity( void ) {
5763 for ( i = 0; i < bodies.Num(); i++ ) {
5765 // add gravitational force
5766 body->current->externalForce.SubVec3( 0 ) += body->mass * gravityVector;
5772 idPhysics_AF::SwapStates
5775 void idPhysics_AF::SwapStates( void ) {
5778 AFBodyPState_t *swap;
5780 for ( i = 0; i < bodies.Num(); i++ ) {
5784 // swap the current and next state for next simulation step
5785 swap = body->current;
5786 body->current = body->next;
5793 idPhysics_AF::UpdateClipModels
5796 void idPhysics_AF::UpdateClipModels( void ) {
5800 for ( i = 0; i < bodies.Num(); i++ ) {
5802 body->clipModel->Link( gameLocal.clip, self, body->clipModel->GetId(), body->current->worldOrigin, body->current->worldAxis );
5808 idPhysics_AF::SetSuspendSpeed
5811 void idPhysics_AF::SetSuspendSpeed( const idVec2 &velocity, const idVec2 &acceleration ) {
5812 this->suspendVelocity = velocity;
5813 this->suspendAcceleration = acceleration;
5818 idPhysics_AF::SetSuspendTime
5821 void idPhysics_AF::SetSuspendTime( const float minTime, const float maxTime ) {
5822 this->minMoveTime = minTime;
5823 this->maxMoveTime = maxTime;
5828 idPhysics_AF::SetSuspendTolerance
5831 void idPhysics_AF::SetSuspendTolerance( const float noMoveTime, const float noMoveTranslation, const float noMoveRotation ) {
5832 this->noMoveTime = noMoveTime;
5833 this->noMoveTranslation = noMoveTranslation;
5834 this->noMoveRotation = noMoveRotation;
5839 idPhysics_AF::SetTimeScaleRamp
5842 void idPhysics_AF::SetTimeScaleRamp( const float start, const float end ) {
5843 timeScaleRampStart = start;
5844 timeScaleRampEnd = end;
5849 idPhysics_AF::SetJointFrictionDent
5852 void idPhysics_AF::SetJointFrictionDent( const float dent, const float start, const float end ) {
5853 jointFrictionDent = dent;
5854 jointFrictionDentStart = start;
5855 jointFrictionDentEnd = end;
5860 idPhysics_AF::GetJointFrictionScale
5863 float idPhysics_AF::GetJointFrictionScale( void ) const {
5864 if ( jointFrictionDentScale > 0.0f ) {
5865 return jointFrictionDentScale;
5866 } else if ( jointFrictionScale > 0.0f ) {
5867 return jointFrictionScale;
5868 } else if ( af_jointFrictionScale.GetFloat() > 0.0f ) {
5869 return af_jointFrictionScale.GetFloat();
5876 idPhysics_AF::SetContactFrictionDent
5879 void idPhysics_AF::SetContactFrictionDent( const float dent, const float start, const float end ) {
5880 contactFrictionDent = dent;
5881 contactFrictionDentStart = start;
5882 contactFrictionDentEnd = end;
5887 idPhysics_AF::GetContactFrictionScale
5890 float idPhysics_AF::GetContactFrictionScale( void ) const {
5891 if ( contactFrictionDentScale > 0.0f ) {
5892 return contactFrictionDentScale;
5893 } else if ( contactFrictionScale > 0.0f ) {
5894 return contactFrictionScale;
5895 } else if ( af_contactFrictionScale.GetFloat() > 0.0f ) {
5896 return af_contactFrictionScale.GetFloat();
5903 idPhysics_AF::TestIfAtRest
5906 bool idPhysics_AF::TestIfAtRest( float timeStep ) {
5908 float translationSqr, maxTranslationSqr, rotation, maxRotation;
5911 if ( current.atRest >= 0 ) {
5915 current.activateTime += timeStep;
5917 // if the simulation should never be suspended before a certaint amount of time passed
5918 if ( minMoveTime > 0.0f && current.activateTime < minMoveTime ) {
5922 // if the simulation should always be suspended after a certain amount time passed
5923 if ( maxMoveTime > 0.0f && current.activateTime > maxMoveTime ) {
5927 // test if all bodies hardly moved over a period of time
5928 if ( current.noMoveTime == 0.0f ) {
5929 for ( i = 0; i < bodies.Num(); i++ ) {
5931 body->atRestOrigin = body->current->worldOrigin;
5932 body->atRestAxis = body->current->worldAxis;
5934 current.noMoveTime += timeStep;
5936 else if ( current.noMoveTime > noMoveTime ) {
5937 current.noMoveTime = 0.0f;
5938 maxTranslationSqr = 0.0f;
5940 for ( i = 0; i < bodies.Num(); i++ ) {
5943 translationSqr = ( body->current->worldOrigin - body->atRestOrigin ).LengthSqr();
5944 if ( translationSqr > maxTranslationSqr ) {
5945 maxTranslationSqr = translationSqr;
5947 rotation = ( body->atRestAxis.Transpose() * body->current->worldAxis ).ToRotation().GetAngle();
5948 if ( rotation > maxRotation ) {
5949 maxRotation = rotation;
5953 if ( maxTranslationSqr < Square( noMoveTranslation ) && maxRotation < noMoveRotation ) {
5954 // hardly moved over a period of time so the articulated figure may come to rest
5958 current.noMoveTime += timeStep;
5961 // test if the velocity or acceleration of any body is still too large to come to rest
5962 for ( i = 0; i < bodies.Num(); i++ ) {
5965 if ( body->current->spatialVelocity.SubVec3(0).LengthSqr() > Square( suspendVelocity[0] ) ) {
5968 if ( body->current->spatialVelocity.SubVec3(1).LengthSqr() > Square( suspendVelocity[1] ) ) {
5971 if ( body->acceleration.SubVec3(0).LengthSqr() > Square( suspendAcceleration[0] ) ) {
5974 if ( body->acceleration.SubVec3(1).LengthSqr() > Square( suspendAcceleration[1] ) ) {
5979 // all bodies have a velocity and acceleration small enough to come to rest
5988 void idPhysics_AF::Rest( void ) {
5991 current.atRest = gameLocal.time;
5993 for ( i = 0; i < bodies.Num(); i++ ) {
5994 bodies[i]->current->spatialVelocity.Zero();
5995 bodies[i]->current->externalForce.Zero();
5998 self->BecomeInactive( TH_PHYSICS );
6003 idPhysics_AF::Activate
6006 void idPhysics_AF::Activate( void ) {
6007 // if the articulated figure was at rest
6008 if ( current.atRest >= 0 ) {
6009 // normally gravity is added at the end of a simulation frame
6010 // if the figure was at rest add gravity here so it is applied this simulation frame
6012 // reset the active time for the max move time
6013 current.activateTime = 0.0f;
6015 current.atRest = -1;
6016 current.noMoveTime = 0.0f;
6017 self->BecomeActive( TH_PHYSICS );
6022 idPhysics_AF::PutToRest
6024 put to rest untill something collides with this physics object
6027 void idPhysics_AF::PutToRest( void ) {
6033 idPhysics_AF::EnableImpact
6036 void idPhysics_AF::EnableImpact( void ) {
6042 idPhysics_AF::DisableImpact
6045 void idPhysics_AF::DisableImpact( void ) {
6051 idPhysics_AF::AddPushVelocity
6054 void idPhysics_AF::AddPushVelocity( const idVec6 &pushVelocity ) {
6057 if ( pushVelocity != vec6_origin ) {
6058 for ( i = 0; i < bodies.Num(); i++ ) {
6059 bodies[i]->current->spatialVelocity += pushVelocity;
6066 idPhysics_AF::SetClipModel
6069 void idPhysics_AF::SetClipModel( idClipModel *model, float density, int id, bool freeOld ) {
6074 idPhysics_AF::GetClipModel
6077 idClipModel *idPhysics_AF::GetClipModel( int id ) const {
6078 if ( id >= 0 && id < bodies.Num() ) {
6079 return bodies[id]->GetClipModel();
6086 idPhysics_AF::GetNumClipModels
6089 int idPhysics_AF::GetNumClipModels( void ) const {
6090 return bodies.Num();
6095 idPhysics_AF::SetMass
6098 void idPhysics_AF::SetMass( float mass, int id ) {
6099 if ( id >= 0 && id < bodies.Num() ) {
6102 forceTotalMass = mass;
6109 idPhysics_AF::GetMass
6112 float idPhysics_AF::GetMass( int id ) const {
6113 if ( id >= 0 && id < bodies.Num() ) {
6114 return bodies[id]->mass;
6121 idPhysics_AF::SetContents
6124 void idPhysics_AF::SetContents( int contents, int id ) {
6127 if ( id >= 0 && id < bodies.Num() ) {
6128 bodies[id]->GetClipModel()->SetContents( contents );
6131 for ( i = 0; i < bodies.Num(); i++ ) {
6132 bodies[i]->GetClipModel()->SetContents( contents );
6139 idPhysics_AF::GetContents
6142 int idPhysics_AF::GetContents( int id ) const {
6145 if ( id >= 0 && id < bodies.Num() ) {
6146 return bodies[id]->GetClipModel()->GetContents();
6150 for ( i = 0; i < bodies.Num(); i++ ) {
6151 contents |= bodies[i]->GetClipModel()->GetContents();
6159 idPhysics_AF::GetBounds
6162 const idBounds &idPhysics_AF::GetBounds( int id ) const {
6164 static idBounds relBounds;
6166 if ( id >= 0 && id < bodies.Num() ) {
6167 return bodies[id]->GetClipModel()->GetBounds();
6169 else if ( !bodies.Num() ) {
6174 relBounds = bodies[0]->GetClipModel()->GetBounds();
6175 for ( i = 1; i < bodies.Num(); i++ ) {
6177 idVec3 origin = ( bodies[i]->GetWorldOrigin() - bodies[0]->GetWorldOrigin() ) * bodies[0]->GetWorldAxis().Transpose();
6178 idMat3 axis = bodies[i]->GetWorldAxis() * bodies[0]->GetWorldAxis().Transpose();
6179 bounds.FromTransformedBounds( bodies[i]->GetClipModel()->GetBounds(), origin, axis );
6180 relBounds += bounds;
6188 idPhysics_AF::GetAbsBounds
6191 const idBounds &idPhysics_AF::GetAbsBounds( int id ) const {
6193 static idBounds absBounds;
6195 if ( id >= 0 && id < bodies.Num() ) {
6196 return bodies[id]->GetClipModel()->GetAbsBounds();
6198 else if ( !bodies.Num() ) {
6203 absBounds = bodies[0]->GetClipModel()->GetAbsBounds();
6204 for ( i = 1; i < bodies.Num(); i++ ) {
6205 absBounds += bodies[i]->GetClipModel()->GetAbsBounds();
6213 idPhysics_AF::Evaluate
6216 bool idPhysics_AF::Evaluate( int timeStepMSec, int endTimeMSec ) {
6219 if ( timeScaleRampStart < MS2SEC( endTimeMSec ) && timeScaleRampEnd > MS2SEC( endTimeMSec ) ) {
6220 timeStep = MS2SEC( timeStepMSec ) * ( MS2SEC( endTimeMSec ) - timeScaleRampStart ) / ( timeScaleRampEnd - timeScaleRampStart );
6221 } else if ( af_timeScale.GetFloat() != 1.0f ) {
6222 timeStep = MS2SEC( timeStepMSec ) * af_timeScale.GetFloat();
6224 timeStep = MS2SEC( timeStepMSec ) * timeScale;
6226 current.lastTimeStep = timeStep;
6229 // if the articulated figure changed
6230 if ( changedAF || ( linearTime != af_useLinearTime.GetBool() ) ) {
6233 linearTime = af_useLinearTime.GetBool();
6236 // get the new master position
6238 idVec3 masterOrigin;
6240 self->GetMasterPosition( masterOrigin, masterAxis );
6241 if ( current.atRest >= 0 && ( masterBody->current->worldOrigin != masterOrigin || masterBody->current->worldAxis != masterAxis ) ) {
6244 masterBody->current->worldOrigin = masterOrigin;
6245 masterBody->current->worldAxis = masterAxis;
6248 // if the simulation is suspended because the figure is at rest
6249 if ( current.atRest >= 0 || timeStep <= 0.0f ) {
6254 // move the af velocity into the frame of a pusher
6255 AddPushVelocity( -current.pushVelocity );
6258 timer_total.Start();
6262 timer_collision.Start();
6265 // evaluate contacts
6268 // setup contact constraints
6269 SetupContactConstraints();
6272 timer_collision.Stop();
6275 // evaluate constraint equations
6276 EvaluateConstraints( timeStep );
6279 ApplyFriction( timeStep, endTimeMSec );
6281 // add frame constraints
6282 AddFrameConstraints();
6285 int i, numPrimary = 0, numAuxiliary = 0;
6286 for ( i = 0; i < primaryConstraints.Num(); i++ ) {
6287 numPrimary += primaryConstraints[i]->J1.GetNumRows();
6289 for ( i = 0; i < auxiliaryConstraints.Num(); i++ ) {
6290 numAuxiliary += auxiliaryConstraints[i]->J1.GetNumRows();
6295 // factor matrices for primary constraints
6298 // calculate forces on bodies after applying primary constraints
6299 PrimaryForces( timeStep );
6306 // calculate and apply auxiliary constraint forces
6307 AuxiliaryForces( timeStep );
6313 // evolve current state to next state
6319 // clear external forces on all bodies
6320 ClearExternalForce();
6322 // apply contact force to other entities
6323 ApplyContactForces();
6325 // remove all frame constraints
6326 RemoveFrameConstraints();
6329 timer_collision.Start();
6332 // check for collisions between current and next state
6333 CheckForCollisions( timeStep );
6336 timer_collision.Stop();
6339 // swap the current and next state
6342 // make sure all clip models are disabled in case they were enabled for self collision
6343 if ( selfCollision && !af_skipSelfCollision.GetBool() ) {
6347 // apply collision impulses
6348 if ( ApplyCollisions( timeStep ) ) {
6349 current.atRest = gameLocal.time;
6353 // test if the simulation can be suspended because the whole figure is at rest
6354 if ( comeToRest && TestIfAtRest( timeStep ) ) {
6357 ActivateContactEntities();
6360 // add gravitational force
6363 // move the af velocity back into the world frame
6364 AddPushVelocity( current.pushVelocity );
6365 current.pushVelocity.Zero();
6367 if ( IsOutsideWorld() ) {
6368 gameLocal.Warning( "articulated figure moved outside world bounds for entity '%s' type '%s' at (%s)",
6369 self->name.c_str(), self->GetType()->classname, bodies[0]->current->worldOrigin.ToString(0) );
6376 if ( af_showTimings.GetInteger() == 1 ) {
6377 gameLocal.Printf( "%12s: t %1.4f pc %2d, %1.4f ac %2d %1.4f lcp %1.4f cd %1.4f\n",
6379 timer_total.Milliseconds(),
6380 numPrimary, timer_pc.Milliseconds(),
6381 numAuxiliary, timer_ac.Milliseconds() - timer_lcp.Milliseconds(),
6382 timer_lcp.Milliseconds(), timer_collision.Milliseconds() );
6384 else if ( af_showTimings.GetInteger() == 2 ) {
6385 numArticulatedFigures++;
6386 if ( endTimeMSec > lastTimerReset ) {
6387 gameLocal.Printf( "af %d: t %1.4f pc %2d, %1.4f ac %2d %1.4f lcp %1.4f cd %1.4f\n",
6388 numArticulatedFigures,
6389 timer_total.Milliseconds(),
6390 numPrimary, timer_pc.Milliseconds(),
6391 numAuxiliary, timer_ac.Milliseconds() - timer_lcp.Milliseconds(),
6392 timer_lcp.Milliseconds(), timer_collision.Milliseconds() );
6396 if ( endTimeMSec > lastTimerReset ) {
6397 lastTimerReset = endTimeMSec;
6398 numArticulatedFigures = 0;
6399 timer_total.Clear();
6402 timer_collision.Clear();
6412 idPhysics_AF::UpdateTime
6415 void idPhysics_AF::UpdateTime( int endTimeMSec ) {
6420 idPhysics_AF::GetTime
6423 int idPhysics_AF::GetTime( void ) const {
6424 return gameLocal.time;
6429 DrawTraceModelSilhouette
6432 void DrawTraceModelSilhouette( const idVec3 &projectionOrigin, const idClipModel *clipModel ) {
6434 int silEdges[MAX_TRACEMODEL_EDGES];
6436 const idTraceModel *trm = clipModel->GetTraceModel();
6437 const idVec3 &origin = clipModel->GetOrigin();
6438 const idMat3 &axis = clipModel->GetAxis();
6440 numSilEdges = trm->GetProjectionSilhouetteEdges( ( projectionOrigin - origin ) * axis.Transpose(), silEdges );
6441 for ( i = 0; i < numSilEdges; i++ ) {
6442 v1 = trm->verts[ trm->edges[ abs(silEdges[i]) ].v[ INTSIGNBITSET( silEdges[i] ) ] ];
6443 v2 = trm->verts[ trm->edges[ abs(silEdges[i]) ].v[ INTSIGNBITNOTSET( silEdges[i] ) ] ];
6444 gameRenderWorld->DebugArrow( colorRed, origin + v1 * axis, origin + v2 * axis, 1 );
6450 idPhysics_AF::DebugDraw
6453 void idPhysics_AF::DebugDraw( void ) {
6455 idAFBody *body, *highlightBody = NULL, *constrainedBody1 = NULL, *constrainedBody2 = NULL;
6456 idAFConstraint *constraint;
6460 if ( af_highlightConstraint.GetString()[0] ) {
6461 constraint = GetConstraint( af_highlightConstraint.GetString() );
6463 constraint->GetCenter( center );
6464 axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3();
6465 gameRenderWorld->DebugCone( colorYellow, center, (axis[2] - axis[1]) * 4.0f, 0.0f, 1.0f, 0 );
6467 if ( af_showConstrainedBodies.GetBool() ) {
6468 cvarSystem->SetCVarString( "cm_drawColor", colorCyan.ToString( 0 ) );
6469 constrainedBody1 = constraint->body1;
6470 if ( constrainedBody1 ) {
6471 collisionModelManager->DrawModel( constrainedBody1->clipModel->Handle(), constrainedBody1->clipModel->GetOrigin(),
6472 constrainedBody1->clipModel->GetAxis(), vec3_origin, 0.0f );
6474 cvarSystem->SetCVarString( "cm_drawColor", colorBlue.ToString( 0 ) );
6475 constrainedBody2 = constraint->body2;
6476 if ( constrainedBody2 ) {
6477 collisionModelManager->DrawModel( constrainedBody2->clipModel->Handle(), constrainedBody2->clipModel->GetOrigin(),
6478 constrainedBody2->clipModel->GetAxis(), vec3_origin, 0.0f );
6480 cvarSystem->SetCVarString( "cm_drawColor", colorRed.ToString( 0 ) );
6485 if ( af_highlightBody.GetString()[0] ) {
6486 highlightBody = GetBody( af_highlightBody.GetString() );
6487 if ( highlightBody ) {
6488 cvarSystem->SetCVarString( "cm_drawColor", colorYellow.ToString( 0 ) );
6489 collisionModelManager->DrawModel( highlightBody->clipModel->Handle(), highlightBody->clipModel->GetOrigin(),
6490 highlightBody->clipModel->GetAxis(), vec3_origin, 0.0f );
6491 cvarSystem->SetCVarString( "cm_drawColor", colorRed.ToString( 0 ) );
6495 if ( af_showBodies.GetBool() ) {
6496 for ( i = 0; i < bodies.Num(); i++ ) {
6498 if ( body == constrainedBody1 || body == constrainedBody2 ) {
6501 if ( body == highlightBody ) {
6504 collisionModelManager->DrawModel( body->clipModel->Handle(), body->clipModel->GetOrigin(),
6505 body->clipModel->GetAxis(), vec3_origin, 0.0f );
6506 //DrawTraceModelSilhouette( gameLocal.GetLocalPlayer()->GetEyePosition(), body->clipModel );
6510 if ( af_showBodyNames.GetBool() ) {
6511 for ( i = 0; i < bodies.Num(); i++ ) {
6513 gameRenderWorld->DrawText( body->GetName().c_str(), body->GetWorldOrigin(), 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
6517 if ( af_showMass.GetBool() ) {
6518 for ( i = 0; i < bodies.Num(); i++ ) {
6520 gameRenderWorld->DrawText( va( "\n%1.2f", 1.0f / body->GetInverseMass() ), body->GetWorldOrigin(), 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
6524 if ( af_showTotalMass.GetBool() ) {
6525 axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3();
6526 gameRenderWorld->DrawText( va( "\n%1.2f", totalMass ), bodies[0]->GetWorldOrigin() + axis[2] * 8.0f, 0.15f, colorCyan, axis, 1 );
6529 if ( af_showInertia.GetBool() ) {
6530 for ( i = 0; i < bodies.Num(); i++ ) {
6532 idMat3 &I = body->inertiaTensor;
6533 gameRenderWorld->DrawText( va( "\n\n\n( %.1f %.1f %.1f )\n( %.1f %.1f %.1f )\n( %.1f %.1f %.1f )",
6534 I[0].x, I[0].y, I[0].z,
6535 I[1].x, I[1].y, I[1].z,
6536 I[2].x, I[2].y, I[2].z ),
6537 body->GetWorldOrigin(), 0.05f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
6541 if ( af_showVelocity.GetBool() ) {
6542 for ( i = 0; i < bodies.Num(); i++ ) {
6543 DrawVelocity( bodies[i]->clipModel->GetId(), 0.1f, 4.0f );
6547 if ( af_showConstraints.GetBool() ) {
6548 for ( i = 0; i < primaryConstraints.Num(); i++ ) {
6549 constraint = primaryConstraints[i];
6550 constraint->DebugDraw();
6552 if ( !af_showPrimaryOnly.GetBool() ) {
6553 for ( i = 0; i < auxiliaryConstraints.Num(); i++ ) {
6554 constraint = auxiliaryConstraints[i];
6555 constraint->DebugDraw();
6560 if ( af_showConstraintNames.GetBool() ) {
6561 for ( i = 0; i < primaryConstraints.Num(); i++ ) {
6562 constraint = primaryConstraints[i];
6563 constraint->GetCenter( center );
6564 gameRenderWorld->DrawText( constraint->GetName().c_str(), center, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
6566 if ( !af_showPrimaryOnly.GetBool() ) {
6567 for ( i = 0; i < auxiliaryConstraints.Num(); i++ ) {
6568 constraint = auxiliaryConstraints[i];
6569 constraint->GetCenter( center );
6570 gameRenderWorld->DrawText( constraint->GetName().c_str(), center, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
6575 if ( af_showTrees.GetBool() || ( af_showActive.GetBool() && current.atRest < 0 ) ) {
6576 for ( i = 0; i < trees.Num(); i++ ) {
6577 trees[i]->DebugDraw( idStr::ColorForIndex( i+3 ) );
6584 idPhysics_AF::idPhysics_AF
6587 idPhysics_AF::idPhysics_AF( void ) {
6590 constraints.Clear();
6591 primaryConstraints.Clear();
6592 auxiliaryConstraints.Clear();
6593 frameConstraints.Clear();
6599 lcp = idLCP::AllocSymmetric();
6601 memset( ¤t, 0, sizeof( current ) );
6602 current.atRest = -1;
6603 current.lastTimeStep = USERCMD_MSEC;
6606 linearFriction = 0.005f;
6607 angularFriction = 0.005f;
6608 contactFriction = 0.8f;
6611 forceTotalMass = -1.0f;
6613 suspendVelocity.Set( SUSPEND_LINEAR_VELOCITY, SUSPEND_ANGULAR_VELOCITY );
6614 suspendAcceleration.Set( SUSPEND_LINEAR_ACCELERATION, SUSPEND_LINEAR_ACCELERATION );
6615 noMoveTime = NO_MOVE_TIME;
6616 noMoveTranslation = NO_MOVE_TRANSLATION_TOLERANCE;
6617 noMoveRotation = NO_MOVE_ROTATION_TOLERANCE;
6618 minMoveTime = MIN_MOVE_TIME;
6619 maxMoveTime = MAX_MOVE_TIME;
6620 impulseThreshold = IMPULSE_THRESHOLD;
6623 timeScaleRampStart = 0.0f;
6624 timeScaleRampEnd = 0.0f;
6626 jointFrictionScale = 0.0f;
6627 jointFrictionDent = 0.0f;
6628 jointFrictionDentStart = 0.0f;
6629 jointFrictionDentEnd = 0.0f;
6630 jointFrictionDentScale = 0.0f;
6632 contactFrictionScale = 0.0f;
6633 contactFrictionDent = 0.0f;
6634 contactFrictionDentStart = 0.0f;
6635 contactFrictionDentEnd = 0.0f;
6636 contactFrictionDentScale = 0.0f;
6638 enableCollision = true;
6639 selfCollision = true;
6643 worldConstraintsLocked = false;
6644 forcePushable = false;
6653 idPhysics_AF::~idPhysics_AF
6656 idPhysics_AF::~idPhysics_AF( void ) {
6659 trees.DeleteContents( true );
6661 for ( i = 0; i < bodies.Num(); i++ ) {
6665 for ( i = 0; i < constraints.Num(); i++ ) {
6666 delete constraints[i];
6669 contactConstraints.SetNum( contactConstraints.NumAllocated(), false );
6670 for ( i = 0; i < contactConstraints.NumAllocated(); i++ ) {
6671 delete contactConstraints[i];
6683 idPhysics_AF_SavePState
6686 void idPhysics_AF_SavePState( idSaveGame *saveFile, const AFPState_t &state ) {
6687 saveFile->WriteInt( state.atRest );
6688 saveFile->WriteFloat( state.noMoveTime );
6689 saveFile->WriteFloat( state.activateTime );
6690 saveFile->WriteFloat( state.lastTimeStep );
6691 saveFile->WriteVec6( state.pushVelocity );
6696 idPhysics_AF_RestorePState
6699 void idPhysics_AF_RestorePState( idRestoreGame *saveFile, AFPState_t &state ) {
6700 saveFile->ReadInt( state.atRest );
6701 saveFile->ReadFloat( state.noMoveTime );
6702 saveFile->ReadFloat( state.activateTime );
6703 saveFile->ReadFloat( state.lastTimeStep );
6704 saveFile->ReadVec6( state.pushVelocity );
6712 void idPhysics_AF::Save( idSaveGame *saveFile ) const {
6715 // the articulated figure structure is handled by the owner
6717 idPhysics_AF_SavePState( saveFile, current );
6718 idPhysics_AF_SavePState( saveFile, saved );
6720 saveFile->WriteInt( bodies.Num() );
6721 for ( i = 0; i < bodies.Num(); i++ ) {
6722 bodies[i]->Save( saveFile );
6725 saveFile->WriteBool( true );
6726 masterBody->Save( saveFile );
6728 saveFile->WriteBool( false );
6731 saveFile->WriteInt( constraints.Num() );
6732 for ( i = 0; i < constraints.Num(); i++ ) {
6733 constraints[i]->Save( saveFile );
6736 saveFile->WriteBool( changedAF );
6738 saveFile->WriteFloat( linearFriction );
6739 saveFile->WriteFloat( angularFriction );
6740 saveFile->WriteFloat( contactFriction );
6741 saveFile->WriteFloat( bouncyness );
6742 saveFile->WriteFloat( totalMass );
6743 saveFile->WriteFloat( forceTotalMass );
6745 saveFile->WriteVec2( suspendVelocity );
6746 saveFile->WriteVec2( suspendAcceleration );
6747 saveFile->WriteFloat( noMoveTime );
6748 saveFile->WriteFloat( noMoveTranslation );
6749 saveFile->WriteFloat( noMoveRotation );
6750 saveFile->WriteFloat( minMoveTime );
6751 saveFile->WriteFloat( maxMoveTime );
6752 saveFile->WriteFloat( impulseThreshold );
6754 saveFile->WriteFloat( timeScale );
6755 saveFile->WriteFloat( timeScaleRampStart );
6756 saveFile->WriteFloat( timeScaleRampEnd );
6758 saveFile->WriteFloat( jointFrictionScale );
6759 saveFile->WriteFloat( jointFrictionDent );
6760 saveFile->WriteFloat( jointFrictionDentStart );
6761 saveFile->WriteFloat( jointFrictionDentEnd );
6762 saveFile->WriteFloat( jointFrictionDentScale );
6764 saveFile->WriteFloat( contactFrictionScale );
6765 saveFile->WriteFloat( contactFrictionDent );
6766 saveFile->WriteFloat( contactFrictionDentStart );
6767 saveFile->WriteFloat( contactFrictionDentEnd );
6768 saveFile->WriteFloat( contactFrictionDentScale );
6770 saveFile->WriteBool( enableCollision );
6771 saveFile->WriteBool( selfCollision );
6772 saveFile->WriteBool( comeToRest );
6773 saveFile->WriteBool( linearTime );
6774 saveFile->WriteBool( noImpact );
6775 saveFile->WriteBool( worldConstraintsLocked );
6776 saveFile->WriteBool( forcePushable );
6781 idPhysics_AF::Restore
6784 void idPhysics_AF::Restore( idRestoreGame *saveFile ) {
6788 // the articulated figure structure should have already been restored
6790 idPhysics_AF_RestorePState( saveFile, current );
6791 idPhysics_AF_RestorePState( saveFile, saved );
6793 saveFile->ReadInt( num );
6794 assert( num == bodies.Num() );
6795 for ( i = 0; i < bodies.Num(); i++ ) {
6796 bodies[i]->Restore( saveFile );
6798 saveFile->ReadBool( hasMaster );
6800 masterBody = new idAFBody();
6801 masterBody->Restore( saveFile );
6804 saveFile->ReadInt( num );
6805 assert( num == constraints.Num() );
6806 for ( i = 0; i < constraints.Num(); i++ ) {
6807 constraints[i]->Restore( saveFile );
6810 saveFile->ReadBool( changedAF );
6812 saveFile->ReadFloat( linearFriction );
6813 saveFile->ReadFloat( angularFriction );
6814 saveFile->ReadFloat( contactFriction );
6815 saveFile->ReadFloat( bouncyness );
6816 saveFile->ReadFloat( totalMass );
6817 saveFile->ReadFloat( forceTotalMass );
6819 saveFile->ReadVec2( suspendVelocity );
6820 saveFile->ReadVec2( suspendAcceleration );
6821 saveFile->ReadFloat( noMoveTime );
6822 saveFile->ReadFloat( noMoveTranslation );
6823 saveFile->ReadFloat( noMoveRotation );
6824 saveFile->ReadFloat( minMoveTime );
6825 saveFile->ReadFloat( maxMoveTime );
6826 saveFile->ReadFloat( impulseThreshold );
6828 saveFile->ReadFloat( timeScale );
6829 saveFile->ReadFloat( timeScaleRampStart );
6830 saveFile->ReadFloat( timeScaleRampEnd );
6832 saveFile->ReadFloat( jointFrictionScale );
6833 saveFile->ReadFloat( jointFrictionDent );
6834 saveFile->ReadFloat( jointFrictionDentStart );
6835 saveFile->ReadFloat( jointFrictionDentEnd );
6836 saveFile->ReadFloat( jointFrictionDentScale );
6838 saveFile->ReadFloat( contactFrictionScale );
6839 saveFile->ReadFloat( contactFrictionDent );
6840 saveFile->ReadFloat( contactFrictionDentStart );
6841 saveFile->ReadFloat( contactFrictionDentEnd );
6842 saveFile->ReadFloat( contactFrictionDentScale );
6844 saveFile->ReadBool( enableCollision );
6845 saveFile->ReadBool( selfCollision );
6846 saveFile->ReadBool( comeToRest );
6847 saveFile->ReadBool( linearTime );
6848 saveFile->ReadBool( noImpact );
6849 saveFile->ReadBool( worldConstraintsLocked );
6850 saveFile->ReadBool( forcePushable );
6859 idPhysics_AF::IsClosedLoop
6862 bool idPhysics_AF::IsClosedLoop( const idAFBody *body1, const idAFBody *body2 ) const {
6863 const idAFBody *b1, *b2;
6865 for ( b1 = body1; b1->parent; b1 = b1->parent ) {
6867 for ( b2 = body2; b2->parent; b2 = b2->parent ) {
6869 return ( b1 == b2 );
6874 idPhysics_AF::BuildTrees
6877 void idPhysics_AF::BuildTrees( void ) {
6884 primaryConstraints.Clear();
6885 auxiliaryConstraints.Clear();
6886 trees.DeleteContents( true );
6889 for ( i = 0; i < bodies.Num(); i++ ) {
6892 b->primaryConstraint = NULL;
6893 b->constraints.SetNum( 0, false );
6894 b->children.Clear();
6896 totalMass += b->mass;
6899 if ( forceTotalMass > 0.0f ) {
6900 scale = forceTotalMass / totalMass;
6901 for ( i = 0; i < bodies.Num(); i++ ) {
6904 b->invMass = 1.0f / b->mass;
6905 b->inertiaTensor *= scale;
6906 b->inverseInertiaTensor = b->inertiaTensor.Inverse();
6908 totalMass = forceTotalMass;
6911 if ( af_useLinearTime.GetBool() ) {
6913 for ( i = 0; i < constraints.Num(); i++ ) {
6916 c->body1->constraints.Append( c );
6918 c->body2->constraints.Append( c );
6921 // only bilateral constraints between two non-world bodies that do not
6922 // create loops can be used as primary constraints
6923 if ( !c->body1->primaryConstraint && c->fl.allowPrimary && c->body2 != NULL && !IsClosedLoop( c->body1, c->body2 ) ) {
6924 c->body1->primaryConstraint = c;
6925 c->body1->parent = c->body2;
6926 c->body2->children.Append( c->body1 );
6927 c->fl.isPrimary = true;
6929 primaryConstraints.Append( c );
6931 c->fl.isPrimary = false;
6932 auxiliaryConstraints.Append( c );
6936 // create trees for all parent bodies
6937 for ( i = 0; i < bodies.Num(); i++ ) {
6938 if ( !bodies[i]->parent ) {
6939 tree = new idAFTree();
6940 tree->sortedBodies.Clear();
6941 tree->sortedBodies.Append( bodies[i] );
6942 bodies[i]->tree = tree;
6943 trees.Append( tree );
6947 // add each child body to the appropriate tree
6948 for ( i = 0; i < bodies.Num(); i++ ) {
6949 if ( bodies[i]->parent ) {
6950 for ( b = bodies[i]->parent; !b->tree; b = b->parent ) {
6952 b->tree->sortedBodies.Append( bodies[i] );
6953 bodies[i]->tree = b->tree;
6957 if ( trees.Num() > 1 ) {
6958 gameLocal.Warning( "Articulated figure has multiple seperate tree structures for entity '%s' type '%s'.",
6959 self->name.c_str(), self->GetType()->classname );
6962 // sort bodies in each tree to make sure parents come first
6963 for ( i = 0; i < trees.Num(); i++ ) {
6964 trees[i]->SortBodies();
6969 // create a tree for each body
6970 for ( i = 0; i < bodies.Num(); i++ ) {
6971 tree = new idAFTree();
6972 tree->sortedBodies.Clear();
6973 tree->sortedBodies.Append( bodies[i] );
6974 bodies[i]->tree = tree;
6975 trees.Append( tree );
6978 for ( i = 0; i < constraints.Num(); i++ ) {
6981 c->body1->constraints.Append( c );
6983 c->body2->constraints.Append( c );
6986 c->fl.isPrimary = false;
6987 auxiliaryConstraints.Append( c );
6994 idPhysics_AF::AddBody
6996 bodies get an id in the order they are added starting at zero
6997 as such the first body added will get id zero
7000 int idPhysics_AF::AddBody( idAFBody *body ) {
7003 if ( !body->clipModel ) {
7004 gameLocal.Error( "idPhysics_AF::AddBody: body '%s' has no clip model.", body->name.c_str() );
7007 if ( bodies.Find( body ) ) {
7008 gameLocal.Error( "idPhysics_AF::AddBody: body '%s' added twice.", body->name.c_str() );
7011 if ( GetBody( body->name ) ) {
7012 gameLocal.Error( "idPhysics_AF::AddBody: a body with the name '%s' already exists.", body->name.c_str() );
7016 body->clipModel->SetId( id );
7017 if ( body->linearFriction < 0.0f ) {
7018 body->linearFriction = linearFriction;
7019 body->angularFriction = angularFriction;
7020 body->contactFriction = contactFriction;
7022 if ( body->bouncyness < 0.0f ) {
7023 body->bouncyness = bouncyness;
7025 if ( !body->fl.clipMaskSet ) {
7026 body->clipMask = clipMask;
7029 bodies.Append( body );
7038 idPhysics_AF::AddConstraint
7041 void idPhysics_AF::AddConstraint( idAFConstraint *constraint ) {
7043 if ( constraints.Find( constraint ) ) {
7044 gameLocal.Error( "idPhysics_AF::AddConstraint: constraint '%s' added twice.", constraint->name.c_str() );
7046 if ( GetConstraint( constraint->name ) ) {
7047 gameLocal.Error( "idPhysics_AF::AddConstraint: a constraint with the name '%s' already exists.", constraint->name.c_str() );
7049 if ( !constraint->body1 ) {
7050 gameLocal.Error( "idPhysics_AF::AddConstraint: body1 == NULL on constraint '%s'.", constraint->name.c_str() );
7052 if ( !bodies.Find( constraint->body1 ) ) {
7053 gameLocal.Error( "idPhysics_AF::AddConstraint: body1 of constraint '%s' is not part of the articulated figure.", constraint->name.c_str() );
7055 if ( constraint->body2 && !bodies.Find( constraint->body2 ) ) {
7056 gameLocal.Error( "idPhysics_AF::AddConstraint: body2 of constraint '%s' is not part of the articulated figure.", constraint->name.c_str() );
7058 if ( constraint->body1 == constraint->body2 ) {
7059 gameLocal.Error( "idPhysics_AF::AddConstraint: body1 and body2 of constraint '%s' are the same.", constraint->name.c_str() );
7062 constraints.Append( constraint );
7063 constraint->physics = this;
7070 idPhysics_AF::AddFrameConstraint
7073 void idPhysics_AF::AddFrameConstraint( idAFConstraint *constraint ) {
7074 frameConstraints.Append( constraint );
7075 constraint->physics = this;
7080 idPhysics_AF::ForceBodyId
7083 void idPhysics_AF::ForceBodyId( idAFBody *body, int newId ) {
7086 id = bodies.FindIndex( body );
7088 gameLocal.Error( "ForceBodyId: body '%s' is not part of the articulated figure.\n", body->name.c_str() );
7090 if ( id != newId ) {
7091 idAFBody *b = bodies[newId];
7092 bodies[newId] = bodies[id];
7100 idPhysics_AF::GetBodyId
7103 int idPhysics_AF::GetBodyId( idAFBody *body ) const {
7106 id = bodies.FindIndex( body );
7107 if ( id == -1 && body ) {
7108 gameLocal.Error( "GetBodyId: body '%s' is not part of the articulated figure.\n", body->name.c_str() );
7115 idPhysics_AF::GetBodyId
7118 int idPhysics_AF::GetBodyId( const char *bodyName ) const {
7121 for ( i = 0; i < bodies.Num(); i++ ) {
7122 if ( !bodies[i]->name.Icmp( bodyName ) ) {
7126 gameLocal.Error( "GetBodyId: no body with the name '%s' is not part of the articulated figure.\n", bodyName );
7132 idPhysics_AF::GetConstraintId
7135 int idPhysics_AF::GetConstraintId( idAFConstraint *constraint ) const {
7138 id = constraints.FindIndex( constraint );
7139 if ( id == -1 && constraint ) {
7140 gameLocal.Error( "GetConstraintId: constraint '%s' is not part of the articulated figure.\n", constraint->name.c_str() );
7147 idPhysics_AF::GetConstraintId
7150 int idPhysics_AF::GetConstraintId( const char *constraintName ) const {
7153 for ( i = 0; i < constraints.Num(); i++ ) {
7154 if ( constraints[i]->name.Icmp( constraintName ) == 0 ) {
7158 gameLocal.Error( "GetConstraintId: no constraint with the name '%s' is not part of the articulated figure.\n", constraintName );
7164 idPhysics_AF::GetNumBodies
7167 int idPhysics_AF::GetNumBodies( void ) const {
7168 return bodies.Num();
7173 idPhysics_AF::GetNumConstraints
7176 int idPhysics_AF::GetNumConstraints( void ) const {
7177 return constraints.Num();
7182 idPhysics_AF::GetBody
7185 idAFBody *idPhysics_AF::GetBody( const char *bodyName ) const {
7188 for ( i = 0; i < bodies.Num(); i++ ) {
7189 if ( !bodies[i]->name.Icmp( bodyName ) ) {
7199 idPhysics_AF::GetBody
7202 idAFBody *idPhysics_AF::GetBody( const int id ) const {
7203 if ( id < 0 || id >= bodies.Num() ) {
7204 gameLocal.Error( "GetBody: no body with id %d exists\n", id );
7212 idPhysics_AF::GetConstraint
7215 idAFConstraint *idPhysics_AF::GetConstraint( const char *constraintName ) const {
7218 for ( i = 0; i < constraints.Num(); i++ ) {
7219 if ( constraints[i]->name.Icmp( constraintName ) == 0 ) {
7220 return constraints[i];
7229 idPhysics_AF::GetConstraint
7232 idAFConstraint *idPhysics_AF::GetConstraint( const int id ) const {
7233 if ( id < 0 || id >= constraints.Num() ) {
7234 gameLocal.Error( "GetConstraint: no constraint with id %d exists\n", id );
7237 return constraints[id];
7242 idPhysics_AF::DeleteBody
7245 void idPhysics_AF::DeleteBody( const char *bodyName ) {
7248 // find the body with the given name
7249 for ( i = 0; i < bodies.Num(); i++ ) {
7250 if ( !bodies[i]->name.Icmp( bodyName ) ) {
7255 if ( i >= bodies.Num() ) {
7256 gameLocal.Warning( "DeleteBody: no body found in the articulated figure with the name '%s' for entity '%s' type '%s'.",
7257 bodyName, self->name.c_str(), self->GetType()->classname );
7266 idPhysics_AF::DeleteBody
7269 void idPhysics_AF::DeleteBody( const int id ) {
7272 if ( id < 0 || id > bodies.Num() ) {
7273 gameLocal.Error( "DeleteBody: no body with id %d.", id );
7277 // remove any constraints attached to this body
7278 for ( j = 0; j < constraints.Num(); j++ ) {
7279 if ( constraints[j]->body1 == bodies[id] || constraints[j]->body2 == bodies[id] ) {
7280 delete constraints[j];
7281 constraints.RemoveIndex( j );
7288 bodies.RemoveIndex( id );
7291 for ( j = 0; j < bodies.Num(); j++ ) {
7292 bodies[j]->clipModel->SetId( j );
7300 idPhysics_AF::DeleteConstraint
7303 void idPhysics_AF::DeleteConstraint( const char *constraintName ) {
7306 // find the constraint with the given name
7307 for ( i = 0; i < constraints.Num(); i++ ) {
7308 if ( !constraints[i]->name.Icmp( constraintName ) ) {
7313 if ( i >= constraints.Num() ) {
7314 gameLocal.Warning( "DeleteConstraint: no constriant found in the articulated figure with the name '%s' for entity '%s' type '%s'.",
7315 constraintName, self->name.c_str(), self->GetType()->classname );
7319 DeleteConstraint( i );
7324 idPhysics_AF::DeleteConstraint
7327 void idPhysics_AF::DeleteConstraint( const int id ) {
7329 if ( id < 0 || id >= constraints.Num() ) {
7330 gameLocal.Error( "DeleteConstraint: no constraint with id %d.", id );
7334 // remove the constraint
7335 delete constraints[id];
7336 constraints.RemoveIndex( id );
7343 idPhysics_AF::GetBodyContactConstraints
7346 int idPhysics_AF::GetBodyContactConstraints( const int id, idAFConstraint_Contact *contacts[], int maxContacts ) const {
7349 idAFConstraint_Contact *contact;
7351 if ( id < 0 || id >= bodies.Num() || maxContacts <= 0 ) {
7357 for ( i = 0; i < contactConstraints.Num(); i++ ) {
7358 contact = contactConstraints[i];
7359 if ( contact->body1 == body || contact->body2 == body ) {
7360 contacts[numContacts++] = contact;
7361 if ( numContacts >= maxContacts ) {
7371 idPhysics_AF::SetDefaultFriction
7374 void idPhysics_AF::SetDefaultFriction( float linear, float angular, float contact ) {
7375 if ( linear < 0.0f || linear > 1.0f ||
7376 angular < 0.0f || angular > 1.0f ||
7377 contact < 0.0f || contact > 1.0f ) {
7380 linearFriction = linear;
7381 angularFriction = angular;
7382 contactFriction = contact;
7387 idPhysics_AF::GetImpactInfo
7390 void idPhysics_AF::GetImpactInfo( const int id, const idVec3 &point, impactInfo_t *info ) const {
7391 if ( id < 0 || id >= bodies.Num() ) {
7392 memset( info, 0, sizeof( *info ) );
7395 info->invMass = 1.0f / bodies[id]->mass;
7396 info->invInertiaTensor = bodies[id]->current->worldAxis.Transpose() * bodies[id]->inverseInertiaTensor * bodies[id]->current->worldAxis;
7397 info->position = point - bodies[id]->current->worldOrigin;
7398 info->velocity = bodies[id]->current->spatialVelocity.SubVec3(0) + bodies[id]->current->spatialVelocity.SubVec3(1).Cross( info->position );
7403 idPhysics_AF::ApplyImpulse
7406 void idPhysics_AF::ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ) {
7407 if ( id < 0 || id >= bodies.Num() ) {
7410 if ( noImpact || impulse.LengthSqr() < Square( impulseThreshold ) ) {
7413 idMat3 invWorldInertiaTensor = bodies[id]->current->worldAxis.Transpose() * bodies[id]->inverseInertiaTensor * bodies[id]->current->worldAxis;
7414 bodies[id]->current->spatialVelocity.SubVec3(0) += bodies[id]->invMass * impulse;
7415 bodies[id]->current->spatialVelocity.SubVec3(1) += invWorldInertiaTensor * (point - bodies[id]->current->worldOrigin).Cross( impulse );
7421 idPhysics_AF::AddForce
7424 void idPhysics_AF::AddForce( const int id, const idVec3 &point, const idVec3 &force ) {
7428 if ( id < 0 || id >= bodies.Num() ) {
7431 bodies[id]->current->externalForce.SubVec3( 0 ) += force;
7432 bodies[id]->current->externalForce.SubVec3( 1 ) += (point - bodies[id]->current->worldOrigin).Cross( force );
7438 idPhysics_AF::IsAtRest
7441 bool idPhysics_AF::IsAtRest( void ) const {
7442 return current.atRest >= 0;
7447 idPhysics_AF::GetRestStartTime
7450 int idPhysics_AF::GetRestStartTime( void ) const {
7451 return current.atRest;
7456 idPhysics_AF::IsPushable
7459 bool idPhysics_AF::IsPushable( void ) const {
7460 return ( !noImpact && ( masterBody == NULL || forcePushable ) );
7465 idPhysics_AF::SaveState
7468 void idPhysics_AF::SaveState( void ) {
7473 for ( i = 0; i < bodies.Num(); i++ ) {
7474 memcpy( &bodies[i]->saved, bodies[i]->current, sizeof( AFBodyPState_t ) );
7480 idPhysics_AF::RestoreState
7483 void idPhysics_AF::RestoreState( void ) {
7488 for ( i = 0; i < bodies.Num(); i++ ) {
7489 *(bodies[i]->current) = bodies[i]->saved;
7497 idPhysics_AF::SetOrigin
7500 void idPhysics_AF::SetOrigin( const idVec3 &newOrigin, int id ) {
7502 Translate( masterBody->current->worldOrigin + masterBody->current->worldAxis * newOrigin - bodies[0]->current->worldOrigin );
7504 Translate( newOrigin - bodies[0]->current->worldOrigin );
7510 idPhysics_AF::SetAxis
7513 void idPhysics_AF::SetAxis( const idMat3 &newAxis, int id ) {
7515 idRotation rotation;
7518 axis = bodies[0]->current->worldAxis.Transpose() * ( newAxis * masterBody->current->worldAxis );
7520 axis = bodies[0]->current->worldAxis.Transpose() * newAxis;
7522 rotation = axis.ToRotation();
7523 rotation.SetOrigin( bodies[0]->current->worldOrigin );
7530 idPhysics_AF::Translate
7533 void idPhysics_AF::Translate( const idVec3 &translation, int id ) {
7537 if ( !worldConstraintsLocked ) {
7538 // translate constraints attached to the world
7539 for ( i = 0; i < constraints.Num(); i++ ) {
7540 constraints[i]->Translate( translation );
7544 // translate all the bodies
7545 for ( i = 0; i < bodies.Num(); i++ ) {
7548 body->current->worldOrigin += translation;
7558 idPhysics_AF::Rotate
7561 void idPhysics_AF::Rotate( const idRotation &rotation, int id ) {
7565 if ( !worldConstraintsLocked ) {
7566 // rotate constraints attached to the world
7567 for ( i = 0; i < constraints.Num(); i++ ) {
7568 constraints[i]->Rotate( rotation );
7572 // rotate all the bodies
7573 for ( i = 0; i < bodies.Num(); i++ ) {
7576 body->current->worldOrigin *= rotation;
7577 body->current->worldAxis *= rotation.ToMat3();
7587 idPhysics_AF::GetOrigin
7590 const idVec3 &idPhysics_AF::GetOrigin( int id ) const {
7591 if ( id < 0 || id >= bodies.Num() ) {
7595 return bodies[id]->current->worldOrigin;
7601 idPhysics_AF::GetAxis
7604 const idMat3 &idPhysics_AF::GetAxis( int id ) const {
7605 if ( id < 0 || id >= bodies.Num() ) {
7606 return mat3_identity;
7609 return bodies[id]->current->worldAxis;
7615 idPhysics_AF::SetLinearVelocity
7618 void idPhysics_AF::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
7619 if ( id < 0 || id >= bodies.Num() ) {
7622 bodies[id]->current->spatialVelocity.SubVec3( 0 ) = newLinearVelocity;
7628 idPhysics_AF::SetAngularVelocity
7631 void idPhysics_AF::SetAngularVelocity( const idVec3 &newAngularVelocity, int id ) {
7632 if ( id < 0 || id >= bodies.Num() ) {
7635 bodies[id]->current->spatialVelocity.SubVec3( 1 ) = newAngularVelocity;
7641 idPhysics_AF::GetLinearVelocity
7644 const idVec3 &idPhysics_AF::GetLinearVelocity( int id ) const {
7645 if ( id < 0 || id >= bodies.Num() ) {
7649 return bodies[id]->current->spatialVelocity.SubVec3( 0 );
7655 idPhysics_AF::GetAngularVelocity
7658 const idVec3 &idPhysics_AF::GetAngularVelocity( int id ) const {
7659 if ( id < 0 || id >= bodies.Num() ) {
7663 return bodies[id]->current->spatialVelocity.SubVec3( 1 );
7669 idPhysics_AF::ClipTranslation
7672 void idPhysics_AF::ClipTranslation( trace_t &results, const idVec3 &translation, const idClipModel *model ) const {
7675 trace_t bodyResults;
7677 results.fraction = 1.0f;
7679 for ( i = 0; i < bodies.Num(); i++ ) {
7682 if ( body->clipModel->IsTraceModel() ) {
7684 gameLocal.clip.TranslationModel( bodyResults, body->current->worldOrigin, body->current->worldOrigin + translation,
7685 body->clipModel, body->current->worldAxis, body->clipMask,
7686 model->Handle(), model->GetOrigin(), model->GetAxis() );
7689 gameLocal.clip.Translation( bodyResults, body->current->worldOrigin, body->current->worldOrigin + translation,
7690 body->clipModel, body->current->worldAxis, body->clipMask, self );
7692 if ( bodyResults.fraction < results.fraction ) {
7693 results = bodyResults;
7698 results.endpos = bodies[0]->current->worldOrigin + results.fraction * translation;
7699 results.endAxis = bodies[0]->current->worldAxis;
7704 idPhysics_AF::ClipRotation
7707 void idPhysics_AF::ClipRotation( trace_t &results, const idRotation &rotation, const idClipModel *model ) const {
7710 trace_t bodyResults;
7711 idRotation partialRotation;
7713 results.fraction = 1.0f;
7715 for ( i = 0; i < bodies.Num(); i++ ) {
7718 if ( body->clipModel->IsTraceModel() ) {
7720 gameLocal.clip.RotationModel( bodyResults, body->current->worldOrigin, rotation,
7721 body->clipModel, body->current->worldAxis, body->clipMask,
7722 model->Handle(), model->GetOrigin(), model->GetAxis() );
7725 gameLocal.clip.Rotation( bodyResults, body->current->worldOrigin, rotation,
7726 body->clipModel, body->current->worldAxis, body->clipMask, self );
7728 if ( bodyResults.fraction < results.fraction ) {
7729 results = bodyResults;
7734 partialRotation = rotation * results.fraction;
7735 results.endpos = bodies[0]->current->worldOrigin * partialRotation;
7736 results.endAxis = bodies[0]->current->worldAxis * partialRotation.ToMat3();
7741 idPhysics_AF::ClipContents
7744 int idPhysics_AF::ClipContents( const idClipModel *model ) const {
7750 for ( i = 0; i < bodies.Num(); i++ ) {
7753 if ( body->clipModel->IsTraceModel() ) {
7755 contents |= gameLocal.clip.ContentsModel( body->current->worldOrigin,
7756 body->clipModel, body->current->worldAxis, -1,
7757 model->Handle(), model->GetOrigin(), model->GetAxis() );
7760 contents |= gameLocal.clip.Contents( body->current->worldOrigin,
7761 body->clipModel, body->current->worldAxis, -1, NULL );
7771 idPhysics_AF::DisableClip
7774 void idPhysics_AF::DisableClip( void ) {
7777 for ( i = 0; i < bodies.Num(); i++ ) {
7778 bodies[i]->clipModel->Disable();
7784 idPhysics_AF::EnableClip
7787 void idPhysics_AF::EnableClip( void ) {
7790 for ( i = 0; i < bodies.Num(); i++ ) {
7791 bodies[i]->clipModel->Enable();
7797 idPhysics_AF::UnlinkClip
7800 void idPhysics_AF::UnlinkClip( void ) {
7803 for ( i = 0; i < bodies.Num(); i++ ) {
7804 bodies[i]->clipModel->Unlink();
7810 idPhysics_AF::LinkClip
7813 void idPhysics_AF::LinkClip( void ) {
7819 idPhysics_AF::SetPushed
7822 void idPhysics_AF::SetPushed( int deltaTime ) {
7824 idRotation rotation;
7826 if ( bodies.Num() ) {
7828 rotation = ( body->saved.worldAxis.Transpose() * body->current->worldAxis ).ToRotation();
7830 // velocity with which the af is pushed
7831 current.pushVelocity.SubVec3(0) += ( body->current->worldOrigin - body->saved.worldOrigin ) / ( deltaTime * idMath::M_MS2SEC );
7832 current.pushVelocity.SubVec3(1) += rotation.GetVec() * -DEG2RAD( rotation.GetAngle() ) / ( deltaTime * idMath::M_MS2SEC );
7838 idPhysics_AF::GetPushedLinearVelocity
7841 const idVec3 &idPhysics_AF::GetPushedLinearVelocity( const int id ) const {
7842 return current.pushVelocity.SubVec3(0);
7847 idPhysics_AF::GetPushedAngularVelocity
7850 const idVec3 &idPhysics_AF::GetPushedAngularVelocity( const int id ) const {
7851 return current.pushVelocity.SubVec3(1);
7856 idPhysics_AF::SetMaster
7858 the binding is orientated based on the constraints being used
7861 void idPhysics_AF::SetMaster( idEntity *master, const bool orientated ) {
7863 idVec3 masterOrigin;
7865 idRotation rotation;
7868 self->GetMasterPosition( masterOrigin, masterAxis );
7869 if ( !masterBody ) {
7870 masterBody = new idAFBody();
7871 // translate and rotate all the constraints with body2 == NULL from world space to master space
7872 rotation = masterAxis.Transpose().ToRotation();
7873 for ( i = 0; i < constraints.Num(); i++ ) {
7874 if ( constraints[i]->GetBody2() == NULL ) {
7875 constraints[i]->Translate( -masterOrigin );
7876 constraints[i]->Rotate( rotation );
7881 masterBody->current->worldOrigin = masterOrigin;
7882 masterBody->current->worldAxis = masterAxis;
7886 // translate and rotate all the constraints with body2 == NULL from master space to world space
7887 rotation = masterBody->current->worldAxis.ToRotation();
7888 for ( i = 0; i < constraints.Num(); i++ ) {
7889 if ( constraints[i]->GetBody2() == NULL ) {
7890 constraints[i]->Rotate( rotation );
7891 constraints[i]->Translate( masterBody->current->worldOrigin );
7902 const float AF_VELOCITY_MAX = 16000;
7903 const int AF_VELOCITY_TOTAL_BITS = 16;
7904 const int AF_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( AF_VELOCITY_MAX ) ) + 1;
7905 const int AF_VELOCITY_MANTISSA_BITS = AF_VELOCITY_TOTAL_BITS - 1 - AF_VELOCITY_EXPONENT_BITS;
7906 const float AF_FORCE_MAX = 1e20f;
7907 const int AF_FORCE_TOTAL_BITS = 16;
7908 const int AF_FORCE_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( AF_FORCE_MAX ) ) + 1;
7909 const int AF_FORCE_MANTISSA_BITS = AF_FORCE_TOTAL_BITS - 1 - AF_FORCE_EXPONENT_BITS;
7913 idPhysics_AF::WriteToSnapshot
7916 void idPhysics_AF::WriteToSnapshot( idBitMsgDelta &msg ) const {
7920 msg.WriteLong( current.atRest );
7921 msg.WriteFloat( current.noMoveTime );
7922 msg.WriteFloat( current.activateTime );
7923 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[0], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7924 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[1], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7925 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[2], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7926 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[3], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7927 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[4], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7928 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[5], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7930 msg.WriteByte( bodies.Num() );
7932 for ( i = 0; i < bodies.Num(); i++ ) {
7933 AFBodyPState_t *state = bodies[i]->current;
7934 quat = state->worldAxis.ToCQuat();
7936 msg.WriteFloat( state->worldOrigin[0] );
7937 msg.WriteFloat( state->worldOrigin[1] );
7938 msg.WriteFloat( state->worldOrigin[2] );
7939 msg.WriteFloat( quat.x );
7940 msg.WriteFloat( quat.y );
7941 msg.WriteFloat( quat.z );
7942 msg.WriteDeltaFloat( 0.0f, state->spatialVelocity[0], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7943 msg.WriteDeltaFloat( 0.0f, state->spatialVelocity[1], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7944 msg.WriteDeltaFloat( 0.0f, state->spatialVelocity[2], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7945 msg.WriteDeltaFloat( 0.0f, state->spatialVelocity[3], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7946 msg.WriteDeltaFloat( 0.0f, state->spatialVelocity[4], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7947 msg.WriteDeltaFloat( 0.0f, state->spatialVelocity[5], AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7948 /* msg.WriteDeltaFloat( 0.0f, state->externalForce[0], AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7949 msg.WriteDeltaFloat( 0.0f, state->externalForce[1], AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7950 msg.WriteDeltaFloat( 0.0f, state->externalForce[2], AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7951 msg.WriteDeltaFloat( 0.0f, state->externalForce[3], AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7952 msg.WriteDeltaFloat( 0.0f, state->externalForce[4], AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7953 msg.WriteDeltaFloat( 0.0f, state->externalForce[5], AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7960 idPhysics_AF::ReadFromSnapshot
7963 void idPhysics_AF::ReadFromSnapshot( const idBitMsgDelta &msg ) {
7967 current.atRest = msg.ReadLong();
7968 current.noMoveTime = msg.ReadFloat();
7969 current.activateTime = msg.ReadFloat();
7970 current.pushVelocity[0] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7971 current.pushVelocity[1] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7972 current.pushVelocity[2] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7973 current.pushVelocity[3] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7974 current.pushVelocity[4] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7975 current.pushVelocity[5] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7977 num = msg.ReadByte();
7978 assert( num == bodies.Num() );
7980 for ( i = 0; i < bodies.Num(); i++ ) {
7981 AFBodyPState_t *state = bodies[i]->current;
7983 state->worldOrigin[0] = msg.ReadFloat();
7984 state->worldOrigin[1] = msg.ReadFloat();
7985 state->worldOrigin[2] = msg.ReadFloat();
7986 quat.x = msg.ReadFloat();
7987 quat.y = msg.ReadFloat();
7988 quat.z = msg.ReadFloat();
7989 state->spatialVelocity[0] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7990 state->spatialVelocity[1] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7991 state->spatialVelocity[2] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7992 state->spatialVelocity[3] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7993 state->spatialVelocity[4] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7994 state->spatialVelocity[5] = msg.ReadDeltaFloat( 0.0f, AF_VELOCITY_EXPONENT_BITS, AF_VELOCITY_MANTISSA_BITS );
7995 /* state->externalForce[0] = msg.ReadDeltaFloat( 0.0f, AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7996 state->externalForce[1] = msg.ReadDeltaFloat( 0.0f, AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7997 state->externalForce[2] = msg.ReadDeltaFloat( 0.0f, AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7998 state->externalForce[3] = msg.ReadDeltaFloat( 0.0f, AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
7999 state->externalForce[4] = msg.ReadDeltaFloat( 0.0f, AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
8000 state->externalForce[5] = msg.ReadDeltaFloat( 0.0f, AF_FORCE_EXPONENT_BITS, AF_FORCE_MANTISSA_BITS );
8002 state->worldAxis = quat.ToMat3();