diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-09-15 11:33:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-09-15 11:33:30 -0300 |
commit | 8cab401d08f8e25aa9b2dc710204785858ff3dbb (patch) | |
tree | 1a4cec868f937fb24d340ee33fbeba2f1c6fa9f2 /servers/physics/joints | |
parent | 1a2cb755e2d8b9d59178f36702f6dff7235b9088 (diff) |
3D Physics Rework, Other Stuff
-=-=-=-=-=-=-=-=-=-=-=-=-=-
3D Physics:
-Fixed "Bounce" parameter in 3D
-Fixed bug affecting Area (sometims it would not detect properly)
-Vehicle Body has seen heavy work
-Added Query API for doing space queries in 3D. Needs some docs though.
-Added JOINTS! Adapted Bullet Joints: and created easy gizmos for setting them up:
-PinJoint
-HingeJoint (with motor)
-SliderJoint
-ConeTwistJoint
-Generic6DOFJoint
-Added OBJECT PICKING! based on the new query API. Any physics object now (Area or Body) has the following signals and virtual functions:
-input_event (mouse or multitouch input over the body)
-mouse_enter (mouse entered the body area)
-mouse_exit (mouse exited body area)
For Area it needs to be activated manually, as it isn't by default (ray goes thru).
Other:
-Begun working on Windows 8 (RT) port. Compiles but does not work yet.
-Added TheoraPlayer library for improved to-texture and portable video support.
-Fixed a few bugs in the renderer, collada importer, collada exporter, etc.
Diffstat (limited to 'servers/physics/joints')
-rw-r--r-- | servers/physics/joints/SCsub | 8 | ||||
-rw-r--r-- | servers/physics/joints/cone_twist_joint_sw.cpp | 340 | ||||
-rw-r--r-- | servers/physics/joints/cone_twist_joint_sw.h | 125 | ||||
-rw-r--r-- | servers/physics/joints/generic_6dof_joint_sw.cpp | 691 | ||||
-rw-r--r-- | servers/physics/joints/generic_6dof_joint_sw.h | 428 | ||||
-rw-r--r-- | servers/physics/joints/hinge_joint_sw.cpp | 438 | ||||
-rw-r--r-- | servers/physics/joints/hinge_joint_sw.h | 89 | ||||
-rw-r--r-- | servers/physics/joints/jacobian_entry_sw.cpp | 2 | ||||
-rw-r--r-- | servers/physics/joints/jacobian_entry_sw.h | 146 | ||||
-rw-r--r-- | servers/physics/joints/pin_joint_sw.cpp | 127 | ||||
-rw-r--r-- | servers/physics/joints/pin_joint_sw.h | 67 | ||||
-rw-r--r-- | servers/physics/joints/slider_joint_sw.cpp | 439 | ||||
-rw-r--r-- | servers/physics/joints/slider_joint_sw.h | 218 |
13 files changed, 3118 insertions, 0 deletions
diff --git a/servers/physics/joints/SCsub b/servers/physics/joints/SCsub new file mode 100644 index 0000000000..97d6edea21 --- /dev/null +++ b/servers/physics/joints/SCsub @@ -0,0 +1,8 @@ +Import('env') + +env.add_source_files(env.servers_sources,"*.cpp") + +Export('env') + + + diff --git a/servers/physics/joints/cone_twist_joint_sw.cpp b/servers/physics/joints/cone_twist_joint_sw.cpp new file mode 100644 index 0000000000..d97d8c599f --- /dev/null +++ b/servers/physics/joints/cone_twist_joint_sw.cpp @@ -0,0 +1,340 @@ +#include "cone_twist_joint_sw.h" + +static void plane_space(const Vector3& n, Vector3& p, Vector3& q) { + + if (Math::abs(n.z) > 0.707106781186547524400844362) { + // choose p in y-z plane + real_t a = n[1]*n[1] + n[2]*n[2]; + real_t k = 1.0/Math::sqrt(a); + p=Vector3(0,-n[2]*k,n[1]*k); + // set q = n x p + q=Vector3(a*k,-n[0]*p[2],n[0]*p[1]); + } + else { + // choose p in x-y plane + real_t a = n.x*n.x + n.y*n.y; + real_t k = 1.0/Math::sqrt(a); + p=Vector3(-n.y*k,n.x*k,0); + // set q = n x p + q=Vector3(-n.z*p.y,n.z*p.x,a*k); + } +} + + +static _FORCE_INLINE_ real_t atan2fast(real_t y, real_t x) +{ + real_t coeff_1 = Math_PI / 4.0f; + real_t coeff_2 = 3.0f * coeff_1; + real_t abs_y = Math::abs(y); + real_t angle; + if (x >= 0.0f) { + real_t r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } else { + real_t r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; +} + +ConeTwistJointSW::ConeTwistJointSW(BodySW* rbA,BodySW* rbB,const Transform& rbAFrame, const Transform& rbBFrame) : JointSW(_arr,2) { + + A=rbA; + B=rbB; + + + m_rbAFrame=rbAFrame; + m_rbBFrame=rbBFrame; + + m_swingSpan1 = Math_PI/4.0; + m_swingSpan2 = Math_PI/4.0; + m_twistSpan = Math_PI*2; + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + + m_solveTwistLimit = false; + m_solveSwingLimit = false; + + A->add_constraint(this,0); + B->add_constraint(this,1); + + m_appliedImpulse=0; +} + + +bool ConeTwistJointSW::setup(float p_step) { + m_appliedImpulse = real_t(0.); + + //set bias, sign, clear accumulator + m_swingCorrection = real_t(0.); + m_twistLimitSign = real_t(0.); + m_solveTwistLimit = false; + m_solveSwingLimit = false; + m_accTwistLimitImpulse = real_t(0.); + m_accSwingLimitImpulse = real_t(0.); + + if (!m_angularOnly) + { + Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin); + Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin); + Vector3 relPos = pivotBInW - pivotAInW; + + Vector3 normal[3]; + if (relPos.length_squared() > CMP_EPSILON) + { + normal[0] = relPos.normalized(); + } + else + { + normal[0]=Vector3(real_t(1.0),0,0); + } + + plane_space(normal[0], normal[1], normal[2]); + + for (int i=0;i<3;i++) + { + memnew_placement(&m_jac[i], JacobianEntrySW( + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + pivotAInW - A->get_transform().origin, + pivotBInW - B->get_transform().origin, + normal[i], + A->get_inv_inertia(), + A->get_inv_mass(), + B->get_inv_inertia(), + B->get_inv_mass())); + } + } + + Vector3 b1Axis1,b1Axis2,b1Axis3; + Vector3 b2Axis1,b2Axis2; + + b1Axis1 = A->get_transform().basis.xform( this->m_rbAFrame.basis.get_axis(0) ); + b2Axis1 = B->get_transform().basis.xform( this->m_rbBFrame.basis.get_axis(0) ); + + real_t swing1=real_t(0.),swing2 = real_t(0.); + + real_t swx=real_t(0.),swy = real_t(0.); + real_t thresh = real_t(10.); + real_t fact; + + // Get Frame into world space + if (m_swingSpan1 >= real_t(0.05f)) + { + b1Axis2 = A->get_transform().basis.xform( this->m_rbAFrame.basis.get_axis(1) ); +// swing1 = btAtan2Fast( b2Axis1.dot(b1Axis2),b2Axis1.dot(b1Axis1) ); + swx = b2Axis1.dot(b1Axis1); + swy = b2Axis1.dot(b1Axis2); + swing1 = atan2fast(swy, swx); + fact = (swy*swy + swx*swx) * thresh * thresh; + fact = fact / (fact + real_t(1.0)); + swing1 *= fact; + + } + + if (m_swingSpan2 >= real_t(0.05f)) + { + b1Axis3 = A->get_transform().basis.xform( this->m_rbAFrame.basis.get_axis(2) ); +// swing2 = btAtan2Fast( b2Axis1.dot(b1Axis3),b2Axis1.dot(b1Axis1) ); + swx = b2Axis1.dot(b1Axis1); + swy = b2Axis1.dot(b1Axis3); + swing2 = atan2fast(swy, swx); + fact = (swy*swy + swx*swx) * thresh * thresh; + fact = fact / (fact + real_t(1.0)); + swing2 *= fact; + } + + real_t RMaxAngle1Sq = 1.0f / (m_swingSpan1*m_swingSpan1); + real_t RMaxAngle2Sq = 1.0f / (m_swingSpan2*m_swingSpan2); + real_t EllipseAngle = Math::abs(swing1*swing1)* RMaxAngle1Sq + Math::abs(swing2*swing2) * RMaxAngle2Sq; + + if (EllipseAngle > 1.0f) + { + m_swingCorrection = EllipseAngle-1.0f; + m_solveSwingLimit = true; + + // Calculate necessary axis & factors + m_swingAxis = b2Axis1.cross(b1Axis2* b2Axis1.dot(b1Axis2) + b1Axis3* b2Axis1.dot(b1Axis3)); + m_swingAxis.normalize(); + + real_t swingAxisSign = (b2Axis1.dot(b1Axis1) >= 0.0f) ? 1.0f : -1.0f; + m_swingAxis *= swingAxisSign; + + m_kSwing = real_t(1.) / (A->compute_angular_impulse_denominator(m_swingAxis) + + B->compute_angular_impulse_denominator(m_swingAxis)); + + } + + // Twist limits + if (m_twistSpan >= real_t(0.)) + { + Vector3 b2Axis2 = B->get_transform().basis.xform( this->m_rbBFrame.basis.get_axis(1) ); + Quat rotationArc = Quat(b2Axis1,b1Axis1); + Vector3 TwistRef = rotationArc.xform(b2Axis2); + real_t twist = atan2fast( TwistRef.dot(b1Axis3), TwistRef.dot(b1Axis2) ); + + real_t lockedFreeFactor = (m_twistSpan > real_t(0.05f)) ? m_limitSoftness : real_t(0.); + if (twist <= -m_twistSpan*lockedFreeFactor) + { + m_twistCorrection = -(twist + m_twistSpan); + m_solveTwistLimit = true; + + m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f; + m_twistAxis.normalize(); + m_twistAxis *= -1.0f; + + m_kTwist = real_t(1.) / (A->compute_angular_impulse_denominator(m_twistAxis) + + B->compute_angular_impulse_denominator(m_twistAxis)); + + } else + if (twist > m_twistSpan*lockedFreeFactor) + { + m_twistCorrection = (twist - m_twistSpan); + m_solveTwistLimit = true; + + m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f; + m_twistAxis.normalize(); + + m_kTwist = real_t(1.) / (A->compute_angular_impulse_denominator(m_twistAxis) + + B->compute_angular_impulse_denominator(m_twistAxis)); + + } + } + + return true; +} + +void ConeTwistJointSW::solve(real_t timeStep) +{ + + Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin); + Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin); + + real_t tau = real_t(0.3); + + //linear part + if (!m_angularOnly) + { + Vector3 rel_pos1 = pivotAInW - A->get_transform().origin; + Vector3 rel_pos2 = pivotBInW - B->get_transform().origin; + + Vector3 vel1 = A->get_velocity_in_local_point(rel_pos1); + Vector3 vel2 = B->get_velocity_in_local_point(rel_pos2); + Vector3 vel = vel1 - vel2; + + for (int i=0;i<3;i++) + { + const Vector3& normal = m_jac[i].m_linearJointAxis; + real_t jacDiagABInv = real_t(1.) / m_jac[i].getDiagonal(); + + real_t rel_vel; + rel_vel = normal.dot(vel); + //positional error (zeroth order error) + real_t depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + real_t impulse = depth*tau/timeStep * jacDiagABInv - rel_vel * jacDiagABInv; + m_appliedImpulse += impulse; + Vector3 impulse_vector = normal * impulse; + A->apply_impulse(pivotAInW - A->get_transform().origin, impulse_vector); + B->apply_impulse(pivotBInW - B->get_transform().origin, -impulse_vector); + } + } + + { + ///solve angular part + const Vector3& angVelA = A->get_angular_velocity(); + const Vector3& angVelB = B->get_angular_velocity(); + + // solve swing limit + if (m_solveSwingLimit) + { + real_t amplitude = ((angVelB - angVelA).dot( m_swingAxis )*m_relaxationFactor*m_relaxationFactor + m_swingCorrection*(real_t(1.)/timeStep)*m_biasFactor); + real_t impulseMag = amplitude * m_kSwing; + + // Clamp the accumulated impulse + real_t temp = m_accSwingLimitImpulse; + m_accSwingLimitImpulse = MAX(m_accSwingLimitImpulse + impulseMag, real_t(0.0) ); + impulseMag = m_accSwingLimitImpulse - temp; + + Vector3 impulse = m_swingAxis * impulseMag; + + A->apply_torque_impulse(impulse); + B->apply_torque_impulse(-impulse); + + } + + // solve twist limit + if (m_solveTwistLimit) + { + real_t amplitude = ((angVelB - angVelA).dot( m_twistAxis )*m_relaxationFactor*m_relaxationFactor + m_twistCorrection*(real_t(1.)/timeStep)*m_biasFactor ); + real_t impulseMag = amplitude * m_kTwist; + + // Clamp the accumulated impulse + real_t temp = m_accTwistLimitImpulse; + m_accTwistLimitImpulse = MAX(m_accTwistLimitImpulse + impulseMag, real_t(0.0) ); + impulseMag = m_accTwistLimitImpulse - temp; + + Vector3 impulse = m_twistAxis * impulseMag; + + A->apply_torque_impulse(impulse); + B->apply_torque_impulse(-impulse); + + } + + } + +} + +void ConeTwistJointSW::set_param(PhysicsServer::ConeTwistJointParam p_param, float p_value) { + + switch(p_param) { + case PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN: { + + m_swingSpan1=p_value; + m_swingSpan2=p_value; + } break; + case PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN: { + + m_twistSpan=p_value; + } break; + case PhysicsServer::CONE_TWIST_JOINT_BIAS: { + + m_biasFactor=p_value; + } break; + case PhysicsServer::CONE_TWIST_JOINT_SOFTNESS: { + + m_limitSoftness=p_value; + } break; + case PhysicsServer::CONE_TWIST_JOINT_RELAXATION: { + + m_relaxationFactor=p_value; + } break; + } +} + +float ConeTwistJointSW::get_param(PhysicsServer::ConeTwistJointParam p_param) const{ + + switch(p_param) { + case PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN: { + + return m_swingSpan1; + } break; + case PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN: { + + return m_twistSpan; + } break; + case PhysicsServer::CONE_TWIST_JOINT_BIAS: { + + return m_biasFactor; + } break; + case PhysicsServer::CONE_TWIST_JOINT_SOFTNESS: { + + return m_limitSoftness; + } break; + case PhysicsServer::CONE_TWIST_JOINT_RELAXATION: { + + return m_relaxationFactor; + } break; + } + + return 0; +} diff --git a/servers/physics/joints/cone_twist_joint_sw.h b/servers/physics/joints/cone_twist_joint_sw.h new file mode 100644 index 0000000000..63502d2036 --- /dev/null +++ b/servers/physics/joints/cone_twist_joint_sw.h @@ -0,0 +1,125 @@ +#ifndef CONE_TWIST_JOINT_SW_H +#define CONE_TWIST_JOINT_SW_H + +#include "servers/physics/joints_sw.h" +#include "servers/physics/joints/jacobian_entry_sw.h" + + +/* +Bullet Continuous Collision Detection and Physics Library +ConeTwistJointSW is Copyright (c) 2007 Starbreeze Studios + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Written by: Marcus Hennix +*/ + + + +///ConeTwistJointSW can be used to simulate ragdoll joints (upper arm, leg etc) +class ConeTwistJointSW : public JointSW +{ +#ifdef IN_PARALLELL_SOLVER +public: +#endif + + union { + struct { + BodySW *A; + BodySW *B; + }; + + BodySW *_arr[2]; + }; + + JacobianEntrySW m_jac[3]; //3 orthogonal linear constraints + + + real_t m_appliedImpulse; + Transform m_rbAFrame; + Transform m_rbBFrame; + + real_t m_limitSoftness; + real_t m_biasFactor; + real_t m_relaxationFactor; + + real_t m_swingSpan1; + real_t m_swingSpan2; + real_t m_twistSpan; + + Vector3 m_swingAxis; + Vector3 m_twistAxis; + + real_t m_kSwing; + real_t m_kTwist; + + real_t m_twistLimitSign; + real_t m_swingCorrection; + real_t m_twistCorrection; + + real_t m_accSwingLimitImpulse; + real_t m_accTwistLimitImpulse; + + bool m_angularOnly; + bool m_solveTwistLimit; + bool m_solveSwingLimit; + + +public: + + virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_CONE_TWIST; } + + virtual bool setup(float p_step); + virtual void solve(float p_step); + + ConeTwistJointSW(BodySW* rbA,BodySW* rbB,const Transform& rbAFrame, const Transform& rbBFrame); + + + void setAngularOnly(bool angularOnly) + { + m_angularOnly = angularOnly; + } + + void setLimit(real_t _swingSpan1,real_t _swingSpan2,real_t _twistSpan, real_t _softness = 0.8f, real_t _biasFactor = 0.3f, real_t _relaxationFactor = 1.0f) + { + m_swingSpan1 = _swingSpan1; + m_swingSpan2 = _swingSpan2; + m_twistSpan = _twistSpan; + + m_limitSoftness = _softness; + m_biasFactor = _biasFactor; + m_relaxationFactor = _relaxationFactor; + } + + inline int getSolveTwistLimit() + { + return m_solveTwistLimit; + } + + inline int getSolveSwingLimit() + { + return m_solveTwistLimit; + } + + inline real_t getTwistLimitSign() + { + return m_twistLimitSign; + } + + void set_param(PhysicsServer::ConeTwistJointParam p_param, float p_value); + float get_param(PhysicsServer::ConeTwistJointParam p_param) const; + + +}; + + + +#endif // CONE_TWIST_JOINT_SW_H diff --git a/servers/physics/joints/generic_6dof_joint_sw.cpp b/servers/physics/joints/generic_6dof_joint_sw.cpp new file mode 100644 index 0000000000..3d569df2c9 --- /dev/null +++ b/servers/physics/joints/generic_6dof_joint_sw.cpp @@ -0,0 +1,691 @@ +#include "generic_6dof_joint_sw.h" + + + +#define GENERIC_D6_DISABLE_WARMSTARTING 1 + +real_t btGetMatrixElem(const Matrix3& mat, int index); +real_t btGetMatrixElem(const Matrix3& mat, int index) +{ + int i = index%3; + int j = index/3; + return mat[i][j]; +} + +///MatrixToEulerXYZ from http://www.geometrictools.com/LibFoundation/Mathematics/Wm4Matrix3.inl.html +bool matrixToEulerXYZ(const Matrix3& mat,Vector3& xyz); +bool matrixToEulerXYZ(const Matrix3& mat,Vector3& xyz) +{ +// // rot = cy*cz -cy*sz sy +// // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx +// // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy +// + + if (btGetMatrixElem(mat,2) < real_t(1.0)) + { + if (btGetMatrixElem(mat,2) > real_t(-1.0)) + { + xyz[0] = Math::atan2(-btGetMatrixElem(mat,5),btGetMatrixElem(mat,8)); + xyz[1] = Math::asin(btGetMatrixElem(mat,2)); + xyz[2] = Math::atan2(-btGetMatrixElem(mat,1),btGetMatrixElem(mat,0)); + return true; + } + else + { + // WARNING. Not unique. XA - ZA = -atan2(r10,r11) + xyz[0] = -Math::atan2(btGetMatrixElem(mat,3),btGetMatrixElem(mat,4)); + xyz[1] = -Math_PI*0.5; + xyz[2] = real_t(0.0); + return false; + } + } + else + { + // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11) + xyz[0] = Math::atan2(btGetMatrixElem(mat,3),btGetMatrixElem(mat,4)); + xyz[1] = Math_PI*0.5; + xyz[2] = 0.0; + + } + + + return false; +} + + + +//////////////////////////// G6DOFRotationalLimitMotorSW //////////////////////////////////// + + +int G6DOFRotationalLimitMotorSW::testLimitValue(real_t test_value) +{ + if(m_loLimit>m_hiLimit) + { + m_currentLimit = 0;//Free from violation + return 0; + } + + if (test_value < m_loLimit) + { + m_currentLimit = 1;//low limit violation + m_currentLimitError = test_value - m_loLimit; + return 1; + } + else if (test_value> m_hiLimit) + { + m_currentLimit = 2;//High limit violation + m_currentLimitError = test_value - m_hiLimit; + return 2; + }; + + m_currentLimit = 0;//Free from violation + return 0; + +} + + +real_t G6DOFRotationalLimitMotorSW::solveAngularLimits( + real_t timeStep,Vector3& axis,real_t jacDiagABInv, + BodySW * body0, BodySW * body1) +{ + if (needApplyTorques()==false) return 0.0f; + + real_t target_velocity = m_targetVelocity; + real_t maxMotorForce = m_maxMotorForce; + + //current error correction + if (m_currentLimit!=0) + { + target_velocity = -m_ERP*m_currentLimitError/(timeStep); + maxMotorForce = m_maxLimitForce; + } + + maxMotorForce *= timeStep; + + // current velocity difference + Vector3 vel_diff = body0->get_angular_velocity(); + if (body1) + { + vel_diff -= body1->get_angular_velocity(); + } + + + + real_t rel_vel = axis.dot(vel_diff); + + // correction velocity + real_t motor_relvel = m_limitSoftness*(target_velocity - m_damping*rel_vel); + + + if ( motor_relvel < CMP_EPSILON && motor_relvel > -CMP_EPSILON ) + { + return 0.0f;//no need for applying force + } + + + // correction impulse + real_t unclippedMotorImpulse = (1+m_bounce)*motor_relvel*jacDiagABInv; + + // clip correction impulse + real_t clippedMotorImpulse; + + ///@todo: should clip against accumulated impulse + if (unclippedMotorImpulse>0.0f) + { + clippedMotorImpulse = unclippedMotorImpulse > maxMotorForce? maxMotorForce: unclippedMotorImpulse; + } + else + { + clippedMotorImpulse = unclippedMotorImpulse < -maxMotorForce ? -maxMotorForce: unclippedMotorImpulse; + } + + + // sort with accumulated impulses + real_t lo = real_t(-1e30); + real_t hi = real_t(1e30); + + real_t oldaccumImpulse = m_accumulatedImpulse; + real_t sum = oldaccumImpulse + clippedMotorImpulse; + m_accumulatedImpulse = sum > hi ? real_t(0.) : sum < lo ? real_t(0.) : sum; + + clippedMotorImpulse = m_accumulatedImpulse - oldaccumImpulse; + + + + Vector3 motorImp = clippedMotorImpulse * axis; + + + body0->apply_torque_impulse(motorImp); + if (body1) body1->apply_torque_impulse(-motorImp); + + return clippedMotorImpulse; + + +} + +//////////////////////////// End G6DOFRotationalLimitMotorSW //////////////////////////////////// + +//////////////////////////// G6DOFTranslationalLimitMotorSW //////////////////////////////////// +real_t G6DOFTranslationalLimitMotorSW::solveLinearAxis( + real_t timeStep, + real_t jacDiagABInv, + BodySW* body1,const Vector3 &pointInA, + BodySW* body2,const Vector3 &pointInB, + int limit_index, + const Vector3 & axis_normal_on_a, + const Vector3 & anchorPos) +{ + +///find relative velocity +// Vector3 rel_pos1 = pointInA - body1->get_transform().origin; +// Vector3 rel_pos2 = pointInB - body2->get_transform().origin; + Vector3 rel_pos1 = anchorPos - body1->get_transform().origin; + Vector3 rel_pos2 = anchorPos - body2->get_transform().origin; + + Vector3 vel1 = body1->get_velocity_in_local_point(rel_pos1); + Vector3 vel2 = body2->get_velocity_in_local_point(rel_pos2); + Vector3 vel = vel1 - vel2; + + real_t rel_vel = axis_normal_on_a.dot(vel); + + + +/// apply displacement correction + +//positional error (zeroth order error) + real_t depth = -(pointInA - pointInB).dot(axis_normal_on_a); + real_t lo = real_t(-1e30); + real_t hi = real_t(1e30); + + real_t minLimit = m_lowerLimit[limit_index]; + real_t maxLimit = m_upperLimit[limit_index]; + + //handle the limits + if (minLimit < maxLimit) + { + { + if (depth > maxLimit) + { + depth -= maxLimit; + lo = real_t(0.); + + } + else + { + if (depth < minLimit) + { + depth -= minLimit; + hi = real_t(0.); + } + else + { + return 0.0f; + } + } + } + } + + real_t normalImpulse= m_limitSoftness[limit_index]*(m_restitution[limit_index]*depth/timeStep - m_damping[limit_index]*rel_vel) * jacDiagABInv; + + + + + real_t oldNormalImpulse = m_accumulatedImpulse[limit_index]; + real_t sum = oldNormalImpulse + normalImpulse; + m_accumulatedImpulse[limit_index] = sum > hi ? real_t(0.) : sum < lo ? real_t(0.) : sum; + normalImpulse = m_accumulatedImpulse[limit_index] - oldNormalImpulse; + + Vector3 impulse_vector = axis_normal_on_a * normalImpulse; + body1->apply_impulse( rel_pos1, impulse_vector); + body2->apply_impulse( rel_pos2, -impulse_vector); + return normalImpulse; +} + +//////////////////////////// G6DOFTranslationalLimitMotorSW //////////////////////////////////// + + +Generic6DOFJointSW::Generic6DOFJointSW(BodySW* rbA, BodySW* rbB, const Transform& frameInA, const Transform& frameInB, bool useLinearReferenceFrameA) + : JointSW(_arr,2) + , m_frameInA(frameInA) + , m_frameInB(frameInB), + m_useLinearReferenceFrameA(useLinearReferenceFrameA) +{ + A=rbA; + B=rbB; + A->add_constraint(this,0); + B->add_constraint(this,1); +} + + + + + +void Generic6DOFJointSW::calculateAngleInfo() +{ + Matrix3 relative_frame = m_calculatedTransformA.basis.inverse()*m_calculatedTransformB.basis; + + matrixToEulerXYZ(relative_frame,m_calculatedAxisAngleDiff); + + + + // in euler angle mode we do not actually constrain the angular velocity + // along the axes axis[0] and axis[2] (although we do use axis[1]) : + // + // to get constrain w2-w1 along ...not + // ------ --------------------- ------ + // d(angle[0])/dt = 0 ax[1] x ax[2] ax[0] + // d(angle[1])/dt = 0 ax[1] + // d(angle[2])/dt = 0 ax[0] x ax[1] ax[2] + // + // constraining w2-w1 along an axis 'a' means that a'*(w2-w1)=0. + // to prove the result for angle[0], write the expression for angle[0] from + // GetInfo1 then take the derivative. to prove this for angle[2] it is + // easier to take the euler rate expression for d(angle[2])/dt with respect + // to the components of w and set that to 0. + + Vector3 axis0 = m_calculatedTransformB.basis.get_axis(0); + Vector3 axis2 = m_calculatedTransformA.basis.get_axis(2); + + m_calculatedAxis[1] = axis2.cross(axis0); + m_calculatedAxis[0] = m_calculatedAxis[1].cross(axis2); + m_calculatedAxis[2] = axis0.cross(m_calculatedAxis[1]); + + +// if(m_debugDrawer) +// { +// +// char buff[300]; +// sprintf(buff,"\n X: %.2f ; Y: %.2f ; Z: %.2f ", +// m_calculatedAxisAngleDiff[0], +// m_calculatedAxisAngleDiff[1], +// m_calculatedAxisAngleDiff[2]); +// m_debugDrawer->reportErrorWarning(buff); +// } + +} + +void Generic6DOFJointSW::calculateTransforms() +{ + m_calculatedTransformA = A->get_transform() * m_frameInA; + m_calculatedTransformB = B->get_transform() * m_frameInB; + + calculateAngleInfo(); +} + + +void Generic6DOFJointSW::buildLinearJacobian( + JacobianEntrySW & jacLinear,const Vector3 & normalWorld, + const Vector3 & pivotAInW,const Vector3 & pivotBInW) +{ + memnew_placement(&jacLinear, JacobianEntrySW( + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + pivotAInW - A->get_transform().origin, + pivotBInW - B->get_transform().origin, + normalWorld, + A->get_inv_inertia(), + A->get_inv_mass(), + B->get_inv_inertia(), + B->get_inv_mass())); + +} + +void Generic6DOFJointSW::buildAngularJacobian( + JacobianEntrySW & jacAngular,const Vector3 & jointAxisW) +{ + memnew_placement(&jacAngular, JacobianEntrySW(jointAxisW, + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + A->get_inv_inertia(), + B->get_inv_inertia())); + +} + +bool Generic6DOFJointSW::testAngularLimitMotor(int axis_index) +{ + real_t angle = m_calculatedAxisAngleDiff[axis_index]; + + //test limits + m_angularLimits[axis_index].testLimitValue(angle); + return m_angularLimits[axis_index].needApplyTorques(); +} + +bool Generic6DOFJointSW::setup(float p_step) { + + // Clear accumulated impulses for the next simulation step + m_linearLimits.m_accumulatedImpulse=Vector3(real_t(0.), real_t(0.), real_t(0.)); + int i; + for(i = 0; i < 3; i++) + { + m_angularLimits[i].m_accumulatedImpulse = real_t(0.); + } + //calculates transform + calculateTransforms(); + +// const Vector3& pivotAInW = m_calculatedTransformA.origin; +// const Vector3& pivotBInW = m_calculatedTransformB.origin; + calcAnchorPos(); + Vector3 pivotAInW = m_AnchorPos; + Vector3 pivotBInW = m_AnchorPos; + +// not used here +// Vector3 rel_pos1 = pivotAInW - A->get_transform().origin; +// Vector3 rel_pos2 = pivotBInW - B->get_transform().origin; + + Vector3 normalWorld; + //linear part + for (i=0;i<3;i++) + { + if (m_linearLimits.enable_limit[i] && m_linearLimits.isLimited(i)) + { + if (m_useLinearReferenceFrameA) + normalWorld = m_calculatedTransformA.basis.get_axis(i); + else + normalWorld = m_calculatedTransformB.basis.get_axis(i); + + buildLinearJacobian( + m_jacLinear[i],normalWorld , + pivotAInW,pivotBInW); + + } + } + + // angular part + for (i=0;i<3;i++) + { + //calculates error angle + if (m_angularLimits[i].m_enableLimit && testAngularLimitMotor(i)) + { + normalWorld = this->getAxis(i); + // Create angular atom + buildAngularJacobian(m_jacAng[i],normalWorld); + } + } + + return true; +} + + +void Generic6DOFJointSW::solve(real_t timeStep) +{ + m_timeStep = timeStep; + + //calculateTransforms(); + + int i; + + // linear + + Vector3 pointInA = m_calculatedTransformA.origin; + Vector3 pointInB = m_calculatedTransformB.origin; + + real_t jacDiagABInv; + Vector3 linear_axis; + for (i=0;i<3;i++) + { + if (m_linearLimits.enable_limit[i] && m_linearLimits.isLimited(i)) + { + jacDiagABInv = real_t(1.) / m_jacLinear[i].getDiagonal(); + + if (m_useLinearReferenceFrameA) + linear_axis = m_calculatedTransformA.basis.get_axis(i); + else + linear_axis = m_calculatedTransformB.basis.get_axis(i); + + m_linearLimits.solveLinearAxis( + m_timeStep, + jacDiagABInv, + A,pointInA, + B,pointInB, + i,linear_axis, m_AnchorPos); + + } + } + + // angular + Vector3 angular_axis; + real_t angularJacDiagABInv; + for (i=0;i<3;i++) + { + if (m_angularLimits[i].m_enableLimit && m_angularLimits[i].needApplyTorques()) + { + + // get axis + angular_axis = getAxis(i); + + angularJacDiagABInv = real_t(1.) / m_jacAng[i].getDiagonal(); + + m_angularLimits[i].solveAngularLimits(m_timeStep,angular_axis,angularJacDiagABInv, A,B); + } + } +} + +void Generic6DOFJointSW::updateRHS(real_t timeStep) +{ + (void)timeStep; + +} + +Vector3 Generic6DOFJointSW::getAxis(int axis_index) const +{ + return m_calculatedAxis[axis_index]; +} + +real_t Generic6DOFJointSW::getAngle(int axis_index) const +{ + return m_calculatedAxisAngleDiff[axis_index]; +} + +void Generic6DOFJointSW::calcAnchorPos(void) +{ + real_t imA = A->get_inv_mass(); + real_t imB = B->get_inv_mass(); + real_t weight; + if(imB == real_t(0.0)) + { + weight = real_t(1.0); + } + else + { + weight = imA / (imA + imB); + } + const Vector3& pA = m_calculatedTransformA.origin; + const Vector3& pB = m_calculatedTransformB.origin; + m_AnchorPos = pA * weight + pB * (real_t(1.0) - weight); + return; +} // Generic6DOFJointSW::calcAnchorPos() + + +void Generic6DOFJointSW::set_param(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisParam p_param, float p_value) { + + ERR_FAIL_INDEX(p_axis,3); + switch(p_param) { + case PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT: { + + m_linearLimits.m_lowerLimit[p_axis]=p_value; + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT: { + + m_linearLimits.m_upperLimit[p_axis]=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS: { + + m_linearLimits.m_limitSoftness[p_axis]=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION: { + + m_linearLimits.m_restitution[p_axis]=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING: { + + m_linearLimits.m_damping[p_axis]=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: { + + m_angularLimits[p_axis].m_loLimit=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT: { + + m_angularLimits[p_axis].m_hiLimit=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS: { + + m_angularLimits[p_axis].m_limitSoftness; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING: { + + m_angularLimits[p_axis].m_damping=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION: { + + m_angularLimits[p_axis].m_bounce=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_FORCE_LIMIT: { + + m_angularLimits[p_axis].m_maxLimitForce=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP: { + + m_angularLimits[p_axis].m_ERP=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY: { + + m_angularLimits[p_axis].m_targetVelocity=p_value; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT: { + + m_angularLimits[p_axis].m_maxLimitForce=p_value; + + } break; + } +} + +float Generic6DOFJointSW::get_param(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisParam p_param) const{ + ERR_FAIL_INDEX_V(p_axis,3,0); + switch(p_param) { + case PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT: { + + return m_linearLimits.m_lowerLimit[p_axis]; + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT: { + + return m_linearLimits.m_upperLimit[p_axis]; + + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS: { + + return m_linearLimits.m_limitSoftness[p_axis]; + + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION: { + + return m_linearLimits.m_restitution[p_axis]; + + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING: { + + return m_linearLimits.m_damping[p_axis]; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: { + + return m_angularLimits[p_axis].m_loLimit; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT: { + + return m_angularLimits[p_axis].m_hiLimit; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS: { + + return m_angularLimits[p_axis].m_limitSoftness; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING: { + + return m_angularLimits[p_axis].m_damping; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION: { + + return m_angularLimits[p_axis].m_bounce; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_FORCE_LIMIT: { + + return m_angularLimits[p_axis].m_maxLimitForce; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP: { + + return m_angularLimits[p_axis].m_ERP; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY: { + + return m_angularLimits[p_axis].m_targetVelocity; + + } break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT: { + + return m_angularLimits[p_axis].m_maxLimitForce; + + } break; + } + return 0; +} + +void Generic6DOFJointSW::set_flag(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisFlag p_flag, bool p_value){ + + ERR_FAIL_INDEX(p_axis,3); + + switch(p_flag) { + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT: { + + m_linearLimits.enable_limit[p_axis]=p_value; + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT: { + + m_angularLimits[p_axis].m_enableLimit=p_value; + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR: { + + m_angularLimits[p_axis].m_enableMotor=p_value; + } break; + } + + +} +bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisFlag p_flag) const{ + + ERR_FAIL_INDEX_V(p_axis,3,0); + switch(p_flag) { + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT: { + + return m_linearLimits.enable_limit[p_axis]; + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT: { + + return m_angularLimits[p_axis].m_enableLimit; + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR: { + + return m_angularLimits[p_axis].m_enableMotor; + } break; + } + + return 0; +} diff --git a/servers/physics/joints/generic_6dof_joint_sw.h b/servers/physics/joints/generic_6dof_joint_sw.h new file mode 100644 index 0000000000..7f762e51a2 --- /dev/null +++ b/servers/physics/joints/generic_6dof_joint_sw.h @@ -0,0 +1,428 @@ +#ifndef GENERIC_6DOF_JOINT_SW_H +#define GENERIC_6DOF_JOINT_SW_H + +#include "servers/physics/joints_sw.h" +#include "servers/physics/joints/jacobian_entry_sw.h" + + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +/* +2007-09-09 +Generic6DOFJointSW Refactored by Francisco Le?n +email: projectileman@yahoo.com +http://gimpact.sf.net +*/ + + +//! Rotation Limit structure for generic joints +class G6DOFRotationalLimitMotorSW { +public: + //! limit_parameters + //!@{ + real_t m_loLimit;//!< joint limit + real_t m_hiLimit;//!< joint limit + real_t m_targetVelocity;//!< target motor velocity + real_t m_maxMotorForce;//!< max force on motor + real_t m_maxLimitForce;//!< max force on limit + real_t m_damping;//!< Damping. + real_t m_limitSoftness;//! Relaxation factor + real_t m_ERP;//!< Error tolerance factor when joint is at limit + real_t m_bounce;//!< restitution factor + bool m_enableMotor; + bool m_enableLimit; + + //!@} + + //! temp_variables + //!@{ + real_t m_currentLimitError;//! How much is violated this limit + int m_currentLimit;//!< 0=free, 1=at lo limit, 2=at hi limit + real_t m_accumulatedImpulse; + //!@} + + G6DOFRotationalLimitMotorSW() + { + m_accumulatedImpulse = 0.f; + m_targetVelocity = 0; + m_maxMotorForce = 0.1f; + m_maxLimitForce = 300.0f; + m_loLimit = -1e30; + m_hiLimit = 1e30; + m_ERP = 0.5f; + m_bounce = 0.0f; + m_damping = 1.0f; + m_limitSoftness = 0.5f; + m_currentLimit = 0; + m_currentLimitError = 0; + m_enableMotor = false; + m_enableLimit=false; + } + + G6DOFRotationalLimitMotorSW(const G6DOFRotationalLimitMotorSW & limot) + { + m_targetVelocity = limot.m_targetVelocity; + m_maxMotorForce = limot.m_maxMotorForce; + m_limitSoftness = limot.m_limitSoftness; + m_loLimit = limot.m_loLimit; + m_hiLimit = limot.m_hiLimit; + m_ERP = limot.m_ERP; + m_bounce = limot.m_bounce; + m_currentLimit = limot.m_currentLimit; + m_currentLimitError = limot.m_currentLimitError; + m_enableMotor = limot.m_enableMotor; + } + + + + //! Is limited + bool isLimited() + { + if(m_loLimit>=m_hiLimit) return false; + return true; + } + + //! Need apply correction + bool needApplyTorques() + { + if(m_currentLimit == 0 && m_enableMotor == false) return false; + return true; + } + + //! calculates error + /*! + calculates m_currentLimit and m_currentLimitError. + */ + int testLimitValue(real_t test_value); + + //! apply the correction impulses for two bodies + real_t solveAngularLimits(real_t timeStep,Vector3& axis, real_t jacDiagABInv,BodySW * body0, BodySW * body1); + + +}; + + + +class G6DOFTranslationalLimitMotorSW +{ +public: + Vector3 m_lowerLimit;//!< the constraint lower limits + Vector3 m_upperLimit;//!< the constraint upper limits + Vector3 m_accumulatedImpulse; + //! Linear_Limit_parameters + //!@{ + Vector3 m_limitSoftness;//!< Softness for linear limit + Vector3 m_damping;//!< Damping for linear limit + Vector3 m_restitution;//! Bounce parameter for linear limit + //!@} + bool enable_limit[3]; + + G6DOFTranslationalLimitMotorSW() + { + m_lowerLimit=Vector3(0.f,0.f,0.f); + m_upperLimit=Vector3(0.f,0.f,0.f); + m_accumulatedImpulse=Vector3(0.f,0.f,0.f); + + m_limitSoftness = Vector3(1,1,1)*0.7f; + m_damping = Vector3(1,1,1)*real_t(1.0f); + m_restitution = Vector3(1,1,1)*real_t(0.5f); + + enable_limit[0]=true; + enable_limit[1]=true; + enable_limit[2]=true; + } + + G6DOFTranslationalLimitMotorSW(const G6DOFTranslationalLimitMotorSW & other ) + { + m_lowerLimit = other.m_lowerLimit; + m_upperLimit = other.m_upperLimit; + m_accumulatedImpulse = other.m_accumulatedImpulse; + + m_limitSoftness = other.m_limitSoftness ; + m_damping = other.m_damping; + m_restitution = other.m_restitution; + } + + //! Test limit + /*! + - free means upper < lower, + - locked means upper == lower + - limited means upper > lower + - limitIndex: first 3 are linear, next 3 are angular + */ + inline bool isLimited(int limitIndex) + { + return (m_upperLimit[limitIndex] >= m_lowerLimit[limitIndex]); + } + + + real_t solveLinearAxis( + real_t timeStep, + real_t jacDiagABInv, + BodySW* body1,const Vector3 &pointInA, + BodySW* body2,const Vector3 &pointInB, + int limit_index, + const Vector3 & axis_normal_on_a, + const Vector3 & anchorPos); + + +}; + + +class Generic6DOFJointSW : public JointSW +{ +protected: + + + union { + struct { + BodySW *A; + BodySW *B; + }; + + BodySW *_arr[2]; + }; + + //! relative_frames + //!@{ + Transform m_frameInA;//!< the constraint space w.r.t body A + Transform m_frameInB;//!< the constraint space w.r.t body B + //!@} + + //! Jacobians + //!@{ + JacobianEntrySW m_jacLinear[3];//!< 3 orthogonal linear constraints + JacobianEntrySW m_jacAng[3];//!< 3 orthogonal angular constraints + //!@} + + //! Linear_Limit_parameters + //!@{ + G6DOFTranslationalLimitMotorSW m_linearLimits; + //!@} + + + //! hinge_parameters + //!@{ + G6DOFRotationalLimitMotorSW m_angularLimits[3]; + //!@} + + +protected: + //! temporal variables + //!@{ + real_t m_timeStep; + Transform m_calculatedTransformA; + Transform m_calculatedTransformB; + Vector3 m_calculatedAxisAngleDiff; + Vector3 m_calculatedAxis[3]; + + Vector3 m_AnchorPos; // point betwen pivots of bodies A and B to solve linear axes + + bool m_useLinearReferenceFrameA; + + //!@} + + Generic6DOFJointSW& operator=(Generic6DOFJointSW& other) + { + ERR_PRINT("pito"); + (void) other; + return *this; + } + + + + void buildLinearJacobian( + JacobianEntrySW & jacLinear,const Vector3 & normalWorld, + const Vector3 & pivotAInW,const Vector3 & pivotBInW); + + void buildAngularJacobian(JacobianEntrySW & jacAngular,const Vector3 & jointAxisW); + + + //! calcs the euler angles between the two bodies. + void calculateAngleInfo(); + + + +public: + Generic6DOFJointSW(BodySW* rbA, BodySW* rbB, const Transform& frameInA, const Transform& frameInB ,bool useLinearReferenceFrameA); + + virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_6DOF; } + + virtual bool setup(float p_step); + virtual void solve(float p_step); + + + //! Calcs global transform of the offsets + /*! + Calcs the global transform for the joint offset for body A an B, and also calcs the agle differences between the bodies. + \sa Generic6DOFJointSW.getCalculatedTransformA , Generic6DOFJointSW.getCalculatedTransformB, Generic6DOFJointSW.calculateAngleInfo + */ + void calculateTransforms(); + + //! Gets the global transform of the offset for body A + /*! + \sa Generic6DOFJointSW.getFrameOffsetA, Generic6DOFJointSW.getFrameOffsetB, Generic6DOFJointSW.calculateAngleInfo. + */ + const Transform & getCalculatedTransformA() const + { + return m_calculatedTransformA; + } + + //! Gets the global transform of the offset for body B + /*! + \sa Generic6DOFJointSW.getFrameOffsetA, Generic6DOFJointSW.getFrameOffsetB, Generic6DOFJointSW.calculateAngleInfo. + */ + const Transform & getCalculatedTransformB() const + { + return m_calculatedTransformB; + } + + const Transform & getFrameOffsetA() const + { + return m_frameInA; + } + + const Transform & getFrameOffsetB() const + { + return m_frameInB; + } + + + Transform & getFrameOffsetA() + { + return m_frameInA; + } + + Transform & getFrameOffsetB() + { + return m_frameInB; + } + + + //! performs Jacobian calculation, and also calculates angle differences and axis + + + void updateRHS(real_t timeStep); + + //! Get the rotation axis in global coordinates + /*! + \pre Generic6DOFJointSW.buildJacobian must be called previously. + */ + Vector3 getAxis(int axis_index) const; + + //! Get the relative Euler angle + /*! + \pre Generic6DOFJointSW.buildJacobian must be called previously. + */ + real_t getAngle(int axis_index) const; + + //! Test angular limit. + /*! + Calculates angular correction and returns true if limit needs to be corrected. + \pre Generic6DOFJointSW.buildJacobian must be called previously. + */ + bool testAngularLimitMotor(int axis_index); + + void setLinearLowerLimit(const Vector3& linearLower) + { + m_linearLimits.m_lowerLimit = linearLower; + } + + void setLinearUpperLimit(const Vector3& linearUpper) + { + m_linearLimits.m_upperLimit = linearUpper; + } + + void setAngularLowerLimit(const Vector3& angularLower) + { + m_angularLimits[0].m_loLimit = angularLower.x; + m_angularLimits[1].m_loLimit = angularLower.y; + m_angularLimits[2].m_loLimit = angularLower.z; + } + + void setAngularUpperLimit(const Vector3& angularUpper) + { + m_angularLimits[0].m_hiLimit = angularUpper.x; + m_angularLimits[1].m_hiLimit = angularUpper.y; + m_angularLimits[2].m_hiLimit = angularUpper.z; + } + + //! Retrieves the angular limit informacion + G6DOFRotationalLimitMotorSW * getRotationalLimitMotor(int index) + { + return &m_angularLimits[index]; + } + + //! Retrieves the limit informacion + G6DOFTranslationalLimitMotorSW * getTranslationalLimitMotor() + { + return &m_linearLimits; + } + + //first 3 are linear, next 3 are angular + void setLimit(int axis, real_t lo, real_t hi) + { + if(axis<3) + { + m_linearLimits.m_lowerLimit[axis] = lo; + m_linearLimits.m_upperLimit[axis] = hi; + } + else + { + m_angularLimits[axis-3].m_loLimit = lo; + m_angularLimits[axis-3].m_hiLimit = hi; + } + } + + //! Test limit + /*! + - free means upper < lower, + - locked means upper == lower + - limited means upper > lower + - limitIndex: first 3 are linear, next 3 are angular + */ + bool isLimited(int limitIndex) + { + if(limitIndex<3) + { + return m_linearLimits.isLimited(limitIndex); + + } + return m_angularLimits[limitIndex-3].isLimited(); + } + + const BodySW* getRigidBodyA() const + { + return A; + } + const BodySW* getRigidBodyB() const + { + return B; + } + + virtual void calcAnchorPos(void); // overridable + + void set_param(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisParam p_param, float p_value); + float get_param(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisParam p_param) const; + + void set_flag(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisFlag p_flag, bool p_value); + bool get_flag(Vector3::Axis p_axis,PhysicsServer::G6DOFJointAxisFlag p_flag) const; + +}; + + +#endif // GENERIC_6DOF_JOINT_SW_H diff --git a/servers/physics/joints/hinge_joint_sw.cpp b/servers/physics/joints/hinge_joint_sw.cpp new file mode 100644 index 0000000000..feaf00290d --- /dev/null +++ b/servers/physics/joints/hinge_joint_sw.cpp @@ -0,0 +1,438 @@ +#include "hinge_joint_sw.h" + +static void plane_space(const Vector3& n, Vector3& p, Vector3& q) { + + if (Math::abs(n.z) > 0.707106781186547524400844362) { + // choose p in y-z plane + real_t a = n[1]*n[1] + n[2]*n[2]; + real_t k = 1.0/Math::sqrt(a); + p=Vector3(0,-n[2]*k,n[1]*k); + // set q = n x p + q=Vector3(a*k,-n[0]*p[2],n[0]*p[1]); + } + else { + // choose p in x-y plane + real_t a = n.x*n.x + n.y*n.y; + real_t k = 1.0/Math::sqrt(a); + p=Vector3(-n.y*k,n.x*k,0); + // set q = n x p + q=Vector3(-n.z*p.y,n.z*p.x,a*k); + } +} + +HingeJointSW::HingeJointSW(BodySW* rbA,BodySW* rbB, const Transform& frameA, const Transform& frameB) : JointSW(_arr,2) { + + A=rbA; + B=rbB; + + m_rbAFrame=frameA; + m_rbBFrame=frameB; + // flip axis + m_rbBFrame.basis[0][2] *= real_t(-1.); + m_rbBFrame.basis[1][2] *= real_t(-1.); + m_rbBFrame.basis[2][2] *= real_t(-1.); + + + //start with free + m_lowerLimit = Math_PI; + m_upperLimit = -Math_PI; + + + m_useLimit = false; + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; + + tau=0.3; + + m_angularOnly=false; + m_enableAngularMotor=false; + + A->add_constraint(this,0); + B->add_constraint(this,1); + +} + +HingeJointSW::HingeJointSW(BodySW* rbA,BodySW* rbB, const Vector3& pivotInA,const Vector3& pivotInB, + const Vector3& axisInA,const Vector3& axisInB) : JointSW(_arr,2) { + + A=rbA; + B=rbB; + + m_rbAFrame.origin = pivotInA; + + // since no frame is given, assume this to be zero angle and just pick rb transform axis + Vector3 rbAxisA1 = rbA->get_transform().basis.get_axis(0); + + Vector3 rbAxisA2; + real_t projection = axisInA.dot(rbAxisA1); + if (projection >= 1.0f - CMP_EPSILON) { + rbAxisA1 = -rbA->get_transform().basis.get_axis(2); + rbAxisA2 = rbA->get_transform().basis.get_axis(1); + } else if (projection <= -1.0f + CMP_EPSILON) { + rbAxisA1 = rbA->get_transform().basis.get_axis(2); + rbAxisA2 = rbA->get_transform().basis.get_axis(1); + } else { + rbAxisA2 = axisInA.cross(rbAxisA1); + rbAxisA1 = rbAxisA2.cross(axisInA); + } + + m_rbAFrame.basis=Matrix3( rbAxisA1.x,rbAxisA2.x,axisInA.x, + rbAxisA1.y,rbAxisA2.y,axisInA.y, + rbAxisA1.z,rbAxisA2.z,axisInA.z ); + + Quat rotationArc = Quat(axisInA,axisInB); + Vector3 rbAxisB1 = rotationArc.xform(rbAxisA1); + Vector3 rbAxisB2 = axisInB.cross(rbAxisB1); + + m_rbBFrame.origin = pivotInB; + m_rbBFrame.basis=Matrix3( rbAxisB1.x,rbAxisB2.x,-axisInB.x, + rbAxisB1.y,rbAxisB2.y,-axisInB.y, + rbAxisB1.z,rbAxisB2.z,-axisInB.z ); + + //start with free + m_lowerLimit = Math_PI; + m_upperLimit = -Math_PI; + + + m_useLimit = false; + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; + + tau=0.3; + + m_angularOnly=false; + m_enableAngularMotor=false; + + A->add_constraint(this,0); + B->add_constraint(this,1); + +} + + + +bool HingeJointSW::setup(float p_step) { + + m_appliedImpulse = real_t(0.); + + if (!m_angularOnly) + { + Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin); + Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin); + Vector3 relPos = pivotBInW - pivotAInW; + + Vector3 normal[3]; + if (relPos.length_squared() > CMP_EPSILON) + { + normal[0] = relPos.normalized(); + } + else + { + normal[0]=Vector3(real_t(1.0),0,0); + } + + plane_space(normal[0], normal[1], normal[2]); + + for (int i=0;i<3;i++) + { + memnew_placement(&m_jac[i], JacobianEntrySW( + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + pivotAInW - A->get_transform().origin, + pivotBInW - B->get_transform().origin, + normal[i], + A->get_inv_inertia(), + A->get_inv_mass(), + B->get_inv_inertia(), + B->get_inv_mass()) ); + } + } + + //calculate two perpendicular jointAxis, orthogonal to hingeAxis + //these two jointAxis require equal angular velocities for both bodies + + //this is unused for now, it's a todo + Vector3 jointAxis0local; + Vector3 jointAxis1local; + + plane_space(m_rbAFrame.basis.get_axis(2),jointAxis0local,jointAxis1local); + + A->get_transform().basis.xform( m_rbAFrame.basis.get_axis(2) ); + Vector3 jointAxis0 = A->get_transform().basis.xform( jointAxis0local ); + Vector3 jointAxis1 = A->get_transform().basis.xform( jointAxis1local ); + Vector3 hingeAxisWorld = A->get_transform().basis.xform( m_rbAFrame.basis.get_axis(2) ); + + memnew_placement(&m_jacAng[0], JacobianEntrySW(jointAxis0, + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + A->get_inv_inertia(), + B->get_inv_inertia())); + + memnew_placement(&m_jacAng[1], JacobianEntrySW(jointAxis1, + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + A->get_inv_inertia(), + B->get_inv_inertia())); + + memnew_placement(&m_jacAng[2], JacobianEntrySW(hingeAxisWorld, + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + A->get_inv_inertia(), + B->get_inv_inertia())); + + + // Compute limit information + real_t hingeAngle = get_hinge_angle(); + +// print_line("angle: "+rtos(hingeAngle)); + //set bias, sign, clear accumulator + m_correction = real_t(0.); + m_limitSign = real_t(0.); + m_solveLimit = false; + m_accLimitImpulse = real_t(0.); + + + + /*if (m_useLimit) { + print_line("low: "+rtos(m_lowerLimit)); + print_line("hi: "+rtos(m_upperLimit)); + }*/ + +// if (m_lowerLimit < m_upperLimit) + if (m_useLimit && m_lowerLimit <= m_upperLimit) + { +// if (hingeAngle <= m_lowerLimit*m_limitSoftness) + if (hingeAngle <= m_lowerLimit) + { + m_correction = (m_lowerLimit - hingeAngle); + m_limitSign = 1.0f; + m_solveLimit = true; + } +// else if (hingeAngle >= m_upperLimit*m_limitSoftness) + else if (hingeAngle >= m_upperLimit) + { + m_correction = m_upperLimit - hingeAngle; + m_limitSign = -1.0f; + m_solveLimit = true; + } + } + + //Compute K = J*W*J' for hinge axis + Vector3 axisA = A->get_transform().basis.xform( m_rbAFrame.basis.get_axis(2) ); + m_kHinge = 1.0f / (A->compute_angular_impulse_denominator(axisA) + + B->compute_angular_impulse_denominator(axisA)); + + return true; +} + +void HingeJointSW::solve(float p_step) { + + Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin); + Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin); + + //real_t tau = real_t(0.3); + + //linear part + if (!m_angularOnly) + { + Vector3 rel_pos1 = pivotAInW - A->get_transform().origin; + Vector3 rel_pos2 = pivotBInW - B->get_transform().origin; + + Vector3 vel1 = A->get_velocity_in_local_point(rel_pos1); + Vector3 vel2 = B->get_velocity_in_local_point(rel_pos2); + Vector3 vel = vel1 - vel2; + + for (int i=0;i<3;i++) + { + const Vector3& normal = m_jac[i].m_linearJointAxis; + real_t jacDiagABInv = real_t(1.) / m_jac[i].getDiagonal(); + + real_t rel_vel; + rel_vel = normal.dot(vel); + //positional error (zeroth order error) + real_t depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + real_t impulse = depth*tau/p_step * jacDiagABInv - rel_vel * jacDiagABInv; + m_appliedImpulse += impulse; + Vector3 impulse_vector = normal * impulse; + A->apply_impulse(pivotAInW - A->get_transform().origin,impulse_vector); + B->apply_impulse(pivotBInW - B->get_transform().origin,-impulse_vector); + } + } + + + { + ///solve angular part + + // get axes in world space + Vector3 axisA = A->get_transform().basis.xform( m_rbAFrame.basis.get_axis(2) ); + Vector3 axisB = B->get_transform().basis.xform( m_rbBFrame.basis.get_axis(2) ); + + const Vector3& angVelA = A->get_angular_velocity(); + const Vector3& angVelB = B->get_angular_velocity(); + + Vector3 angVelAroundHingeAxisA = axisA * axisA.dot(angVelA); + Vector3 angVelAroundHingeAxisB = axisB * axisB.dot(angVelB); + + Vector3 angAorthog = angVelA - angVelAroundHingeAxisA; + Vector3 angBorthog = angVelB - angVelAroundHingeAxisB; + Vector3 velrelOrthog = angAorthog-angBorthog; + { + //solve orthogonal angular velocity correction + real_t relaxation = real_t(1.); + real_t len = velrelOrthog.length(); + if (len > real_t(0.00001)) + { + Vector3 normal = velrelOrthog.normalized(); + real_t denom = A->compute_angular_impulse_denominator(normal) + + B->compute_angular_impulse_denominator(normal); + // scale for mass and relaxation + velrelOrthog *= (real_t(1.)/denom) * m_relaxationFactor; + } + + //solve angular positional correction + Vector3 angularError = -axisA.cross(axisB) *(real_t(1.)/p_step); + real_t len2 = angularError.length(); + if (len2>real_t(0.00001)) + { + Vector3 normal2 = angularError.normalized(); + real_t denom2 = A->compute_angular_impulse_denominator(normal2) + + B->compute_angular_impulse_denominator(normal2); + angularError *= (real_t(1.)/denom2) * relaxation; + } + + A->apply_torque_impulse(-velrelOrthog+angularError); + B->apply_torque_impulse(velrelOrthog-angularError); + + // solve limit + if (m_solveLimit) + { + real_t amplitude = ( (angVelB - angVelA).dot( axisA )*m_relaxationFactor + m_correction* (real_t(1.)/p_step)*m_biasFactor ) * m_limitSign; + + real_t impulseMag = amplitude * m_kHinge; + + // Clamp the accumulated impulse + real_t temp = m_accLimitImpulse; + m_accLimitImpulse = MAX(m_accLimitImpulse + impulseMag, real_t(0) ); + impulseMag = m_accLimitImpulse - temp; + + + Vector3 impulse = axisA * impulseMag * m_limitSign; + A->apply_torque_impulse(impulse); + B->apply_torque_impulse(-impulse); + } + } + + //apply motor + if (m_enableAngularMotor) + { + //todo: add limits too + Vector3 angularLimit(0,0,0); + + Vector3 velrel = angVelAroundHingeAxisA - angVelAroundHingeAxisB; + real_t projRelVel = velrel.dot(axisA); + + real_t desiredMotorVel = m_motorTargetVelocity; + real_t motor_relvel = desiredMotorVel - projRelVel; + + real_t unclippedMotorImpulse = m_kHinge * motor_relvel;; + //todo: should clip against accumulated impulse + real_t clippedMotorImpulse = unclippedMotorImpulse > m_maxMotorImpulse ? m_maxMotorImpulse : unclippedMotorImpulse; + clippedMotorImpulse = clippedMotorImpulse < -m_maxMotorImpulse ? -m_maxMotorImpulse : clippedMotorImpulse; + Vector3 motorImp = clippedMotorImpulse * axisA; + + A->apply_torque_impulse(motorImp+angularLimit); + B->apply_torque_impulse(-motorImp-angularLimit); + + } + } + +} +/* +void HingeJointSW::updateRHS(real_t timeStep) +{ + (void)timeStep; + +} +*/ + +static _FORCE_INLINE_ real_t atan2fast(real_t y, real_t x) +{ + real_t coeff_1 = Math_PI / 4.0f; + real_t coeff_2 = 3.0f * coeff_1; + real_t abs_y = Math::abs(y); + real_t angle; + if (x >= 0.0f) { + real_t r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } else { + real_t r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; +} + + +real_t HingeJointSW::get_hinge_angle() { + const Vector3 refAxis0 = A->get_transform().basis.xform( m_rbAFrame.basis.get_axis(0) ); + const Vector3 refAxis1 = A->get_transform().basis.xform( m_rbAFrame.basis.get_axis(1) ); + const Vector3 swingAxis = B->get_transform().basis.xform( m_rbBFrame.basis.get_axis(1) ); + + return atan2fast( swingAxis.dot(refAxis0), swingAxis.dot(refAxis1) ); +} + + +void HingeJointSW::set_param(PhysicsServer::HingeJointParam p_param, float p_value) { + + switch (p_param) { + + case PhysicsServer::HINGE_JOINT_BIAS: tau=p_value; break; + case PhysicsServer::HINGE_JOINT_LIMIT_UPPER: m_upperLimit=p_value; break; + case PhysicsServer::HINGE_JOINT_LIMIT_LOWER: m_lowerLimit=p_value; break; + case PhysicsServer::HINGE_JOINT_LIMIT_BIAS: m_biasFactor=p_value; break; + case PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS: m_limitSoftness=p_value; break; + case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: m_relaxationFactor=p_value; break; + case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: m_motorTargetVelocity=p_value; break; + case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: m_maxMotorImpulse=p_value; break; + + } +} + +float HingeJointSW::get_param(PhysicsServer::HingeJointParam p_param) const{ + + switch (p_param) { + + case PhysicsServer::HINGE_JOINT_BIAS: return tau; + case PhysicsServer::HINGE_JOINT_LIMIT_UPPER: return m_upperLimit; + case PhysicsServer::HINGE_JOINT_LIMIT_LOWER: return m_lowerLimit; + case PhysicsServer::HINGE_JOINT_LIMIT_BIAS: return m_biasFactor; + case PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS: return m_limitSoftness; + case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: return m_relaxationFactor; + case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: return m_motorTargetVelocity; + case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: return m_maxMotorImpulse; + + } + + return 0; +} + +void HingeJointSW::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_value){ + + print_line(p_flag+": "+itos(p_value)); + switch (p_flag) { + case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: m_useLimit=p_value; break; + case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: m_enableAngularMotor=p_value; break; + } + +} +bool HingeJointSW::get_flag(PhysicsServer::HingeJointFlag p_flag) const{ + + switch (p_flag) { + case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: return m_useLimit; + case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR:return m_enableAngularMotor; + } + + return false; +} diff --git a/servers/physics/joints/hinge_joint_sw.h b/servers/physics/joints/hinge_joint_sw.h new file mode 100644 index 0000000000..4f6cdaf799 --- /dev/null +++ b/servers/physics/joints/hinge_joint_sw.h @@ -0,0 +1,89 @@ +#ifndef HINGE_JOINT_SW_H +#define HINGE_JOINT_SW_H + +#include "servers/physics/joints_sw.h" +#include "servers/physics/joints/jacobian_entry_sw.h" + + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + + +class HingeJointSW : public JointSW { + + union { + struct { + BodySW *A; + BodySW *B; + }; + + BodySW *_arr[2]; + }; + + JacobianEntrySW m_jac[3]; //3 orthogonal linear constraints + JacobianEntrySW m_jacAng[3]; //2 orthogonal angular constraints+ 1 for limit/motor + + Transform m_rbAFrame; // constraint axii. Assumes z is hinge axis. + Transform m_rbBFrame; + + real_t m_motorTargetVelocity; + real_t m_maxMotorImpulse; + + real_t m_limitSoftness; + real_t m_biasFactor; + real_t m_relaxationFactor; + + real_t m_lowerLimit; + real_t m_upperLimit; + + real_t m_kHinge; + + real_t m_limitSign; + real_t m_correction; + + real_t m_accLimitImpulse; + + real_t tau; + + bool m_useLimit; + bool m_angularOnly; + bool m_enableAngularMotor; + bool m_solveLimit; + + real_t m_appliedImpulse; + + +public: + + virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_HINGE; } + + virtual bool setup(float p_step); + virtual void solve(float p_step); + + real_t get_hinge_angle(); + + void set_param(PhysicsServer::HingeJointParam p_param, float p_value); + float get_param(PhysicsServer::HingeJointParam p_param) const; + + void set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_value); + bool get_flag(PhysicsServer::HingeJointFlag p_flag) const; + + HingeJointSW(BodySW* rbA,BodySW* rbB, const Transform& frameA, const Transform& frameB); + HingeJointSW(BodySW* rbA,BodySW* rbB, const Vector3& pivotInA,const Vector3& pivotInB, const Vector3& axisInA,const Vector3& axisInB); + +}; + +#endif // HINGE_JOINT_SW_H diff --git a/servers/physics/joints/jacobian_entry_sw.cpp b/servers/physics/joints/jacobian_entry_sw.cpp new file mode 100644 index 0000000000..faa3cf15c4 --- /dev/null +++ b/servers/physics/joints/jacobian_entry_sw.cpp @@ -0,0 +1,2 @@ +#include "jacobian_entry_sw.h" + diff --git a/servers/physics/joints/jacobian_entry_sw.h b/servers/physics/joints/jacobian_entry_sw.h new file mode 100644 index 0000000000..16fa034215 --- /dev/null +++ b/servers/physics/joints/jacobian_entry_sw.h @@ -0,0 +1,146 @@ +#ifndef JACOBIAN_ENTRY_SW_H +#define JACOBIAN_ENTRY_SW_H + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "transform.h" + +class JacobianEntrySW { +public: + JacobianEntrySW() {}; + //constraint between two different rigidbodies + JacobianEntrySW( + const Matrix3& world2A, + const Matrix3& world2B, + const Vector3& rel_pos1,const Vector3& rel_pos2, + const Vector3& jointAxis, + const Vector3& inertiaInvA, + const real_t massInvA, + const Vector3& inertiaInvB, + const real_t massInvB) + :m_linearJointAxis(jointAxis) + { + m_aJ = world2A.xform(rel_pos1.cross(m_linearJointAxis)); + m_bJ = world2B.xform(rel_pos2.cross(-m_linearJointAxis)); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = massInvA + m_0MinvJt.dot(m_aJ) + massInvB + m_1MinvJt.dot(m_bJ); + + ERR_FAIL_COND(m_Adiag <= real_t(0.0)); + } + + //angular constraint between two different rigidbodies + JacobianEntrySW(const Vector3& jointAxis, + const Matrix3& world2A, + const Matrix3& world2B, + const Vector3& inertiaInvA, + const Vector3& inertiaInvB) + :m_linearJointAxis(Vector3(real_t(0.),real_t(0.),real_t(0.))) + { + m_aJ= world2A.xform(jointAxis); + m_bJ = world2B.xform(-jointAxis); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = m_0MinvJt.dot(m_aJ) + m_1MinvJt.dot(m_bJ); + + ERR_FAIL_COND(m_Adiag <= real_t(0.0)); + } + + //angular constraint between two different rigidbodies + JacobianEntrySW(const Vector3& axisInA, + const Vector3& axisInB, + const Vector3& inertiaInvA, + const Vector3& inertiaInvB) + : m_linearJointAxis(Vector3(real_t(0.),real_t(0.),real_t(0.))) + , m_aJ(axisInA) + , m_bJ(-axisInB) + { + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = m_0MinvJt.dot(m_aJ) + m_1MinvJt.dot(m_bJ); + + ERR_FAIL_COND(m_Adiag <= real_t(0.0)); + } + + //constraint on one rigidbody + JacobianEntrySW( + const Matrix3& world2A, + const Vector3& rel_pos1,const Vector3& rel_pos2, + const Vector3& jointAxis, + const Vector3& inertiaInvA, + const real_t massInvA) + :m_linearJointAxis(jointAxis) + { + m_aJ= world2A.xform(rel_pos1.cross(jointAxis)); + m_bJ = world2A.xform(rel_pos2.cross(-jointAxis)); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = Vector3(real_t(0.),real_t(0.),real_t(0.)); + m_Adiag = massInvA + m_0MinvJt.dot(m_aJ); + + ERR_FAIL_COND(m_Adiag <= real_t(0.0)); + } + + real_t getDiagonal() const { return m_Adiag; } + + // for two constraints on the same rigidbody (for example vehicle friction) + real_t getNonDiagonal(const JacobianEntrySW& jacB, const real_t massInvA) const + { + const JacobianEntrySW& jacA = *this; + real_t lin = massInvA * jacA.m_linearJointAxis.dot(jacB.m_linearJointAxis); + real_t ang = jacA.m_0MinvJt.dot(jacB.m_aJ); + return lin + ang; + } + + + + // for two constraints on sharing two same rigidbodies (for example two contact points between two rigidbodies) + real_t getNonDiagonal(const JacobianEntrySW& jacB,const real_t massInvA,const real_t massInvB) const + { + const JacobianEntrySW& jacA = *this; + Vector3 lin = jacA.m_linearJointAxis * jacB.m_linearJointAxis; + Vector3 ang0 = jacA.m_0MinvJt * jacB.m_aJ; + Vector3 ang1 = jacA.m_1MinvJt * jacB.m_bJ; + Vector3 lin0 = massInvA * lin ; + Vector3 lin1 = massInvB * lin; + Vector3 sum = ang0+ang1+lin0+lin1; + return sum[0]+sum[1]+sum[2]; + } + + real_t getRelativeVelocity(const Vector3& linvelA,const Vector3& angvelA,const Vector3& linvelB,const Vector3& angvelB) + { + Vector3 linrel = linvelA - linvelB; + Vector3 angvela = angvelA * m_aJ; + Vector3 angvelb = angvelB * m_bJ; + linrel *= m_linearJointAxis; + angvela += angvelb; + angvela += linrel; + real_t rel_vel2 = angvela[0]+angvela[1]+angvela[2]; + return rel_vel2 + CMP_EPSILON; + } +//private: + + Vector3 m_linearJointAxis; + Vector3 m_aJ; + Vector3 m_bJ; + Vector3 m_0MinvJt; + Vector3 m_1MinvJt; + //Optimization: can be stored in the w/last component of one of the vectors + real_t m_Adiag; + +}; + + +#endif // JACOBIAN_ENTRY_SW_H diff --git a/servers/physics/joints/pin_joint_sw.cpp b/servers/physics/joints/pin_joint_sw.cpp new file mode 100644 index 0000000000..229863fb7b --- /dev/null +++ b/servers/physics/joints/pin_joint_sw.cpp @@ -0,0 +1,127 @@ +#include "pin_joint_sw.h" + +bool PinJointSW::setup(float p_step) { + + m_appliedImpulse = real_t(0.); + + Vector3 normal(0,0,0); + + for (int i=0;i<3;i++) + { + normal[i] = 1; + memnew_placement(&m_jac[i],JacobianEntrySW( + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + A->get_transform().xform(m_pivotInA) - A->get_transform().origin, + B->get_transform().xform(m_pivotInB) - B->get_transform().origin, + normal, + A->get_inv_inertia(), + A->get_inv_mass(), + B->get_inv_inertia(), + B->get_inv_mass())); + normal[i] = 0; + } + + return true; +} + +void PinJointSW::solve(float p_step){ + + Vector3 pivotAInW = A->get_transform().xform(m_pivotInA); + Vector3 pivotBInW = B->get_transform().xform(m_pivotInB); + + + Vector3 normal(0,0,0); + + +// Vector3 angvelA = A->get_transform().origin.getBasis().transpose() * A->getAngularVelocity(); +// Vector3 angvelB = B->get_transform().origin.getBasis().transpose() * B->getAngularVelocity(); + + for (int i=0;i<3;i++) + { + normal[i] = 1; + real_t jacDiagABInv = real_t(1.) / m_jac[i].getDiagonal(); + + Vector3 rel_pos1 = pivotAInW - A->get_transform().origin; + Vector3 rel_pos2 = pivotBInW - B->get_transform().origin; + //this jacobian entry could be re-used for all iterations + + Vector3 vel1 = A->get_velocity_in_local_point(rel_pos1); + Vector3 vel2 = B->get_velocity_in_local_point(rel_pos2); + Vector3 vel = vel1 - vel2; + + real_t rel_vel; + rel_vel = normal.dot(vel); + + /* + //velocity error (first order error) + real_t rel_vel = m_jac[i].getRelativeVelocity(A->getLinearVelocity(),angvelA, + B->getLinearVelocity(),angvelB); + */ + + //positional error (zeroth order error) + real_t depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + + real_t impulse = depth*m_tau/p_step * jacDiagABInv - m_damping * rel_vel * jacDiagABInv; + + real_t impulseClamp = m_impulseClamp; + if (impulseClamp > 0) + { + if (impulse < -impulseClamp) + impulse = -impulseClamp; + if (impulse > impulseClamp) + impulse = impulseClamp; + } + + m_appliedImpulse+=impulse; + Vector3 impulse_vector = normal * impulse; + A->apply_impulse(pivotAInW - A->get_transform().origin,impulse_vector); + B->apply_impulse(pivotBInW - B->get_transform().origin,-impulse_vector); + + normal[i] = 0; + } +} + +void PinJointSW::set_param(PhysicsServer::PinJointParam p_param,float p_value) { + + switch(p_param) { + case PhysicsServer::PIN_JOINT_BIAS: m_tau=p_value; break; + case PhysicsServer::PIN_JOINT_DAMPING: m_damping=p_value; break; + case PhysicsServer::PIN_JOINT_IMPULSE_CLAMP: m_impulseClamp=p_value; break; + } +} + +float PinJointSW::get_param(PhysicsServer::PinJointParam p_param) const{ + + switch(p_param) { + case PhysicsServer::PIN_JOINT_BIAS: return m_tau; + case PhysicsServer::PIN_JOINT_DAMPING: return m_damping; + case PhysicsServer::PIN_JOINT_IMPULSE_CLAMP: return m_impulseClamp; + } + + return 0; +} + +PinJointSW::PinJointSW(BodySW* p_body_a,const Vector3& p_pos_a,BodySW* p_body_b,const Vector3& p_pos_b) : JointSW(_arr,2) { + + A=p_body_a; + B=p_body_b; + m_pivotInA=p_pos_a; + m_pivotInB=p_pos_b; + + m_tau=0.3; + m_damping=1; + m_impulseClamp=0; + m_appliedImpulse=0; + + A->add_constraint(this,0); + B->add_constraint(this,1); + + +} + +PinJointSW::~PinJointSW() { + + + +} diff --git a/servers/physics/joints/pin_joint_sw.h b/servers/physics/joints/pin_joint_sw.h new file mode 100644 index 0000000000..dae6e7d5f2 --- /dev/null +++ b/servers/physics/joints/pin_joint_sw.h @@ -0,0 +1,67 @@ +#ifndef PIN_JOINT_SW_H +#define PIN_JOINT_SW_H + +#include "servers/physics/joints_sw.h" +#include "servers/physics/joints/jacobian_entry_sw.h" + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +class PinJointSW : public JointSW { + + union { + struct { + BodySW *A; + BodySW *B; + }; + + BodySW *_arr[2]; + }; + + + real_t m_tau; //bias + real_t m_damping; + real_t m_impulseClamp; + real_t m_appliedImpulse; + + JacobianEntrySW m_jac[3]; //3 orthogonal linear constraints + + Vector3 m_pivotInA; + Vector3 m_pivotInB; + +public: + + virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_PIN; } + + virtual bool setup(float p_step); + virtual void solve(float p_step); + + void set_param(PhysicsServer::PinJointParam p_param,float p_value); + float get_param(PhysicsServer::PinJointParam p_param) const; + + void set_pos_A(const Vector3& p_pos) { m_pivotInA=p_pos; } + void set_pos_B(const Vector3& p_pos) { m_pivotInB=p_pos; } + + Vector3 get_pos_A() { return m_pivotInB; } + Vector3 get_pos_B() { return m_pivotInA; } + + PinJointSW(BodySW* p_body_a,const Vector3& p_pos_a,BodySW* p_body_b,const Vector3& p_pos_b); + ~PinJointSW(); +}; + + + +#endif // PIN_JOINT_SW_H diff --git a/servers/physics/joints/slider_joint_sw.cpp b/servers/physics/joints/slider_joint_sw.cpp new file mode 100644 index 0000000000..faa6875378 --- /dev/null +++ b/servers/physics/joints/slider_joint_sw.cpp @@ -0,0 +1,439 @@ +#include "slider_joint_sw.h" + +//----------------------------------------------------------------------------- + +static _FORCE_INLINE_ real_t atan2fast(real_t y, real_t x) +{ + real_t coeff_1 = Math_PI / 4.0f; + real_t coeff_2 = 3.0f * coeff_1; + real_t abs_y = Math::abs(y); + real_t angle; + if (x >= 0.0f) { + real_t r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } else { + real_t r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; +} + + +void SliderJointSW::initParams() +{ + m_lowerLinLimit = real_t(1.0); + m_upperLinLimit = real_t(-1.0); + m_lowerAngLimit = real_t(0.); + m_upperAngLimit = real_t(0.); + m_softnessDirLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionDirLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingDirLin = real_t(0.); + m_softnessDirAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionDirAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingDirAng = real_t(0.); + m_softnessOrthoLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionOrthoLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingOrthoLin = SLIDER_CONSTRAINT_DEF_DAMPING; + m_softnessOrthoAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionOrthoAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingOrthoAng = SLIDER_CONSTRAINT_DEF_DAMPING; + m_softnessLimLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionLimLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingLimLin = SLIDER_CONSTRAINT_DEF_DAMPING; + m_softnessLimAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionLimAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingLimAng = SLIDER_CONSTRAINT_DEF_DAMPING; + + m_poweredLinMotor = false; + m_targetLinMotorVelocity = real_t(0.); + m_maxLinMotorForce = real_t(0.); + m_accumulatedLinMotorImpulse = real_t(0.0); + + m_poweredAngMotor = false; + m_targetAngMotorVelocity = real_t(0.); + m_maxAngMotorForce = real_t(0.); + m_accumulatedAngMotorImpulse = real_t(0.0); + +} // SliderJointSW::initParams() + +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- + +SliderJointSW::SliderJointSW(BodySW* rbA, BodySW* rbB, const Transform& frameInA, const Transform& frameInB) + : JointSW(_arr,2) + , m_frameInA(frameInA) + , m_frameInB(frameInB) +{ + + A=rbA; + B=rbB; + + A->add_constraint(this,0); + B->add_constraint(this,1); + + initParams(); +} // SliderJointSW::SliderJointSW() + +//----------------------------------------------------------------------------- + +bool SliderJointSW::setup(float p_step) +{ + + //calculate transforms + m_calculatedTransformA = A->get_transform() * m_frameInA; + m_calculatedTransformB = B->get_transform() * m_frameInB; + m_realPivotAInW = m_calculatedTransformA.origin; + m_realPivotBInW = m_calculatedTransformB.origin; + m_sliderAxis = m_calculatedTransformA.basis.get_axis(0); // along X + m_delta = m_realPivotBInW - m_realPivotAInW; + m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis; + m_relPosA = m_projPivotInW - A->get_transform().origin; + m_relPosB = m_realPivotBInW - B->get_transform().origin; + Vector3 normalWorld; + int i; + //linear part + for(i = 0; i < 3; i++) + { + normalWorld = m_calculatedTransformA.basis.get_axis(i); + memnew_placement(&m_jacLin[i], JacobianEntrySW( + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + m_relPosA, + m_relPosB, + normalWorld, + A->get_inv_inertia(), + A->get_inv_mass(), + B->get_inv_inertia(), + B->get_inv_mass() + )); + m_jacLinDiagABInv[i] = real_t(1.) / m_jacLin[i].getDiagonal(); + m_depth[i] = m_delta.dot(normalWorld); + } + testLinLimits(); + // angular part + for(i = 0; i < 3; i++) + { + normalWorld = m_calculatedTransformA.basis.get_axis(i); + memnew_placement(&m_jacAng[i], JacobianEntrySW( + normalWorld, + A->get_transform().basis.transposed(), + B->get_transform().basis.transposed(), + A->get_inv_inertia(), + B->get_inv_inertia() + )); + } + testAngLimits(); + Vector3 axisA = m_calculatedTransformA.basis.get_axis(0); + m_kAngle = real_t(1.0 )/ (A->compute_angular_impulse_denominator(axisA) + B->compute_angular_impulse_denominator(axisA)); + // clear accumulator for motors + m_accumulatedLinMotorImpulse = real_t(0.0); + m_accumulatedAngMotorImpulse = real_t(0.0); + + return true; +} // SliderJointSW::buildJacobianInt() + +//----------------------------------------------------------------------------- + +void SliderJointSW::solve(real_t p_step) { + + int i; + // linear + Vector3 velA = A->get_velocity_in_local_point(m_relPosA); + Vector3 velB = B->get_velocity_in_local_point(m_relPosB); + Vector3 vel = velA - velB; + for(i = 0; i < 3; i++) + { + const Vector3& normal = m_jacLin[i].m_linearJointAxis; + real_t rel_vel = normal.dot(vel); + // calculate positional error + real_t depth = m_depth[i]; + // get parameters + real_t softness = (i) ? m_softnessOrthoLin : (m_solveLinLim ? m_softnessLimLin : m_softnessDirLin); + real_t restitution = (i) ? m_restitutionOrthoLin : (m_solveLinLim ? m_restitutionLimLin : m_restitutionDirLin); + real_t damping = (i) ? m_dampingOrthoLin : (m_solveLinLim ? m_dampingLimLin : m_dampingDirLin); + // calcutate and apply impulse + real_t normalImpulse = softness * (restitution * depth / p_step - damping * rel_vel) * m_jacLinDiagABInv[i]; + Vector3 impulse_vector = normal * normalImpulse; + A->apply_impulse( m_relPosA, impulse_vector); + B->apply_impulse(m_relPosB,-impulse_vector); + if(m_poweredLinMotor && (!i)) + { // apply linear motor + if(m_accumulatedLinMotorImpulse < m_maxLinMotorForce) + { + real_t desiredMotorVel = m_targetLinMotorVelocity; + real_t motor_relvel = desiredMotorVel + rel_vel; + normalImpulse = -motor_relvel * m_jacLinDiagABInv[i]; + // clamp accumulated impulse + real_t new_acc = m_accumulatedLinMotorImpulse + Math::abs(normalImpulse); + if(new_acc > m_maxLinMotorForce) + { + new_acc = m_maxLinMotorForce; + } + real_t del = new_acc - m_accumulatedLinMotorImpulse; + if(normalImpulse < real_t(0.0)) + { + normalImpulse = -del; + } + else + { + normalImpulse = del; + } + m_accumulatedLinMotorImpulse = new_acc; + // apply clamped impulse + impulse_vector = normal * normalImpulse; + A->apply_impulse( m_relPosA, impulse_vector); + B->apply_impulse( m_relPosB,-impulse_vector); + } + } + } + // angular + // get axes in world space + Vector3 axisA = m_calculatedTransformA.basis.get_axis(0); + Vector3 axisB = m_calculatedTransformB.basis.get_axis(0); + + const Vector3& angVelA = A->get_angular_velocity(); + const Vector3& angVelB = B->get_angular_velocity(); + + Vector3 angVelAroundAxisA = axisA * axisA.dot(angVelA); + Vector3 angVelAroundAxisB = axisB * axisB.dot(angVelB); + + Vector3 angAorthog = angVelA - angVelAroundAxisA; + Vector3 angBorthog = angVelB - angVelAroundAxisB; + Vector3 velrelOrthog = angAorthog-angBorthog; + //solve orthogonal angular velocity correction + real_t len = velrelOrthog.length(); + if (len > real_t(0.00001)) + { + Vector3 normal = velrelOrthog.normalized(); + real_t denom = A->compute_angular_impulse_denominator(normal) + B->compute_angular_impulse_denominator(normal); + velrelOrthog *= (real_t(1.)/denom) * m_dampingOrthoAng * m_softnessOrthoAng; + } + //solve angular positional correction + Vector3 angularError = axisA.cross(axisB) *(real_t(1.)/p_step); + real_t len2 = angularError.length(); + if (len2>real_t(0.00001)) + { + Vector3 normal2 = angularError.normalized(); + real_t denom2 = A->compute_angular_impulse_denominator(normal2) + B->compute_angular_impulse_denominator(normal2); + angularError *= (real_t(1.)/denom2) * m_restitutionOrthoAng * m_softnessOrthoAng; + } + // apply impulse + A->apply_torque_impulse(-velrelOrthog+angularError); + B->apply_torque_impulse(velrelOrthog-angularError); + real_t impulseMag; + //solve angular limits + if(m_solveAngLim) + { + impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingLimAng + m_angDepth * m_restitutionLimAng / p_step; + impulseMag *= m_kAngle * m_softnessLimAng; + } + else + { + impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingDirAng + m_angDepth * m_restitutionDirAng / p_step; + impulseMag *= m_kAngle * m_softnessDirAng; + } + Vector3 impulse = axisA * impulseMag; + A->apply_torque_impulse(impulse); + B->apply_torque_impulse(-impulse); + //apply angular motor + if(m_poweredAngMotor) + { + if(m_accumulatedAngMotorImpulse < m_maxAngMotorForce) + { + Vector3 velrel = angVelAroundAxisA - angVelAroundAxisB; + real_t projRelVel = velrel.dot(axisA); + + real_t desiredMotorVel = m_targetAngMotorVelocity; + real_t motor_relvel = desiredMotorVel - projRelVel; + + real_t angImpulse = m_kAngle * motor_relvel; + // clamp accumulated impulse + real_t new_acc = m_accumulatedAngMotorImpulse + Math::abs(angImpulse); + if(new_acc > m_maxAngMotorForce) + { + new_acc = m_maxAngMotorForce; + } + real_t del = new_acc - m_accumulatedAngMotorImpulse; + if(angImpulse < real_t(0.0)) + { + angImpulse = -del; + } + else + { + angImpulse = del; + } + m_accumulatedAngMotorImpulse = new_acc; + // apply clamped impulse + Vector3 motorImp = angImpulse * axisA; + A->apply_torque_impulse(motorImp); + B->apply_torque_impulse(-motorImp); + } + } +} // SliderJointSW::solveConstraint() + +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +void SliderJointSW::calculateTransforms(void){ + m_calculatedTransformA = A->get_transform() * m_frameInA ; + m_calculatedTransformB = B->get_transform() * m_frameInB; + m_realPivotAInW = m_calculatedTransformA.origin; + m_realPivotBInW = m_calculatedTransformB.origin; + m_sliderAxis = m_calculatedTransformA.basis.get_axis(0); // along X + m_delta = m_realPivotBInW - m_realPivotAInW; + m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis; + Vector3 normalWorld; + int i; + //linear part + for(i = 0; i < 3; i++) + { + normalWorld = m_calculatedTransformA.basis.get_axis(i); + m_depth[i] = m_delta.dot(normalWorld); + } +} // SliderJointSW::calculateTransforms() + +//----------------------------------------------------------------------------- + +void SliderJointSW::testLinLimits(void) +{ + m_solveLinLim = false; + m_linPos = m_depth[0]; + if(m_lowerLinLimit <= m_upperLinLimit) + { + if(m_depth[0] > m_upperLinLimit) + { + m_depth[0] -= m_upperLinLimit; + m_solveLinLim = true; + } + else if(m_depth[0] < m_lowerLinLimit) + { + m_depth[0] -= m_lowerLinLimit; + m_solveLinLim = true; + } + else + { + m_depth[0] = real_t(0.); + } + } + else + { + m_depth[0] = real_t(0.); + } +} // SliderJointSW::testLinLimits() + +//----------------------------------------------------------------------------- + + +void SliderJointSW::testAngLimits(void) +{ + m_angDepth = real_t(0.); + m_solveAngLim = false; + if(m_lowerAngLimit <= m_upperAngLimit) + { + const Vector3 axisA0 = m_calculatedTransformA.basis.get_axis(1); + const Vector3 axisA1 = m_calculatedTransformA.basis.get_axis(2); + const Vector3 axisB0 = m_calculatedTransformB.basis.get_axis(1); + real_t rot = atan2fast(axisB0.dot(axisA1), axisB0.dot(axisA0)); + if(rot < m_lowerAngLimit) + { + m_angDepth = rot - m_lowerAngLimit; + m_solveAngLim = true; + } + else if(rot > m_upperAngLimit) + { + m_angDepth = rot - m_upperAngLimit; + m_solveAngLim = true; + } + } +} // SliderJointSW::testAngLimits() + + +//----------------------------------------------------------------------------- + + + +Vector3 SliderJointSW::getAncorInA(void) +{ + Vector3 ancorInA; + ancorInA = m_realPivotAInW + (m_lowerLinLimit + m_upperLinLimit) * real_t(0.5) * m_sliderAxis; + ancorInA = A->get_transform().inverse().xform( ancorInA ); + return ancorInA; +} // SliderJointSW::getAncorInA() + +//----------------------------------------------------------------------------- + +Vector3 SliderJointSW::getAncorInB(void) +{ + Vector3 ancorInB; + ancorInB = m_frameInB.origin; + return ancorInB; +} // SliderJointSW::getAncorInB(); + +void SliderJointSW::set_param(PhysicsServer::SliderJointParam p_param, float p_value) { + + switch(p_param) { + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER: m_upperLinLimit=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER: m_lowerLinLimit=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS: m_softnessLimLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION: m_restitutionLimLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING: m_dampingLimLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_SOFTNESS: m_softnessDirLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_RESTITUTION: m_restitutionDirLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_DAMPING: m_dampingDirLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_SOFTNESS: m_softnessOrthoLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_RESTITUTION: m_restitutionOrthoLin=p_value; break; + case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_DAMPING: m_dampingOrthoLin=p_value; break; + + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER: m_upperAngLimit=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER: m_lowerAngLimit=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS: m_softnessLimAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_RESTITUTION: m_restitutionLimAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING: m_dampingLimAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_SOFTNESS: m_softnessDirAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_RESTITUTION: m_restitutionDirAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_DAMPING: m_dampingDirAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: m_softnessOrthoAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: m_restitutionOrthoAng=p_value; break; + case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: m_dampingOrthoAng=p_value; break; + + } + +} + +float SliderJointSW::get_param(PhysicsServer::SliderJointParam p_param) const { + + switch(p_param) { + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER: return m_upperLinLimit; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER: return m_lowerLinLimit; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS: return m_softnessLimLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION: return m_restitutionLimLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING: return m_dampingLimLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_SOFTNESS: return m_softnessDirLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_RESTITUTION: return m_restitutionDirLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_DAMPING: return m_dampingDirLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_SOFTNESS: return m_softnessOrthoLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_RESTITUTION: return m_restitutionOrthoLin; + case PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_DAMPING: return m_dampingOrthoLin; + + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER: return m_upperAngLimit; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER: return m_lowerAngLimit; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS: return m_softnessLimAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_RESTITUTION: return m_restitutionLimAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING: return m_dampingLimAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_SOFTNESS: return m_softnessDirAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_RESTITUTION: return m_restitutionDirAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_DAMPING: return m_dampingDirAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: return m_softnessOrthoAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: return m_restitutionOrthoAng; + case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: return m_dampingOrthoAng; + + } + + return 0; + +} + + diff --git a/servers/physics/joints/slider_joint_sw.h b/servers/physics/joints/slider_joint_sw.h new file mode 100644 index 0000000000..517bb5e6bc --- /dev/null +++ b/servers/physics/joints/slider_joint_sw.h @@ -0,0 +1,218 @@ +#ifndef SLIDER_JOINT_SW_H +#define SLIDER_JOINT_SW_H + +#include "servers/physics/joints_sw.h" +#include "servers/physics/joints/jacobian_entry_sw.h" + + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +/* +Added by Roman Ponomarev (rponom@gmail.com) +April 04, 2008 + +*/ + +#define SLIDER_CONSTRAINT_DEF_SOFTNESS (real_t(1.0)) +#define SLIDER_CONSTRAINT_DEF_DAMPING (real_t(1.0)) +#define SLIDER_CONSTRAINT_DEF_RESTITUTION (real_t(0.7)) + +//----------------------------------------------------------------------------- + +class SliderJointSW : public JointSW { +protected: + + union { + struct { + BodySW *A; + BodySW *B; + }; + + BodySW *_arr[2]; + }; + + Transform m_frameInA; + Transform m_frameInB; + + // linear limits + real_t m_lowerLinLimit; + real_t m_upperLinLimit; + // angular limits + real_t m_lowerAngLimit; + real_t m_upperAngLimit; + // softness, restitution and damping for different cases + // DirLin - moving inside linear limits + // LimLin - hitting linear limit + // DirAng - moving inside angular limits + // LimAng - hitting angular limit + // OrthoLin, OrthoAng - against constraint axis + real_t m_softnessDirLin; + real_t m_restitutionDirLin; + real_t m_dampingDirLin; + real_t m_softnessDirAng; + real_t m_restitutionDirAng; + real_t m_dampingDirAng; + real_t m_softnessLimLin; + real_t m_restitutionLimLin; + real_t m_dampingLimLin; + real_t m_softnessLimAng; + real_t m_restitutionLimAng; + real_t m_dampingLimAng; + real_t m_softnessOrthoLin; + real_t m_restitutionOrthoLin; + real_t m_dampingOrthoLin; + real_t m_softnessOrthoAng; + real_t m_restitutionOrthoAng; + real_t m_dampingOrthoAng; + + // for interlal use + bool m_solveLinLim; + bool m_solveAngLim; + + JacobianEntrySW m_jacLin[3]; + real_t m_jacLinDiagABInv[3]; + + JacobianEntrySW m_jacAng[3]; + + real_t m_timeStep; + Transform m_calculatedTransformA; + Transform m_calculatedTransformB; + + Vector3 m_sliderAxis; + Vector3 m_realPivotAInW; + Vector3 m_realPivotBInW; + Vector3 m_projPivotInW; + Vector3 m_delta; + Vector3 m_depth; + Vector3 m_relPosA; + Vector3 m_relPosB; + + real_t m_linPos; + + real_t m_angDepth; + real_t m_kAngle; + + bool m_poweredLinMotor; + real_t m_targetLinMotorVelocity; + real_t m_maxLinMotorForce; + real_t m_accumulatedLinMotorImpulse; + + bool m_poweredAngMotor; + real_t m_targetAngMotorVelocity; + real_t m_maxAngMotorForce; + real_t m_accumulatedAngMotorImpulse; + + //------------------------ + void initParams(); +public: + // constructors + SliderJointSW(BodySW* rbA, BodySW* rbB, const Transform& frameInA, const Transform& frameInB); + //SliderJointSW(); + // overrides + + // access + const BodySW* getRigidBodyA() const { return A; } + const BodySW* getRigidBodyB() const { return B; } + const Transform & getCalculatedTransformA() const { return m_calculatedTransformA; } + const Transform & getCalculatedTransformB() const { return m_calculatedTransformB; } + const Transform & getFrameOffsetA() const { return m_frameInA; } + const Transform & getFrameOffsetB() const { return m_frameInB; } + Transform & getFrameOffsetA() { return m_frameInA; } + Transform & getFrameOffsetB() { return m_frameInB; } + real_t getLowerLinLimit() { return m_lowerLinLimit; } + void setLowerLinLimit(real_t lowerLimit) { m_lowerLinLimit = lowerLimit; } + real_t getUpperLinLimit() { return m_upperLinLimit; } + void setUpperLinLimit(real_t upperLimit) { m_upperLinLimit = upperLimit; } + real_t getLowerAngLimit() { return m_lowerAngLimit; } + void setLowerAngLimit(real_t lowerLimit) { m_lowerAngLimit = lowerLimit; } + real_t getUpperAngLimit() { return m_upperAngLimit; } + void setUpperAngLimit(real_t upperLimit) { m_upperAngLimit = upperLimit; } + + real_t getSoftnessDirLin() { return m_softnessDirLin; } + real_t getRestitutionDirLin() { return m_restitutionDirLin; } + real_t getDampingDirLin() { return m_dampingDirLin ; } + real_t getSoftnessDirAng() { return m_softnessDirAng; } + real_t getRestitutionDirAng() { return m_restitutionDirAng; } + real_t getDampingDirAng() { return m_dampingDirAng; } + real_t getSoftnessLimLin() { return m_softnessLimLin; } + real_t getRestitutionLimLin() { return m_restitutionLimLin; } + real_t getDampingLimLin() { return m_dampingLimLin; } + real_t getSoftnessLimAng() { return m_softnessLimAng; } + real_t getRestitutionLimAng() { return m_restitutionLimAng; } + real_t getDampingLimAng() { return m_dampingLimAng; } + real_t getSoftnessOrthoLin() { return m_softnessOrthoLin; } + real_t getRestitutionOrthoLin() { return m_restitutionOrthoLin; } + real_t getDampingOrthoLin() { return m_dampingOrthoLin; } + real_t getSoftnessOrthoAng() { return m_softnessOrthoAng; } + real_t getRestitutionOrthoAng() { return m_restitutionOrthoAng; } + real_t getDampingOrthoAng() { return m_dampingOrthoAng; } + void setSoftnessDirLin(real_t softnessDirLin) { m_softnessDirLin = softnessDirLin; } + void setRestitutionDirLin(real_t restitutionDirLin) { m_restitutionDirLin = restitutionDirLin; } + void setDampingDirLin(real_t dampingDirLin) { m_dampingDirLin = dampingDirLin; } + void setSoftnessDirAng(real_t softnessDirAng) { m_softnessDirAng = softnessDirAng; } + void setRestitutionDirAng(real_t restitutionDirAng) { m_restitutionDirAng = restitutionDirAng; } + void setDampingDirAng(real_t dampingDirAng) { m_dampingDirAng = dampingDirAng; } + void setSoftnessLimLin(real_t softnessLimLin) { m_softnessLimLin = softnessLimLin; } + void setRestitutionLimLin(real_t restitutionLimLin) { m_restitutionLimLin = restitutionLimLin; } + void setDampingLimLin(real_t dampingLimLin) { m_dampingLimLin = dampingLimLin; } + void setSoftnessLimAng(real_t softnessLimAng) { m_softnessLimAng = softnessLimAng; } + void setRestitutionLimAng(real_t restitutionLimAng) { m_restitutionLimAng = restitutionLimAng; } + void setDampingLimAng(real_t dampingLimAng) { m_dampingLimAng = dampingLimAng; } + void setSoftnessOrthoLin(real_t softnessOrthoLin) { m_softnessOrthoLin = softnessOrthoLin; } + void setRestitutionOrthoLin(real_t restitutionOrthoLin) { m_restitutionOrthoLin = restitutionOrthoLin; } + void setDampingOrthoLin(real_t dampingOrthoLin) { m_dampingOrthoLin = dampingOrthoLin; } + void setSoftnessOrthoAng(real_t softnessOrthoAng) { m_softnessOrthoAng = softnessOrthoAng; } + void setRestitutionOrthoAng(real_t restitutionOrthoAng) { m_restitutionOrthoAng = restitutionOrthoAng; } + void setDampingOrthoAng(real_t dampingOrthoAng) { m_dampingOrthoAng = dampingOrthoAng; } + void setPoweredLinMotor(bool onOff) { m_poweredLinMotor = onOff; } + bool getPoweredLinMotor() { return m_poweredLinMotor; } + void setTargetLinMotorVelocity(real_t targetLinMotorVelocity) { m_targetLinMotorVelocity = targetLinMotorVelocity; } + real_t getTargetLinMotorVelocity() { return m_targetLinMotorVelocity; } + void setMaxLinMotorForce(real_t maxLinMotorForce) { m_maxLinMotorForce = maxLinMotorForce; } + real_t getMaxLinMotorForce() { return m_maxLinMotorForce; } + void setPoweredAngMotor(bool onOff) { m_poweredAngMotor = onOff; } + bool getPoweredAngMotor() { return m_poweredAngMotor; } + void setTargetAngMotorVelocity(real_t targetAngMotorVelocity) { m_targetAngMotorVelocity = targetAngMotorVelocity; } + real_t getTargetAngMotorVelocity() { return m_targetAngMotorVelocity; } + void setMaxAngMotorForce(real_t maxAngMotorForce) { m_maxAngMotorForce = maxAngMotorForce; } + real_t getMaxAngMotorForce() { return m_maxAngMotorForce; } + real_t getLinearPos() { return m_linPos; } + + // access for ODE solver + bool getSolveLinLimit() { return m_solveLinLim; } + real_t getLinDepth() { return m_depth[0]; } + bool getSolveAngLimit() { return m_solveAngLim; } + real_t getAngDepth() { return m_angDepth; } + // shared code used by ODE solver + void calculateTransforms(void); + void testLinLimits(void); + void testAngLimits(void); + // access for PE Solver + Vector3 getAncorInA(void); + Vector3 getAncorInB(void); + + void set_param(PhysicsServer::SliderJointParam p_param, float p_value); + float get_param(PhysicsServer::SliderJointParam p_param) const; + + bool setup(float p_step); + void solve(float p_step); + + virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_SLIDER; } + +}; + + +#endif // SLIDER_JOINT_SW_H |