godot/scene/3d/vehicle_body.cpp
Juan Linietsky 1a2cb755e2 3D Physics and Other Stuff
-=-=-=-=-=-=-=-=-=-=-=-=-=

-New Vehicle (Based on Bullet's RaycastVehicle) - Vehiclebody/VehicleWheel. Demo will come soon, old vehicle (CarBody) will go away soon too.
-A lot of fixes to the 3D physics engine
-Added KinematicBody with demo
-Fixed the space query API for 2D (demo will come soon). 3D is WIP.
-Fixed long-standing bug with body_enter/body_exit for Area and Area2D
-Performance variables now includes physics (active bodies, collision pairs and islands)
-Ability to see what's inside of instanced scenes!
-Fixed Blend Shapes (no bs+skeleton yet)
-Added an Android JavaClassWrapper singleton for using Android native classes directly from GDScript. This is very Alpha!
2014-09-02 23:13:40 -03:00

1045 lines
29 KiB
C++

#include "vehicle_body.h"
#define ROLLING_INFLUENCE_FIX
class btVehicleJacobianEntry
{
public:
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;
real_t getDiagonal() const { return m_Adiag; }
btVehicleJacobianEntry() {};
//constraint between two different rigidbodies
btVehicleJacobianEntry(
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);
//btAssert(m_Adiag > real_t(0.0));
}
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;
}
};
void VehicleWheel::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_SCENE) {
if (!get_parent())
return;
VehicleBody *cb = get_parent()->cast_to<VehicleBody>();
if (!cb)
return;
body=cb;
local_xform=get_transform();
cb->wheels.push_back(this);
m_chassisConnectionPointCS = get_transform().origin;
m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized();
}
if (p_what==NOTIFICATION_EXIT_SCENE) {
if (!get_parent())
return;
VehicleBody *cb = get_parent()->cast_to<VehicleBody>();
if (!cb)
return;
cb->wheels.erase(this);
body=NULL;
}
}
void VehicleWheel::_update(PhysicsDirectBodyState *s) {
if (m_raycastInfo.m_isInContact)
{
real_t project= m_raycastInfo.m_contactNormalWS.dot( m_raycastInfo.m_wheelDirectionWS );
Vector3 chassis_velocity_at_contactPoint;
Vector3 relpos = m_raycastInfo.m_contactPointWS - s->get_transform().origin;
chassis_velocity_at_contactPoint = s->get_linear_velocity() +
(s->get_angular_velocity()).cross(relpos);// * mPos);
real_t projVel = m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint );
if ( project >= real_t(-0.1))
{
m_suspensionRelativeVelocity = real_t(0.0);
m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1);
}
else
{
real_t inv = real_t(-1.) / project;
m_suspensionRelativeVelocity = projVel * inv;
m_clippedInvContactDotSuspension = inv;
}
}
else // Not in contact : position wheel in a nice (rest length) position
{
m_raycastInfo.m_suspensionLength = m_suspensionRestLength;
m_suspensionRelativeVelocity = real_t(0.0);
m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS;
m_clippedInvContactDotSuspension = real_t(1.0);
}
}
void VehicleWheel::set_radius(float p_radius) {
m_wheelRadius=p_radius;
update_gizmo();
}
float VehicleWheel::get_radius() const{
return m_wheelRadius;
}
void VehicleWheel::set_suspension_rest_length(float p_length){
m_suspensionRestLength=p_length;
update_gizmo();
}
float VehicleWheel::get_suspension_rest_length() const{
return m_suspensionRestLength;
}
void VehicleWheel::set_suspension_travel(float p_length){
m_maxSuspensionTravelCm=p_length/0.01;
}
float VehicleWheel::get_suspension_travel() const{
return m_maxSuspensionTravelCm*0.01;
}
void VehicleWheel::set_suspension_stiffness(float p_value){
m_suspensionStiffness=p_value;
}
float VehicleWheel::get_suspension_stiffness() const{
return m_suspensionStiffness;
}
void VehicleWheel::set_suspension_max_force(float p_value){
m_maxSuspensionForce=p_value;
}
float VehicleWheel::get_suspension_max_force() const{
return m_maxSuspensionForce;
}
void VehicleWheel::set_damping_compression(float p_value){
m_wheelsDampingCompression=p_value;
}
float VehicleWheel::get_damping_compression() const{
return m_wheelsDampingRelaxation;
}
void VehicleWheel::set_damping_relaxation(float p_value){
m_wheelsDampingRelaxation=p_value;
}
float VehicleWheel::get_damping_relaxation() const{
return m_wheelsDampingRelaxation;
}
void VehicleWheel::set_friction_slip(float p_value) {
m_frictionSlip=p_value;
}
float VehicleWheel::get_friction_slip() const{
return m_frictionSlip;
}
void VehicleWheel::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_radius","length"),&VehicleWheel::set_radius);
ObjectTypeDB::bind_method(_MD("get_radius"),&VehicleWheel::get_radius);
ObjectTypeDB::bind_method(_MD("set_suspension_rest_length","length"),&VehicleWheel::set_suspension_rest_length);
ObjectTypeDB::bind_method(_MD("get_suspension_rest_length"),&VehicleWheel::get_suspension_rest_length);
ObjectTypeDB::bind_method(_MD("set_suspension_travel","length"),&VehicleWheel::set_suspension_travel);
ObjectTypeDB::bind_method(_MD("get_suspension_travel"),&VehicleWheel::get_suspension_travel);
ObjectTypeDB::bind_method(_MD("set_suspension_stiffness","length"),&VehicleWheel::set_suspension_stiffness);
ObjectTypeDB::bind_method(_MD("get_suspension_stiffness"),&VehicleWheel::get_suspension_stiffness);
ObjectTypeDB::bind_method(_MD("set_suspension_max_force","length"),&VehicleWheel::set_suspension_max_force);
ObjectTypeDB::bind_method(_MD("get_suspension_max_force"),&VehicleWheel::get_suspension_max_force);
ObjectTypeDB::bind_method(_MD("set_damping_compression","length"),&VehicleWheel::set_damping_compression);
ObjectTypeDB::bind_method(_MD("get_damping_compression"),&VehicleWheel::get_damping_compression);
ObjectTypeDB::bind_method(_MD("set_damping_relaxation","length"),&VehicleWheel::set_damping_relaxation);
ObjectTypeDB::bind_method(_MD("get_damping_relaxation"),&VehicleWheel::get_damping_relaxation);
ObjectTypeDB::bind_method(_MD("set_use_as_traction","enable"),&VehicleWheel::set_use_as_traction);
ObjectTypeDB::bind_method(_MD("is_used_as_traction"),&VehicleWheel::is_used_as_traction);
ObjectTypeDB::bind_method(_MD("set_use_as_steering","enable"),&VehicleWheel::set_use_as_steering);
ObjectTypeDB::bind_method(_MD("is_used_as_steering"),&VehicleWheel::is_used_as_steering);
ObjectTypeDB::bind_method(_MD("set_friction_slip","length"),&VehicleWheel::set_friction_slip);
ObjectTypeDB::bind_method(_MD("get_friction_slip"),&VehicleWheel::get_friction_slip);
ADD_PROPERTY(PropertyInfo(Variant::BOOL,"type/traction"),_SCS("set_use_as_traction"),_SCS("is_used_as_traction"));
ADD_PROPERTY(PropertyInfo(Variant::BOOL,"type/steering"),_SCS("set_use_as_steering"),_SCS("is_used_as_steering"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"wheel/radius"),_SCS("set_radius"),_SCS("get_radius"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"wheel/rest_length"),_SCS("set_suspension_rest_length"),_SCS("get_suspension_rest_length"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"wheel/friction_slip"),_SCS("set_friction_slip"),_SCS("get_friction_slip"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"suspension/travel"),_SCS("set_suspension_travel"),_SCS("get_suspension_travel"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"suspension/stiffness"),_SCS("set_suspension_stiffness"),_SCS("get_suspension_stiffness"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"suspension/max_force"),_SCS("set_suspension_max_force"),_SCS("get_suspension_max_force"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"damping/compression"),_SCS("set_damping_compression"),_SCS("get_damping_compression"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"damping/relaxation"),_SCS("set_damping_relaxation"),_SCS("get_damping_relaxation"));
}
void VehicleWheel::set_use_as_traction(bool p_enable) {
engine_traction=p_enable;
}
bool VehicleWheel::is_used_as_traction() const{
return engine_traction;
}
void VehicleWheel::set_use_as_steering(bool p_enabled){
steers=p_enabled;
}
bool VehicleWheel::is_used_as_steering() const{
return steers;
}
VehicleWheel::VehicleWheel() {
steers=false;
engine_traction=false;
m_steering = real_t(0.);
//m_engineForce = real_t(0.);
m_rotation = real_t(0.);
m_deltaRotation = real_t(0.);
m_brake = real_t(0.);
m_rollInfluence = real_t(0.1);
m_suspensionRestLength = 0.15;
m_wheelRadius = 0.5;//0.28;
m_suspensionStiffness = 5.88;
m_wheelsDampingCompression = 0.83;
m_wheelsDampingRelaxation = 0.88;
m_frictionSlip = 10.5;
m_bIsFrontWheel = false;
m_maxSuspensionTravelCm = 500;
m_maxSuspensionForce = 6000;
m_suspensionRelativeVelocity=0;
m_clippedInvContactDotSuspension=1.0;
m_raycastInfo.m_isInContact=false;
body=NULL;
}
void VehicleBody::_update_wheel_transform(VehicleWheel& wheel ,PhysicsDirectBodyState *s) {
wheel.m_raycastInfo.m_isInContact = false;
Transform chassisTrans = s->get_transform();
//if (interpolatedTransform && (getRigidBody()->getMotionState()))
//{
// getRigidBody()->getMotionState()->getWorldTransform(chassisTrans);
//}
wheel.m_raycastInfo.m_hardPointWS = chassisTrans.xform( wheel.m_chassisConnectionPointCS );
//wheel.m_raycastInfo.m_hardPointWS+=s->get_linear_velocity()*s->get_step();
wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.get_basis().xform( wheel.m_wheelDirectionCS).normalized();
wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.get_basis().xform( wheel.m_wheelAxleCS ).normalized();
}
void VehicleBody::_update_wheel(int p_idx,PhysicsDirectBodyState *s) {
VehicleWheel& wheel = *wheels[p_idx];
_update_wheel_transform(wheel,s);
Vector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS;
const Vector3& right = wheel.m_raycastInfo.m_wheelAxleWS;
Vector3 fwd = up.cross(right);
fwd = fwd.normalized();
// up = right.cross(fwd);
// up.normalize();
//rotate around steering over de wheelAxleWS
real_t steering = wheel.steers?m_steeringValue:0.0;
//print_line(itos(p_idx)+": "+rtos(steering));
Matrix3 steeringMat(up,steering);
Matrix3 rotatingMat(right,-wheel.m_rotation);
// if (p_idx==1)
// print_line("steeringMat " +steeringMat);
Matrix3 basis2(
right[0],up[0],fwd[0],
right[1],up[1],fwd[1],
right[2],up[2],fwd[2]
);
wheel.m_worldTransform.set_basis(steeringMat * rotatingMat * basis2);
//wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat));
wheel.m_worldTransform.set_origin(
wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength
);
}
real_t VehicleBody::_ray_cast(int p_idx,PhysicsDirectBodyState *s) {
VehicleWheel& wheel = *wheels[p_idx];
_update_wheel_transform(wheel,s);
real_t depth = -1;
real_t raylen = wheel.m_suspensionRestLength+wheel.m_wheelRadius;
Vector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen);
Vector3 source = wheel.m_raycastInfo.m_hardPointWS;
wheel.m_raycastInfo.m_contactPointWS = source + rayvector;
const Vector3& target = wheel.m_raycastInfo.m_contactPointWS;
source-=wheel.m_wheelRadius * wheel.m_raycastInfo.m_wheelDirectionWS;
real_t param = real_t(0.);
PhysicsDirectSpaceState::RayResult rr;
PhysicsDirectSpaceState *ss=s->get_space_state();
bool col = ss->intersect_ray(source,target,rr,exclude);
wheel.m_raycastInfo.m_groundObject = 0;
if (col)
{
//print_line("WHEEL "+itos(p_idx)+" FROM "+source+" TO: "+target);
//print_line("WHEEL "+itos(p_idx)+" COLLIDE? "+itos(col));
param = source.distance_to(rr.position)/source.distance_to(target);
depth = raylen * param;
wheel.m_raycastInfo.m_contactNormalWS = rr.normal;
wheel.m_raycastInfo.m_isInContact = true;
if (rr.collider)
wheel.m_raycastInfo.m_groundObject=rr.collider->cast_to<PhysicsBody>();
real_t hitDistance = param*raylen;
wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelRadius;
//clamp on max suspension travel
real_t minSuspensionLength = wheel.m_suspensionRestLength - wheel.m_maxSuspensionTravelCm*real_t(0.01);
real_t maxSuspensionLength = wheel.m_suspensionRestLength+ wheel.m_maxSuspensionTravelCm*real_t(0.01);
if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength)
{
wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength;
}
if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength)
{
wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength;
}
wheel.m_raycastInfo.m_contactPointWS = rr.position;
real_t denominator= wheel.m_raycastInfo.m_contactNormalWS.dot( wheel.m_raycastInfo.m_wheelDirectionWS );
Vector3 chassis_velocity_at_contactPoint;
//Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition();
//chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos);
chassis_velocity_at_contactPoint = s->get_linear_velocity() +
(s->get_angular_velocity()).cross(wheel.m_raycastInfo.m_contactPointWS-s->get_transform().origin);// * mPos);
real_t projVel = wheel.m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint );
if ( denominator >= real_t(-0.1))
{
wheel.m_suspensionRelativeVelocity = real_t(0.0);
wheel.m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1);
}
else
{
real_t inv = real_t(-1.) / denominator;
wheel.m_suspensionRelativeVelocity = projVel * inv;
wheel.m_clippedInvContactDotSuspension = inv;
}
} else
{
wheel.m_raycastInfo.m_isInContact = false;
//put wheel info as in rest position
wheel.m_raycastInfo.m_suspensionLength = wheel.m_suspensionRestLength;
wheel.m_suspensionRelativeVelocity = real_t(0.0);
wheel.m_raycastInfo.m_contactNormalWS = - wheel.m_raycastInfo.m_wheelDirectionWS;
wheel.m_clippedInvContactDotSuspension = real_t(1.0);
}
return depth;
}
void VehicleBody::_update_suspension(PhysicsDirectBodyState *s)
{
real_t deltaTime = s->get_step();
real_t chassisMass = mass;
for (int w_it=0; w_it<wheels.size(); w_it++)
{
VehicleWheel& wheel_info = *wheels[w_it];
if ( wheel_info.m_raycastInfo.m_isInContact )
{
real_t force;
// Spring
{
real_t susp_length = wheel_info.m_suspensionRestLength;
real_t current_length = wheel_info.m_raycastInfo.m_suspensionLength;
real_t length_diff = (susp_length - current_length);
force = wheel_info.m_suspensionStiffness
* length_diff * wheel_info.m_clippedInvContactDotSuspension;
}
// Damper
{
real_t projected_rel_vel = wheel_info.m_suspensionRelativeVelocity;
{
real_t susp_damping;
if ( projected_rel_vel < real_t(0.0) )
{
susp_damping = wheel_info.m_wheelsDampingCompression;
}
else
{
susp_damping = wheel_info.m_wheelsDampingRelaxation;
}
force -= susp_damping * projected_rel_vel;
}
}
// RESULT
wheel_info.m_wheelsSuspensionForce = force * chassisMass;
if (wheel_info.m_wheelsSuspensionForce < real_t(0.))
{
wheel_info.m_wheelsSuspensionForce = real_t(0.);
}
}
else
{
wheel_info.m_wheelsSuspensionForce = real_t(0.0);
}
}
}
//bilateral constraint between two dynamic objects
void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3& pos1,
PhysicsBody* body2, const Vector3& pos2, const Vector3& normal,real_t& impulse)
{
real_t normalLenSqr = normal.length_squared();
//ERR_FAIL_COND( normalLenSqr < real_t(1.1));
if (normalLenSqr > real_t(1.1))
{
impulse = real_t(0.);
return;
}
Vector3 rel_pos1 = pos1 - s->get_transform().origin;
Vector3 rel_pos2;
if (body2)
rel_pos2 = pos2 - body2->get_global_transform().origin;
//this jacobian entry could be re-used for all iterations
Vector3 vel1 = s->get_linear_velocity() + (s->get_angular_velocity()).cross(rel_pos1);// * mPos);
Vector3 vel2;
if (body2)
vel2=body2->get_linear_velocity() + body2->get_angular_velocity().cross(rel_pos2);
Vector3 vel = vel1 - vel2;
Matrix3 b2trans;
float b2invmass=0;
Vector3 b2lv;
Vector3 b2av;
Vector3 b2invinertia; //todo
if (body2) {
b2trans = body2->get_global_transform().basis.transposed();
b2invmass = body2->get_inverse_mass();
b2lv = body2->get_linear_velocity();
b2av = body2->get_angular_velocity();
}
btVehicleJacobianEntry jac(s->get_transform().basis.transposed(),
b2trans,
rel_pos1,
rel_pos2,
normal,
s->get_inverse_inertia(),
1.0/mass,
b2invinertia,
b2invmass);
real_t jacDiagAB = jac.getDiagonal();
real_t jacDiagABInv = real_t(1.) / jacDiagAB;
real_t rel_vel = jac.getRelativeVelocity(
s->get_linear_velocity(),
s->get_transform().basis.transposed().xform(s->get_angular_velocity()),
b2lv,
b2trans.xform(b2av));
real_t a;
a=jacDiagABInv;
rel_vel = normal.dot(vel);
//todo: move this into proper structure
real_t contactDamping = real_t(0.4);
#define ONLY_USE_LINEAR_MASS
#ifdef ONLY_USE_LINEAR_MASS
real_t massTerm = real_t(1.) / ((1.0/mass) + b2invmass);
impulse = - contactDamping * rel_vel * massTerm;
#else
real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv;
impulse = velocityImpulse;
#endif
}
VehicleBody::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDirectBodyState *s,PhysicsBody* body1,const Vector3& frictionPosWorld,const Vector3& frictionDirectionWorld, real_t maxImpulse)
:m_s(s),
m_body1(body1),
m_frictionPositionWorld(frictionPosWorld),
m_frictionDirectionWorld(frictionDirectionWorld),
m_maxImpulse(maxImpulse)
{
float denom0=0;
float denom1=0;
{
Vector3 r0 = frictionPosWorld - s->get_transform().origin;
Vector3 c0 = (r0).cross(frictionDirectionWorld);
Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0);
denom0= s->get_inverse_mass() + frictionDirectionWorld.dot(vec);
}
if (body1) {
Vector3 r0 = frictionPosWorld - body1->get_global_transform().origin;
Vector3 c0 = (r0).cross(frictionDirectionWorld);
Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0);
//denom1= body1->get_inverse_mass() + frictionDirectionWorld.dot(vec);
denom1=0;
}
real_t relaxation = 1.f;
m_jacDiagABInv = relaxation/(denom0+denom1);
}
real_t VehicleBody::_calc_rolling_friction(btVehicleWheelContactPoint& contactPoint) {
real_t j1=0.f;
const Vector3& contactPosWorld = contactPoint.m_frictionPositionWorld;
Vector3 rel_pos1 = contactPosWorld - contactPoint.m_s->get_transform().origin;
Vector3 rel_pos2;
if (contactPoint.m_body1)
rel_pos2 = contactPosWorld - contactPoint.m_body1->get_global_transform().origin;
real_t maxImpulse = contactPoint.m_maxImpulse;
Vector3 vel1 = contactPoint.m_s->get_linear_velocity() + (contactPoint.m_s->get_angular_velocity()).cross(rel_pos1);// * mPos);
Vector3 vel2;
if (contactPoint.m_body1) {
vel2=contactPoint.m_body1->get_linear_velocity() + contactPoint.m_body1->get_angular_velocity().cross(rel_pos2);
}
Vector3 vel = vel1 - vel2;
real_t vrel = contactPoint.m_frictionDirectionWorld.dot(vel);
// calculate j that moves us to zero relative velocity
j1 = -vrel * contactPoint.m_jacDiagABInv;
return CLAMP(j1,-maxImpulse,maxImpulse);
}
static const real_t sideFrictionStiffness2 = real_t(1.0);
void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
//calculate the impulse, so that the wheels don't move sidewards
int numWheel = wheels.size();
if (!numWheel)
return;
m_forwardWS.resize(numWheel);
m_axle.resize(numWheel);
m_forwardImpulse.resize(numWheel);
m_sideImpulse.resize(numWheel);
int numWheelsOnGround = 0;
//collapse all those loops into one!
for (int i=0;i<wheels.size();i++)
{
VehicleWheel& wheelInfo = *wheels[i];
if (wheelInfo.m_raycastInfo.m_isInContact)
numWheelsOnGround++;
m_sideImpulse[i] = real_t(0.);
m_forwardImpulse[i] = real_t(0.);
}
{
for (int i=0;i<wheels.size();i++)
{
VehicleWheel& wheelInfo = *wheels[i];
if (wheelInfo.m_raycastInfo.m_isInContact)
{
//const btTransform& wheelTrans = getWheelTransformWS( i );
Matrix3 wheelBasis0 = wheelInfo.m_worldTransform.basis;//get_global_transform().basis;
m_axle[i] = wheelBasis0.get_axis(Vector3::AXIS_X);
//m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS;
const Vector3& surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS;
real_t proj = m_axle[i].dot(surfNormalWS);
m_axle[i] -= surfNormalWS * proj;
m_axle[i] = m_axle[i].normalized();
m_forwardWS[i] = surfNormalWS.cross(m_axle[i]);
m_forwardWS[i].normalize();
_resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS,
wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS,
m_axle[i],m_sideImpulse[i]);
m_sideImpulse[i] *= sideFrictionStiffness2;
}
}
}
real_t sideFactor = real_t(1.);
real_t fwdFactor = 0.5;
bool sliding = false;
{
for (int wheel =0;wheel <wheels.size();wheel++)
{
VehicleWheel& wheelInfo = *wheels[wheel];
//class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject;
real_t rollingFriction = 0.f;
if (wheelInfo.m_raycastInfo.m_isInContact)
{
if (engine_force != 0.f)
{
rollingFriction = engine_force* s->get_step();
} else
{
real_t defaultRollingFrictionImpulse = 0.f;
real_t maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse;
btVehicleWheelContactPoint contactPt(s,wheelInfo.m_raycastInfo.m_groundObject,wheelInfo.m_raycastInfo.m_contactPointWS,m_forwardWS[wheel],maxImpulse);
rollingFriction = _calc_rolling_friction(contactPt);
}
}
//switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break)
m_forwardImpulse[wheel] = real_t(0.);
wheelInfo.m_skidInfo= real_t(1.);
if (wheelInfo.m_raycastInfo.m_isInContact)
{
wheelInfo.m_skidInfo= real_t(1.);
real_t maximp = wheelInfo.m_wheelsSuspensionForce * s->get_step() * wheelInfo.m_frictionSlip;
real_t maximpSide = maximp;
real_t maximpSquared = maximp * maximpSide;
m_forwardImpulse[wheel] = rollingFriction;//wheelInfo.m_engineForce* timeStep;
real_t x = (m_forwardImpulse[wheel] ) * fwdFactor;
real_t y = (m_sideImpulse[wheel] ) * sideFactor;
real_t impulseSquared = (x*x + y*y);
if (impulseSquared > maximpSquared)
{
sliding = true;
real_t factor = maximp / Math::sqrt(impulseSquared);
wheelInfo.m_skidInfo *= factor;
}
}
}
}
if (sliding)
{
for (int wheel = 0;wheel < wheels.size(); wheel++)
{
if (m_sideImpulse[wheel] != real_t(0.))
{
if (wheels[wheel]->m_skidInfo< real_t(1.))
{
m_forwardImpulse[wheel] *= wheels[wheel]->m_skidInfo;
m_sideImpulse[wheel] *= wheels[wheel]->m_skidInfo;
}
}
}
}
// apply the impulses
{
for (int wheel = 0;wheel<wheels.size(); wheel++)
{
VehicleWheel& wheelInfo = *wheels[wheel];
Vector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS -
s->get_transform().origin;
if (m_forwardImpulse[wheel] != real_t(0.))
{
s->apply_impulse(rel_pos,m_forwardWS[wheel]*(m_forwardImpulse[wheel]));
}
if (m_sideImpulse[wheel] != real_t(0.))
{
PhysicsBody* groundObject = wheelInfo.m_raycastInfo.m_groundObject;
Vector3 rel_pos2;
if (groundObject) {
rel_pos2=wheelInfo.m_raycastInfo.m_contactPointWS - groundObject->get_global_transform().origin;
}
Vector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel];
#if defined ROLLING_INFLUENCE_FIX // fix. It only worked if car's up was along Y - VT.
Vector3 vChassisWorldUp = s->get_transform().basis.transposed()[1];//getRigidBody()->getCenterOfMassTransform().getBasis().getColumn(m_indexUpAxis);
rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * (1.f-wheelInfo.m_rollInfluence));
#else
rel_pos[1] *= wheelInfo.m_rollInfluence; //?
#endif
s->apply_impulse(rel_pos,sideImp);
//apply friction impulse on the ground
//todo
//groundObject->applyImpulse(-sideImp,rel_pos2);
}
}
}
}
void VehicleBody::_direct_state_changed(Object *p_state) {
PhysicsDirectBodyState *s = p_state->cast_to<PhysicsDirectBodyState>();
set_ignore_transform_notification(true);
set_global_transform(s->get_transform());
set_ignore_transform_notification(false);
float step = s->get_step();
for(int i=0;i<wheels.size();i++) {
_update_wheel(i,s);
}
for(int i=0;i<wheels.size();i++) {
_ray_cast(i,s);
wheels[i]->set_transform(s->get_transform().inverse() * wheels[i]->m_worldTransform);
}
_update_suspension(s);
for(int i=0;i<wheels.size();i++) {
//apply suspension force
VehicleWheel& wheel = *wheels[i];
real_t suspensionForce = wheel.m_wheelsSuspensionForce;
if (suspensionForce > wheel.m_maxSuspensionForce)
{
suspensionForce = wheel.m_maxSuspensionForce;
}
Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step;
Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin;
s->apply_impulse(relpos,impulse);
//getRigidBody()->applyImpulse(impulse, relpos);
}
_update_friction(s);
for (int i=0;i<wheels.size();i++)
{
VehicleWheel& wheel = *wheels[i];
Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - s->get_transform().origin;
Vector3 vel = s->get_linear_velocity() + (s->get_angular_velocity()).cross(relpos);// * mPos);
if (wheel.m_raycastInfo.m_isInContact)
{
const Transform& chassisWorldTransform = s->get_transform();
Vector3 fwd (
chassisWorldTransform.basis[0][Vector3::AXIS_Z],
chassisWorldTransform.basis[1][Vector3::AXIS_Z],
chassisWorldTransform.basis[2][Vector3::AXIS_Z]);
real_t proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS);
fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj;
real_t proj2 = fwd.dot(vel);
wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelRadius);
wheel.m_rotation += wheel.m_deltaRotation;
} else
{
wheel.m_rotation += wheel.m_deltaRotation;
}
wheel.m_deltaRotation *= real_t(0.99);//damping of rotation when not in contact
}
}
void VehicleBody::set_mass(real_t p_mass) {
mass=p_mass;
PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_MASS,mass);
}
real_t VehicleBody::get_mass() const{
return mass;
}
void VehicleBody::set_friction(real_t p_friction) {
friction=p_friction;
PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_FRICTION,friction);
}
real_t VehicleBody::get_friction() const{
return friction;
}
void VehicleBody::set_engine_force(float p_force) {
engine_force=p_force;
}
float VehicleBody::get_engine_force() const{
return engine_force;
}
void VehicleBody::set_brake(float p_brake){
brake=p_brake;
}
float VehicleBody::get_brake() const{
return brake;
}
void VehicleBody::set_steering(float p_steering){
m_steeringValue=p_steering;
}
float VehicleBody::get_steering() const{
return m_steeringValue;
}
void VehicleBody::_bind_methods(){
ObjectTypeDB::bind_method(_MD("set_mass","mass"),&VehicleBody::set_mass);
ObjectTypeDB::bind_method(_MD("get_mass"),&VehicleBody::get_mass);
ObjectTypeDB::bind_method(_MD("set_friction","friction"),&VehicleBody::set_friction);
ObjectTypeDB::bind_method(_MD("get_friction"),&VehicleBody::get_friction);
ObjectTypeDB::bind_method(_MD("set_engine_force","engine_force"),&VehicleBody::set_engine_force);
ObjectTypeDB::bind_method(_MD("get_engine_force"),&VehicleBody::get_engine_force);
ObjectTypeDB::bind_method(_MD("set_brake","brake"),&VehicleBody::set_brake);
ObjectTypeDB::bind_method(_MD("get_brake"),&VehicleBody::get_brake);
ObjectTypeDB::bind_method(_MD("set_steering","steering"),&VehicleBody::set_steering);
ObjectTypeDB::bind_method(_MD("get_steering"),&VehicleBody::get_steering);
ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&VehicleBody::_direct_state_changed);
ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/engine_force",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_engine_force"),_SCS("get_engine_force"));
ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/brake",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_brake"),_SCS("get_brake"));
ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/steering",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_steering"),_SCS("get_steering"));
ADD_PROPERTY( PropertyInfo(Variant::REAL,"body/mass",PROPERTY_HINT_RANGE,"0.01,65536,0.01"),_SCS("set_mass"),_SCS("get_mass"));
ADD_PROPERTY( PropertyInfo(Variant::REAL,"body/friction",PROPERTY_HINT_RANGE,"0.01,1,0.01"),_SCS("set_friction"),_SCS("get_friction"));
}
VehicleBody::VehicleBody() : PhysicsBody(PhysicsServer::BODY_MODE_RIGID) {
m_pitchControl=0;
m_currentVehicleSpeedKmHour = real_t(0.);
m_steeringValue = real_t(0.);
engine_force=0;
brake=0;
friction=1;
ccd=false;
exclude.insert(get_rid());
PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed");
set_mass(40);
}