/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . 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. 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. =========================================================================== */ #ifndef __PHYSICS_AF_H__ #define __PHYSICS_AF_H__ #include "idlib/math/Lcp.h" #include "physics/Physics_Base.h" /* =================================================================================== Articulated Figure physics Employs a constraint force based dynamic simulation using a lagrangian multiplier method to solve for the constraint forces. =================================================================================== */ class idAFConstraint; class idAFConstraint_Fixed; class idAFConstraint_BallAndSocketJoint; class idAFConstraint_BallAndSocketJointFriction; class idAFConstraint_UniversalJoint; class idAFConstraint_UniversalJointFriction; class idAFConstraint_CylindricalJoint; class idAFConstraint_Hinge; class idAFConstraint_HingeFriction; class idAFConstraint_HingeSteering; class idAFConstraint_Slider; class idAFConstraint_Line; class idAFConstraint_Plane; class idAFConstraint_Spring; class idAFConstraint_Contact; class idAFConstraint_ContactFriction; class idAFConstraint_ConeLimit; class idAFConstraint_PyramidLimit; class idAFConstraint_Suspension; class idAFBody; class idAFTree; class idPhysics_AF; typedef enum { CONSTRAINT_INVALID, CONSTRAINT_FIXED, CONSTRAINT_BALLANDSOCKETJOINT, CONSTRAINT_UNIVERSALJOINT, CONSTRAINT_HINGE, CONSTRAINT_HINGESTEERING, CONSTRAINT_SLIDER, CONSTRAINT_CYLINDRICALJOINT, CONSTRAINT_LINE, CONSTRAINT_PLANE, CONSTRAINT_SPRING, CONSTRAINT_CONTACT, CONSTRAINT_FRICTION, CONSTRAINT_CONELIMIT, CONSTRAINT_PYRAMIDLIMIT, CONSTRAINT_SUSPENSION } constraintType_t; //=============================================================== // // idAFConstraint // //=============================================================== // base class for all constraints class idAFConstraint { friend class idPhysics_AF; friend class idAFTree; public: idAFConstraint( void ); virtual ~idAFConstraint( void ); constraintType_t GetType( void ) const { return type; } const idStr & GetName( void ) const { return name; } idAFBody * GetBody1( void ) const { return body1; } idAFBody * GetBody2( void ) const { return body2; } void SetPhysics( idPhysics_AF *p ) { physics = p; } const idVecX & GetMultiplier( void ); virtual void SetBody1( idAFBody *body ); virtual void SetBody2( idAFBody *body ); virtual void DebugDraw( void ); virtual void GetForce( idAFBody *body, idVec6 &force ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: constraintType_t type; // constraint type idStr name; // name of constraint idAFBody * body1; // first constrained body idAFBody * body2; // second constrained body, NULL for world idPhysics_AF * physics; // for adding additional constraints like limits // simulation variables set by Evaluate idMatX J1, J2; // matrix with left hand side of constraint equations idVecX c1, c2; // right hand side of constraint equations idVecX lo, hi, e; // low and high bounds and lcp epsilon idAFConstraint * boxConstraint; // constraint the boxIndex refers to int boxIndex[6]; // indexes for special box constrained variables // simulation variables used during calculations idMatX invI; // transformed inertia idMatX J; // transformed constraint matrix idVecX s; // temp solution idVecX lm; // lagrange multipliers int firstIndex; // index of the first constraint row in the lcp matrix struct constraintFlags_s { bool allowPrimary : 1; // true if the constraint can be used as a primary constraint bool frameConstraint : 1; // true if this constraint is added to the frame constraints bool noCollision : 1; // true if body1 and body2 never collide with each other bool isPrimary : 1; // true if this is a primary constraint bool isZero : 1; // true if 's' is zero during calculations } fl; protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); void InitSize( int size ); }; // fixed or rigid joint which allows zero degrees of freedom // constrains body1 to have a fixed position and orientation relative to body2 class idAFConstraint_Fixed : public idAFConstraint { public: idAFConstraint_Fixed( const idStr &name, idAFBody *body1, idAFBody *body2 ); void SetRelativeOrigin( const idVec3 &origin ) { this->offset = origin; } void SetRelativeAxis( const idMat3 &axis ) { this->relAxis = axis; } virtual void SetBody1( idAFBody *body ); virtual void SetBody2( idAFBody *body ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 offset; // offset of body1 relative to body2 in body2 space idMat3 relAxis; // rotation of body1 relative to body2 protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); void InitOffset( void ); }; // ball and socket or spherical joint which allows 3 degrees of freedom // constrains body1 relative to body2 with a ball and socket joint class idAFConstraint_BallAndSocketJoint : public idAFConstraint { public: idAFConstraint_BallAndSocketJoint( const idStr &name, idAFBody *body1, idAFBody *body2 ); ~idAFConstraint_BallAndSocketJoint( void ); void SetAnchor( const idVec3 &worldPosition ); idVec3 GetAnchor( void ) const; void SetNoLimit( void ); void SetConeLimit( const idVec3 &coneAxis, const float coneAngle, const idVec3 &body1Axis ); void SetPyramidLimit( const idVec3 &pyramidAxis, const idVec3 &baseAxis, const float angle1, const float angle2, const idVec3 &body1Axis ); void SetLimitEpsilon( const float e ); void SetFriction( const float f ) { friction = f; } float GetFriction( void ) const; virtual void DebugDraw( void ); virtual void GetForce( idAFBody *body, idVec6 &force ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 anchor1; // anchor in body1 space idVec3 anchor2; // anchor in body2 space float friction; // joint friction idAFConstraint_ConeLimit *coneLimit; // cone shaped limit idAFConstraint_PyramidLimit *pyramidLimit; // pyramid shaped limit idAFConstraint_BallAndSocketJointFriction *fc; // friction constraint protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // ball and socket joint friction class idAFConstraint_BallAndSocketJointFriction : public idAFConstraint { public: idAFConstraint_BallAndSocketJointFriction( void ); void Setup( idAFConstraint_BallAndSocketJoint *cc ); bool Add( idPhysics_AF *phys, float invTimeStep ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); protected: idAFConstraint_BallAndSocketJoint *joint; protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // universal, Cardan or Hooke joint which allows 2 degrees of freedom // like a ball and socket joint but also constrains the rotation about the cardan shafts class idAFConstraint_UniversalJoint : public idAFConstraint { public: idAFConstraint_UniversalJoint( const idStr &name, idAFBody *body1, idAFBody *body2 ); ~idAFConstraint_UniversalJoint( void ); void SetAnchor( const idVec3 &worldPosition ); idVec3 GetAnchor( void ) const; void SetShafts( const idVec3 &cardanShaft1, const idVec3 &cardanShaft2 ); void GetShafts( idVec3 &cardanShaft1, idVec3 &cardanShaft2 ) { cardanShaft1 = shaft1; cardanShaft2 = shaft2; } void SetNoLimit( void ); void SetConeLimit( const idVec3 &coneAxis, const float coneAngle ); void SetPyramidLimit( const idVec3 &pyramidAxis, const idVec3 &baseAxis, const float angle1, const float angle2 ); void SetLimitEpsilon( const float e ); void SetFriction( const float f ) { friction = f; } float GetFriction( void ) const; virtual void DebugDraw( void ); virtual void GetForce( idAFBody *body, idVec6 &force ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 anchor1; // anchor in body1 space idVec3 anchor2; // anchor in body2 space idVec3 shaft1; // body1 cardan shaft in body1 space idVec3 shaft2; // body2 cardan shaft in body2 space idVec3 axis1; // cardan axis in body1 space idVec3 axis2; // cardan axis in body2 space float friction; // joint friction idAFConstraint_ConeLimit *coneLimit; // cone shaped limit idAFConstraint_PyramidLimit *pyramidLimit; // pyramid shaped limit idAFConstraint_UniversalJointFriction *fc; // friction constraint protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // universal joint friction class idAFConstraint_UniversalJointFriction : public idAFConstraint { public: idAFConstraint_UniversalJointFriction( void ); void Setup( idAFConstraint_UniversalJoint *cc ); bool Add( idPhysics_AF *phys, float invTimeStep ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); protected: idAFConstraint_UniversalJoint *joint; // universal joint protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // cylindrical joint which allows 2 degrees of freedom // constrains body1 to lie on a line relative to body2 and allows only translation along and rotation about the line class idAFConstraint_CylindricalJoint : public idAFConstraint { public: idAFConstraint_CylindricalJoint( const idStr &name, idAFBody *body1, idAFBody *body2 ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); protected: protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // hinge, revolute or pin joint which allows 1 degree of freedom // constrains all motion of body1 relative to body2 except the rotation about the hinge axis class idAFConstraint_Hinge : public idAFConstraint { public: idAFConstraint_Hinge( const idStr &name, idAFBody *body1, idAFBody *body2 ); ~idAFConstraint_Hinge( void ); void SetAnchor( const idVec3 &worldPosition ); idVec3 GetAnchor( void ) const; void SetAxis( const idVec3 &axis ); void GetAxis( idVec3 &a1, idVec3 &a2 ) const { a1 = axis1; a2 = axis2; } idVec3 GetAxis( void ) const; void SetNoLimit( void ); void SetLimit( const idVec3 &axis, const float angle, const idVec3 &body1Axis ); void SetLimitEpsilon( const float e ); float GetAngle( void ) const; void SetSteerAngle( const float degrees ); void SetSteerSpeed( const float speed ); void SetFriction( const float f ) { friction = f; } float GetFriction( void ) const; virtual void DebugDraw( void ); virtual void GetForce( idAFBody *body, idVec6 &force ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 anchor1; // anchor in body1 space idVec3 anchor2; // anchor in body2 space idVec3 axis1; // axis in body1 space idVec3 axis2; // axis in body2 space idMat3 initialAxis; // initial axis of body1 relative to body2 float friction; // hinge friction idAFConstraint_ConeLimit *coneLimit; // cone limit idAFConstraint_HingeSteering *steering; // steering idAFConstraint_HingeFriction *fc; // friction constraint protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // hinge joint friction class idAFConstraint_HingeFriction : public idAFConstraint { public: idAFConstraint_HingeFriction( void ); void Setup( idAFConstraint_Hinge *cc ); bool Add( idPhysics_AF *phys, float invTimeStep ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); protected: idAFConstraint_Hinge * hinge; // hinge protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // constrains two bodies attached to each other with a hinge to get a specified relative orientation class idAFConstraint_HingeSteering : public idAFConstraint { public: idAFConstraint_HingeSteering( void ); void Setup( idAFConstraint_Hinge *cc ); void SetSteerAngle( const float degrees ) { steerAngle = degrees; } void SetSteerSpeed( const float speed ) { steerSpeed = speed; } void SetEpsilon( const float e ) { epsilon = e; } bool Add( idPhysics_AF *phys, float invTimeStep ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idAFConstraint_Hinge * hinge; // hinge float steerAngle; // desired steer angle in degrees float steerSpeed; // steer speed float epsilon; // lcp epsilon protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // slider, prismatic or translational constraint which allows 1 degree of freedom // constrains body1 to lie on a line relative to body2, the orientation is also fixed relative to body2 class idAFConstraint_Slider : public idAFConstraint { public: idAFConstraint_Slider( const idStr &name, idAFBody *body1, idAFBody *body2 ); void SetAxis( const idVec3 &ax ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 axis; // axis along which body1 slides in body2 space idVec3 offset; // offset of body1 relative to body2 idMat3 relAxis; // rotation of body1 relative to body2 protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // line constraint which allows 4 degrees of freedom // constrains body1 to lie on a line relative to body2, does not constrain the orientation. class idAFConstraint_Line : public idAFConstraint { public: idAFConstraint_Line( const idStr &name, idAFBody *body1, idAFBody *body2 ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); protected: protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // plane constraint which allows 5 degrees of freedom // constrains body1 to lie in a plane relative to body2, does not constrain the orientation. class idAFConstraint_Plane : public idAFConstraint { public: idAFConstraint_Plane( const idStr &name, idAFBody *body1, idAFBody *body2 ); void SetPlane( const idVec3 &normal, const idVec3 &anchor ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 anchor1; // anchor in body1 space idVec3 anchor2; // anchor in body2 space idVec3 planeNormal; // plane normal in body2 space protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // spring constraint which allows 6 or 5 degrees of freedom based on the spring limits // constrains body1 relative to body2 with a spring class idAFConstraint_Spring : public idAFConstraint { public: idAFConstraint_Spring( const idStr &name, idAFBody *body1, idAFBody *body2 ); void SetAnchor( const idVec3 &worldAnchor1, const idVec3 &worldAnchor2 ); void SetSpring( const float stretch, const float compress, const float damping, const float restLength ); void SetLimit( const float minLength, const float maxLength ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 anchor1; // anchor in body1 space idVec3 anchor2; // anchor in body2 space float kstretch; // spring constant when stretched float kcompress; // spring constant when compressed float damping; // spring damping float restLength; // rest length of spring float minLength; // minimum spring length float maxLength; // maximum spring length protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // constrains body1 to either be in contact with or move away from body2 class idAFConstraint_Contact : public idAFConstraint { public: idAFConstraint_Contact( void ); ~idAFConstraint_Contact( void ); void Setup( idAFBody *b1, idAFBody *b2, contactInfo_t &c ); const contactInfo_t & GetContact( void ) const { return contact; } virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void GetCenter( idVec3 ¢er ); protected: contactInfo_t contact; // contact information idAFConstraint_ContactFriction *fc; // contact friction protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // contact friction class idAFConstraint_ContactFriction : public idAFConstraint { public: idAFConstraint_ContactFriction( void ); void Setup( idAFConstraint_Contact *cc ); bool Add( idPhysics_AF *phys, float invTimeStep ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); protected: idAFConstraint_Contact *cc; // contact constraint protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // constrains an axis attached to body1 to be inside a cone relative to body2 class idAFConstraint_ConeLimit : public idAFConstraint { public: idAFConstraint_ConeLimit( void ); void Setup( idAFBody *b1, idAFBody *b2, const idVec3 &coneAnchor, const idVec3 &coneAxis, const float coneAngle, const idVec3 &body1Axis ); void SetAnchor( const idVec3 &coneAnchor ); void SetBody1Axis( const idVec3 &body1Axis ); void SetEpsilon( const float e ) { epsilon = e; } bool Add( idPhysics_AF *phys, float invTimeStep ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 coneAnchor; // top of the cone in body2 space idVec3 coneAxis; // cone axis in body2 space idVec3 body1Axis; // axis in body1 space that should stay within the cone float cosAngle; // cos( coneAngle / 2 ) float sinHalfAngle; // sin( coneAngle / 4 ) float cosHalfAngle; // cos( coneAngle / 4 ) float epsilon; // lcp epsilon protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // constrains an axis attached to body1 to be inside a pyramid relative to body2 class idAFConstraint_PyramidLimit : public idAFConstraint { public: idAFConstraint_PyramidLimit( void ); void Setup( idAFBody *b1, idAFBody *b2, const idVec3 &pyramidAnchor, const idVec3 &pyramidAxis, const idVec3 &baseAxis, const float pyramidAngle1, const float pyramidAngle2, const idVec3 &body1Axis ); void SetAnchor( const idVec3 &pyramidAxis ); void SetBody1Axis( const idVec3 &body1Axis ); void SetEpsilon( const float e ) { epsilon = e; } bool Add( idPhysics_AF *phys, float invTimeStep ); virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); virtual void Save( idSaveGame *saveFile ) const; virtual void Restore( idRestoreGame *saveFile ); protected: idVec3 pyramidAnchor; // top of the pyramid in body2 space idMat3 pyramidBasis; // pyramid basis in body2 space with base[2] being the pyramid axis idVec3 body1Axis; // axis in body1 space that should stay within the cone float cosAngle[2]; // cos( pyramidAngle / 2 ) float sinHalfAngle[2]; // sin( pyramidAngle / 4 ) float cosHalfAngle[2]; // cos( pyramidAngle / 4 ) float epsilon; // lcp epsilon protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; // vehicle suspension class idAFConstraint_Suspension : public idAFConstraint { public: idAFConstraint_Suspension( void ); void Setup( const char *name, idAFBody *body, const idVec3 &origin, const idMat3 &axis, idClipModel *clipModel ); void SetSuspension( const float up, const float down, const float k, const float d, const float f ); void SetSteerAngle( const float degrees ) { steerAngle = degrees; } void EnableMotor( const bool enable ) { motorEnabled = enable; } void SetMotorForce( const float force ) { motorForce = force; } void SetMotorVelocity( const float vel ) { motorVelocity = vel; } void SetEpsilon( const float e ) { epsilon = e; } const idVec3 GetWheelOrigin( void ) const; virtual void DebugDraw( void ); virtual void Translate( const idVec3 &translation ); virtual void Rotate( const idRotation &rotation ); protected: idVec3 localOrigin; // position of suspension relative to body1 idMat3 localAxis; // orientation of suspension relative to body1 float suspensionUp; // suspension up movement float suspensionDown; // suspension down movement float suspensionKCompress; // spring compress constant float suspensionDamping; // spring damping float steerAngle; // desired steer angle in degrees float friction; // friction bool motorEnabled; // whether the motor is enabled or not float motorForce; // motor force float motorVelocity; // desired velocity idClipModel * wheelModel; // wheel model idVec3 wheelOffset; // wheel position relative to body1 trace_t trace; // contact point with the ground float epsilon; // lcp epsilon protected: virtual void Evaluate( float invTimeStep ); virtual void ApplyFriction( float invTimeStep ); }; //=============================================================== // // idAFBody // //=============================================================== typedef struct AFBodyPState_s { idVec3 worldOrigin; // position in world space idMat3 worldAxis; // axis at worldOrigin idVec6 spatialVelocity; // linear and rotational velocity of body idVec6 externalForce; // external force and torque applied to body } AFBodyPState_t; class idAFBody { friend class idPhysics_AF; friend class idAFTree; public: idAFBody( void ); idAFBody( const idStr &name, idClipModel *clipModel, float density ); ~idAFBody( void ); void Init( void ); const idStr & GetName( void ) const { return name; } const idVec3 & GetWorldOrigin( void ) const { return current->worldOrigin; } const idMat3 & GetWorldAxis( void ) const { return current->worldAxis; } const idVec3 & GetLinearVelocity( void ) const { return current->spatialVelocity.SubVec3(0); } const idVec3 & GetAngularVelocity( void ) const { return current->spatialVelocity.SubVec3(1); } idVec3 GetPointVelocity( const idVec3 &point ) const; const idVec3 & GetCenterOfMass( void ) const { return centerOfMass; } void SetClipModel( idClipModel *clipModel ); idClipModel * GetClipModel( void ) const { return clipModel; } void SetClipMask( const int mask ) { clipMask = mask; fl.clipMaskSet = true; } int GetClipMask( void ) const { return clipMask; } void SetSelfCollision( const bool enable ) { fl.selfCollision = enable; } void SetWorldOrigin( const idVec3 &origin ) { current->worldOrigin = origin; } void SetWorldAxis( const idMat3 &axis ) { current->worldAxis = axis; } void SetLinearVelocity( const idVec3 &linear ) const { current->spatialVelocity.SubVec3(0) = linear; } void SetAngularVelocity( const idVec3 &angular ) const { current->spatialVelocity.SubVec3(1) = angular; } void SetFriction( float linear, float angular, float contact ); float GetContactFriction( void ) const { return contactFriction; } void SetBouncyness( float bounce ); float GetBouncyness( void ) const { return bouncyness; } void SetDensity( float density, const idMat3 &inertiaScale = mat3_identity ); float GetInverseMass( void ) const { return invMass; } idMat3 GetInverseWorldInertia( void ) const { return current->worldAxis.Transpose() * inverseInertiaTensor * current->worldAxis; } void SetFrictionDirection( const idVec3 &dir ); bool GetFrictionDirection( idVec3 &dir ) const; void SetContactMotorDirection( const idVec3 &dir ); bool GetContactMotorDirection( idVec3 &dir ) const; void SetContactMotorVelocity( float vel ) { contactMotorVelocity = vel; } float GetContactMotorVelocity( void ) const { return contactMotorVelocity; } void SetContactMotorForce( float force ) { contactMotorForce = force; } float GetContactMotorForce( void ) const { return contactMotorForce; } void AddForce( const idVec3 &point, const idVec3 &force ); void InverseWorldSpatialInertiaMultiply( idVecX &dst, const float *v ) const; idVec6 & GetResponseForce( int index ) { return reinterpret_cast(response[ index * 8 ]); } void Save( idSaveGame *saveFile ); void Restore( idRestoreGame *saveFile ); private: // properties idStr name; // name of body idAFBody * parent; // parent of this body idList children; // children of this body idClipModel * clipModel; // model used for collision detection idAFConstraint * primaryConstraint; // primary constraint (this->constraint->body1 = this) idListconstraints; // all constraints attached to this body idAFTree * tree; // tree structure this body is part of float linearFriction; // translational friction float angularFriction; // rotational friction float contactFriction; // friction with contact surfaces float bouncyness; // bounce int clipMask; // contents this body collides with idVec3 frictionDir; // specifies a single direction of friction in body space idVec3 contactMotorDir; // contact motor direction float contactMotorVelocity; // contact motor velocity float contactMotorForce; // maximum force applied to reach the motor velocity // derived properties float mass; // mass of body float invMass; // inverse mass idVec3 centerOfMass; // center of mass of body idMat3 inertiaTensor; // inertia tensor idMat3 inverseInertiaTensor; // inverse inertia tensor // physics state AFBodyPState_t state[2]; AFBodyPState_t * current; // current physics state AFBodyPState_t * next; // next physics state AFBodyPState_t saved; // saved physics state idVec3 atRestOrigin; // origin at rest idMat3 atRestAxis; // axis at rest // simulation variables used during calculations idMatX inverseWorldSpatialInertia; // inverse spatial inertia in world space idMatX I, invI; // transformed inertia idMatX J; // transformed constraint matrix idVecX s; // temp solution idVecX totalForce; // total force acting on body idVecX auxForce; // force from auxiliary constraints idVecX acceleration; // acceleration float * response; // forces on body in response to auxiliary constraint forces int * responseIndex; // index to response forces int numResponses; // number of response forces int maxAuxiliaryIndex; // largest index of an auxiliary constraint constraining this body int maxSubTreeAuxiliaryIndex; // largest index of an auxiliary constraint constraining this body or one of it's children struct bodyFlags_s { bool clipMaskSet : 1; // true if this body has a clip mask set bool selfCollision : 1; // true if this body can collide with other bodies of this AF bool spatialInertiaSparse: 1; // true if the spatial inertia matrix is sparse bool useFrictionDir : 1; // true if a single friction direction should be used bool useContactMotorDir : 1; // true if a contact motor should be used bool isZero : 1; // true if 's' is zero during calculations } fl; }; //=============================================================== // // idAFTree // //=============================================================== class idAFTree { friend class idPhysics_AF; public: void Factor( void ) const; void Solve( int auxiliaryIndex = 0 ) const; void Response( const idAFConstraint *constraint, int row, int auxiliaryIndex ) const; void CalculateForces( float timeStep ) const; void SetMaxSubTreeAuxiliaryIndex( void ); void SortBodies( void ); void SortBodies_r( idList&sortedList, idAFBody *body ); void DebugDraw( const idVec4 &color ) const; private: idList sortedBodies; }; //=============================================================== // // idPhysics_AF // //=============================================================== typedef struct AFPState_s { int atRest; // >= 0 if articulated figure is at rest float noMoveTime; // time the articulated figure is hardly moving float activateTime; // time since last activation float lastTimeStep; // last time step idVec6 pushVelocity; // velocity with which the af is pushed } AFPState_t; typedef struct AFCollision_s { trace_t trace; idAFBody * body; } AFCollision_t; class idPhysics_AF : public idPhysics_Base { public: CLASS_PROTOTYPE( idPhysics_AF ); idPhysics_AF( void ); ~idPhysics_AF( void ); void Save( idSaveGame *savefile ) const; void Restore( idRestoreGame *savefile ); // initialisation int AddBody( idAFBody *body ); // returns body id void AddConstraint( idAFConstraint *constraint ); void AddFrameConstraint( idAFConstraint *constraint ); // force a body to have a certain id void ForceBodyId( idAFBody *body, int newId ); // get body or constraint id int GetBodyId( idAFBody *body ) const; int GetBodyId( const char *bodyName ) const; int GetConstraintId( idAFConstraint *constraint ) const; int GetConstraintId( const char *constraintName ) const; // number of bodies and constraints int GetNumBodies( void ) const; int GetNumConstraints( void ) const; // retrieve body or constraint idAFBody * GetBody( const char *bodyName ) const; idAFBody * GetBody( const int id ) const; idAFBody * GetMasterBody( void ) const { return masterBody; } idAFConstraint * GetConstraint( const char *constraintName ) const; idAFConstraint * GetConstraint( const int id ) const; // delete body or constraint void DeleteBody( const char *bodyName ); void DeleteBody( const int id ); void DeleteConstraint( const char *constraintName ); void DeleteConstraint( const int id ); // get all the contact constraints acting on the body int GetBodyContactConstraints( const int id, idAFConstraint_Contact *contacts[], int maxContacts ) const; // set the default friction for bodies void SetDefaultFriction( float linear, float angular, float contact ); // suspend settings void SetSuspendSpeed( const idVec2 &velocity, const idVec2 &acceleration ); // set the time and tolerances used to determine if the simulation can be suspended when the figure hardly moves for a while void SetSuspendTolerance( const float noMoveTime, const float translationTolerance, const float rotationTolerance ); // set minimum and maximum simulation time in seconds void SetSuspendTime( const float minTime, const float maxTime ); // set the time scale value void SetTimeScale( const float ts ) { timeScale = ts; } // set time scale ramp void SetTimeScaleRamp( const float start, const float end ); // set the joint friction scale void SetJointFrictionScale( const float scale ) { jointFrictionScale = scale; } // set joint friction dent void SetJointFrictionDent( const float dent, const float start, const float end ); // get the current joint friction scale float GetJointFrictionScale( void ) const; // set the contact friction scale void SetContactFrictionScale( const float scale ) { contactFrictionScale = scale; } // set contact friction dent void SetContactFrictionDent( const float dent, const float start, const float end ); // get the current contact friction scale float GetContactFrictionScale( void ) const; // enable or disable collision detection void SetCollision( const bool enable ) { enableCollision = enable; } // enable or disable self collision void SetSelfCollision( const bool enable ) { selfCollision = enable; } // enable or disable coming to a dead stop void SetComeToRest( bool enable ) { comeToRest = enable; } // call when structure of articulated figure changes void SetChanged( void ) { changedAF = true; } // enable/disable activation by impact void EnableImpact( void ); void DisableImpact( void ); // lock of unlock the world constraints void LockWorldConstraints( const bool lock ) { worldConstraintsLocked = lock; } // set force pushable void SetForcePushable( const bool enable ) { forcePushable = enable; } // update the clip model positions void UpdateClipModels( void ); public: // common physics interface void SetClipModel( idClipModel *model, float density, int id = 0, bool freeOld = true ); idClipModel * GetClipModel( int id = 0 ) const; int GetNumClipModels( void ) const; void SetMass( float mass, int id = -1 ); float GetMass( int id = -1 ) const; void SetContents( int contents, int id = -1 ); int GetContents( int id = -1 ) const; const idBounds & GetBounds( int id = -1 ) const; const idBounds & GetAbsBounds( int id = -1 ) const; bool Evaluate( int timeStepMSec, int endTimeMSec ); void UpdateTime( int endTimeMSec ); int GetTime( void ) const; void GetImpactInfo( const int id, const idVec3 &point, impactInfo_t *info ) const; void ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ); void AddForce( const int id, const idVec3 &point, const idVec3 &force ); bool IsAtRest( void ) const; int GetRestStartTime( void ) const; void Activate( void ); void PutToRest( void ); bool IsPushable( void ) const; void SaveState( void ); void RestoreState( void ); void SetOrigin( const idVec3 &newOrigin, int id = -1 ); void SetAxis( const idMat3 &newAxis, int id = -1 ); void Translate( const idVec3 &translation, int id = -1 ); void Rotate( const idRotation &rotation, int id = -1 ); const idVec3 & GetOrigin( int id = 0 ) const; const idMat3 & GetAxis( int id = 0 ) const; void SetLinearVelocity( const idVec3 &newLinearVelocity, int id = 0 ); void SetAngularVelocity( const idVec3 &newAngularVelocity, int id = 0 ); const idVec3 & GetLinearVelocity( int id = 0 ) const; const idVec3 & GetAngularVelocity( int id = 0 ) const; void ClipTranslation( trace_t &results, const idVec3 &translation, const idClipModel *model ) const; void ClipRotation( trace_t &results, const idRotation &rotation, const idClipModel *model ) const; int ClipContents( const idClipModel *model ) const; void DisableClip( void ); void EnableClip( void ); void UnlinkClip( void ); void LinkClip( void ); bool EvaluateContacts( void ); void SetPushed( int deltaTime ); const idVec3 & GetPushedLinearVelocity( const int id = 0 ) const; const idVec3 & GetPushedAngularVelocity( const int id = 0 ) const; void SetMaster( idEntity *master, const bool orientated = true ); void WriteToSnapshot( idBitMsgDelta &msg ) const; void ReadFromSnapshot( const idBitMsgDelta &msg ); private: // articulated figure idList trees; // tree structures idList bodies; // all bodies idListconstraints; // all frame independent constraints idListprimaryConstraints; // list with primary constraints idListauxiliaryConstraints; // list with auxiliary constraints idListframeConstraints; // constraints that only live one frame idListcontactConstraints; // contact constraints idList contactBodies; // body id for each contact idList collisions; // collisions bool changedAF; // true when the articulated figure just changed // properties float linearFriction; // default translational friction float angularFriction; // default rotational friction float contactFriction; // default friction with contact surfaces float bouncyness; // default bouncyness float totalMass; // total mass of articulated figure float forceTotalMass; // force this total mass idVec2 suspendVelocity; // simulation may not be suspended if a body has more velocity idVec2 suspendAcceleration; // simulation may not be suspended if a body has more acceleration float noMoveTime; // suspend simulation if hardly any movement for this many seconds float noMoveTranslation; // maximum translation considered no movement float noMoveRotation; // maximum rotation considered no movement float minMoveTime; // if > 0 the simulation is never suspended before running this many seconds float maxMoveTime; // if > 0 the simulation is always suspeded after running this many seconds float impulseThreshold; // threshold below which impulses are ignored to avoid continuous activation float timeScale; // the time is scaled with this value for slow motion effects float timeScaleRampStart; // start of time scale change float timeScaleRampEnd; // end of time scale change float jointFrictionScale; // joint friction scale float jointFrictionDent; // joint friction dives from 1 to this value and goes up again float jointFrictionDentStart; // start time of joint friction dent float jointFrictionDentEnd; // end time of joint friction dent float jointFrictionDentScale; // dent scale float contactFrictionScale; // contact friction scale float contactFrictionDent; // contact friction dives from 1 to this value and goes up again float contactFrictionDentStart; // start time of contact friction dent float contactFrictionDentEnd; // end time of contact friction dent float contactFrictionDentScale; // dent scale bool enableCollision; // if true collision detection is enabled bool selfCollision; // if true the self collision is allowed bool comeToRest; // if true the figure can come to rest bool linearTime; // if true use the linear time algorithm bool noImpact; // if true do not activate when another object collides bool worldConstraintsLocked; // if true world constraints cannot be moved bool forcePushable; // if true can be pushed even when bound to a master // physics state AFPState_t current; AFPState_t saved; idAFBody * masterBody; // master body idLCP * lcp; // linear complementarity problem solver private: void BuildTrees( void ); bool IsClosedLoop( const idAFBody *body1, const idAFBody *body2 ) const; void PrimaryFactor( void ); void EvaluateBodies( float timeStep ); void EvaluateConstraints( float timeStep ); void AddFrameConstraints( void ); void RemoveFrameConstraints( void ); void ApplyFriction( float timeStep, float endTimeMSec ); void PrimaryForces( float timeStep ); void AuxiliaryForces( float timeStep ); void VerifyContactConstraints( void ); void SetupContactConstraints( void ); void ApplyContactForces( void ); void Evolve( float timeStep ); idEntity * SetupCollisionForBody( idAFBody *body ) const; bool CollisionImpulse( float timeStep, idAFBody *body, trace_t &collision ); bool ApplyCollisions( float timeStep ); void CheckForCollisions( float timeStep ); void ClearExternalForce( void ); void AddGravity( void ); void SwapStates( void ); bool TestIfAtRest( float timeStep ); void Rest( void ); void AddPushVelocity( const idVec6 &pushVelocity ); void DebugDraw( void ); }; #endif /* !__PHYSICS_AF_H__ */