Beispiel #1
0
void SimAxleUpdate(tCar *car, int index)
{
	tAxle *axle = &(car->axle[index]);
	tdble str, stl, sgn, vtl, vtr;
	
	str = car->wheel[index*2].susp.x;
	stl = car->wheel[index*2+1].susp.x;
	vtr = car->wheel[index*2].susp.v;
	vtl = car->wheel[index*2+1].susp.v;
	
	sgn = (tdble) (SIGN(stl - str));
	axle->arbSusp.x = fabs(stl - str);		
	tSpring *spring = &(axle->arbSusp.spring);

	// To save CPU power we compute the force here directly. Just the spring
	// is considered.
	tdble f;
	f = spring->K * axle->arbSusp.x;
	
	// right
	car->wheel[index*2].axleFz =  + sgn * f;
	// left
	car->wheel[index*2+1].axleFz = - sgn * f;
	
	/* heave/center spring */
	axle->heaveSusp.x = (tdble) (0.5 * (stl + str));
	axle->heaveSusp.v = (tdble) (0.5 * (vtl + vtr));
	SimSuspUpdate(&(axle->heaveSusp));
	f = (tdble) (0.5 * axle->heaveSusp.force);
	car->wheel[index*2].axleFz3rd = f;
	car->wheel[index*2+1].axleFz3rd = f;
}
Beispiel #2
0
void SimWheelUpdateForce(tCar *car, int index)
{
	tWheel *wheel = &(car->wheel[index]);
	tdble axleFz = wheel->axleFz;
	tdble vt, v, v2, wrl; // wheel related velocity
	tdble Fn, Ft;
	tdble waz;
	tdble CosA, SinA;
	tdble s, sa, sx, sy; // slip vector
	tdble stmp, F, Bx;
	tdble mu;
	tdble reaction_force = 0.0f;
	wheel->state = 0;

	// VERTICAL STUFF CONSIDERING SMALL PITCH AND ROLL ANGLES
	// update suspension force
	SimSuspUpdate(&(wheel->susp));

	// check suspension state
	wheel->state |= wheel->susp.state;
	if ((wheel->state & SIM_SUSP_EXT) == 0) {
		wheel->forces.z = axleFz + wheel->susp.force;
		reaction_force = wheel->forces.z;
		wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass;
		if (wheel->forces.z < 0.0f) {
			wheel->forces.z = 0.0f;
		}
	} else {
		if (wheel->rel_vel < 0.0) {
            wheel->rel_vel = 0.0;
        }
        wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass;
		wheel->forces.z = 0.0f;
	}

	// update wheel coord, center relative to GC
	wheel->relPos.z = - wheel->susp.x / wheel->susp.spring.bellcrank + wheel->radius;

	// HORIZONTAL FORCES
	waz = wheel->steer + wheel->staticPos.az;
	CosA = cos(waz);
	SinA = sin(waz);

	// tangent velocity.
	vt = wheel->bodyVel.x * CosA + wheel->bodyVel.y * SinA;
	v2 = wheel->bodyVel.x * wheel->bodyVel.x + wheel->bodyVel.y * wheel->bodyVel.y;
	v = sqrt(v2);

	// slip angle
	if (v < 0.000001f) {
		sa = 0.0f;
	} else {
		sa = atan2(wheel->bodyVel.y, wheel->bodyVel.x) - waz;
	}
	FLOAT_NORM_PI_PI(sa);

	wrl = wheel->spinVel * wheel->radius;
	if ((wheel->state & SIM_SUSP_EXT) != 0) {
		sx = sy = 0.0f;
	} else if (v < 0.000001f) {
		sx = wrl;
		sy = 0.0f;
	} else {
		sx = (vt - wrl) / fabs(vt);
		sy = sin(sa);
	}

	Ft = 0.0f;
	Fn = 0.0f;
	s = sqrt(sx*sx+sy*sy);

	{
		// calculate _skid and _reaction for sound.
		if (v < 2.0f) {
			car->carElt->_skid[index] = 0.0f;
		} else {
			car->carElt->_skid[index] =  MIN(1.0f, (s*reaction_force*0.0002f));
		}
		car->carElt->_reaction[index] = reaction_force;
	}

	stmp = MIN(s, 1.5f);

	// MAGIC FORMULA
	Bx = wheel->mfB * stmp;
	F = sin(wheel->mfC * atan(Bx * (1.0f - wheel->mfE) + wheel->mfE * atan(Bx))) * (1.0f + stmp * simSkidFactor[car->carElt->_skillLevel]);

	// load sensitivity
	mu = wheel->mu * (wheel->lfMin + (wheel->lfMax - wheel->lfMin) * exp(wheel->lfK * wheel->forces.z / wheel->opLoad));

	F *= wheel->forces.z * mu * wheel->trkPos.seg->surface->kFriction * (1.0f + 0.05f * sin(-wheel->staticPos.ax * 18.0f));	/* coeff */

	//For debug weather with some tracks
        #ifdef SD_DEBUG
                GfLogDebug("Simu v2 kFriction : %f   ", wheel->trkPos.seg->surface->kFriction);
	#endif

	wheel->rollRes = wheel->forces.z * wheel->trkPos.seg->surface->kRollRes;
    	car->carElt->priv.wheel[index].rollRes = wheel->rollRes;

	if (s > 0.000001f) {
		// wheel axis based
		Ft -= F * sx / s;
		Fn -= F * sy / s;
	}

	FLOAT_RELAXATION2(Fn, wheel->preFn, 50.0f);
	FLOAT_RELAXATION2(Ft, wheel->preFt, 50.0f);

	wheel->relPos.az = waz;

	wheel->forces.x = Ft * CosA - Fn * SinA;
	wheel->forces.y = Ft * SinA + Fn * CosA;
	wheel->spinTq = Ft * wheel->radius;
	wheel->sa = sa;
	wheel->sx = sx;

	wheel->feedBack.spinVel = wheel->spinVel;
	wheel->feedBack.Tq = wheel->spinTq;
	wheel->feedBack.brkTq = wheel->brake.Tq;

	car->carElt->_wheelSlipSide(index) = sy*v;
	car->carElt->_wheelSlipAccel(index) = sx*v;
	car->carElt->_reaction[index] = reaction_force;
}
// Note that this differs in the calculation of sx, sy, sa
// from simuv2, but is exactly the same when everything is horizontal.
// I changed the implementation so that what happens is mathematically
// clearer, though the code is not very clear :/
void
SimWheelUpdateForce(tCar *car, int index)
{
    tWheel 	*wheel = &(car->wheel[index]);
    tdble 	axleFz = wheel->axleFz;
    //    tdble 	vt, v, v2;
    tdble       wrl; /* wheel related velocity */
    tdble 	Fn, Ft, Ft2;
    tdble 	waz;
    tdble 	CosA, SinA;
    tdble	s = 0.0;
    tdble       sa, sx, sy; /* slip vector */
    tdble	stmp, F, Bx;
    tdble	mu;
    tdble       reaction_force;
    tdble f_z = 0.0;
    t3Dd angles;
    t3Dd normal;
    t3Dd rel_normal;
    bool right_way_up = true;
    static long wcnt = 0;

#ifdef USE_THICKNESS
	int seg_id = (int) ((tdble) N_THICKNESS_SEGMENTS *  (wheel->relPos.ay/(2*M_PI))) % N_THICKNESS_SEGMENTS;
	if (seg_id<0) seg_id += N_THICKNESS_SEGMENTS;
	tdble adjRadius = wheel->radius + wheel->thickness[seg_id];
#else
	tdble adjRadius = wheel->radius;
#endif	


	wheel->T_current = car->carElt->_tyreT_mid(index);
	wheel->condition = car->carElt->_tyreCondition(index);

    waz = wheel->relPos.az;//wheel->steer + wheel->staticPos.az;
    /* Get normal of road relative to the wheel's axis 
	   This should help take into account the camber.*/

	BEGIN_PROFILE(timer_coordinate_transform);
    
    //RtTrackSurfaceNormalL(&(wheel->trkPos), &normal);
	normal = wheel->normal; 

    
    // now rel_normal.x is the effective camber angle
	if (USE_QUATERNIONS==0) {
		angles.x = car->DynGCg.pos.ax + wheel->relPos.ax;
		angles.y = car->DynGCg.pos.ay;
		angles.z = car->DynGCg.pos.az + waz;
		NaiveRotate (normal, angles, &rel_normal);
	} else {
		sgQuat Q;
		sgCopyQuat (Q, car->posQuat);
		sgPreRotQuat (Q, FLOAT_RAD2DEG(wheel->relPos.ax), 1.0f, 0.0f, 0.0f);
		sgPreRotQuat (Q, FLOAT_RAD2DEG(waz), 0.0f, 0.0f, 1.0f);
		sgVec3 P = {normal.x, normal.y, normal.z};
		sgRotateVecQuat (P, Q);
		sg2t3 (P, rel_normal);
	}
    
    wheel->state = 0;
	END_PROFILE(timer_coordinate_transform);

	BEGIN_PROFILE(timer_reaction);
    Ft = 0.0;
    Fn = 0.0;
    wheel->forces.x = 0.0;
    wheel->forces.y = 0.0;
    wheel->forces.z = 0.0;

	
    /* Now uses the normal, so it should work */
    /* update suspension force */
    SimSuspUpdate(&(wheel->susp));
    /* check suspension state */
    wheel->state |= wheel->susp.state;
    reaction_force = 0.0;

    wheel->forces.z = 0;
    Ft = Fn = 0;
    reaction_force = 0.0;
    if ((wheel->state & SIM_SUSP_EXT) == 0) {
		f_z  = axleFz + wheel->susp.force;
        wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass;
		if ((f_z < 0)) {
			f_z = 0;
		}
		/* project the reaction force. Only wheel->forces.z is
		   actually interesting for friction. The rest is just
		   reaction. Now we have included the reaction from the sides
		   which is fake.
		   The suspension pushes the wheel down with f_z, but the reaction
		   of the surface is just f_z*rel_normal.z;!
		*/
		if ((right_way_up) && (rel_normal.z > MIN_NORMAL_Z)) {
			// reaction force on track z axis should be equal to
			// suspension reaction if suspension is perpendicular
			// to the track plane. We assume other reaction forces
			// proportional, but things break down when car is 
			// tilted a lot with respect to the track plane.
			tdble invrel_normal = 1.0f/rel_normal.z;
			if (invrel_normal>= 4.0) {
				invrel_normal = 4.0;
			} else if (invrel_normal<=-4.0) {
				invrel_normal = -4.0;
			}
			reaction_force = f_z; //* invrel_normal;
			// the other reactions are then:
			Ft = reaction_force*rel_normal.x;//*invrel_normal;
			Fn = reaction_force*rel_normal.y;//*invrel_normal;
		} else {
			f_z = 0;
			wheel->susp.force = 0;
			wheel->forces.z = 0;
			Ft = Fn = 0;
			reaction_force = 0.0;
		}

    } else {
		//if (wheel->rel_vel < 0.0) {
        //wheel->rel_vel = 0.0;
        //}
        wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass;
		wheel->forces.z = 0.0f;
	}

    wheel->relPos.z = - wheel->susp.x / wheel->susp.spring.bellcrank + adjRadius; /* center relative to GC */
	END_PROFILE(timer_reaction);


	BEGIN_PROFILE(timer_angles);
    /* HORIZONTAL FORCES */

    CosA = cos(waz);
    SinA = sin(waz);

    /* tangent velocity */
    // This is speed of the wheel relative to the track, so we have
    // to take the projection to the track.
    tdble rel_normal_xz = sqrt (rel_normal.z*rel_normal.z
								+ rel_normal.x*rel_normal.x);
    tdble rel_normal_yz = sqrt (rel_normal.z*rel_normal.z
								+ rel_normal.y*rel_normal.y);
    //tdble rel_normal_xy = sqrt (rel_normal.x*rel_normal.x
	//							+ rel_normal.y*rel_normal.y);


	END_PROFILE(timer_angles);

#ifndef FREE_MOVING_WHEELS
    wheel->bodyVel.z = 0.0;
#endif
    wrl = (wheel->spinVel + car->DynGC.vel.ay) * adjRadius;
    {
		// this thing here should be faster than quat?
		t3Dd angles = {wheel->relPos.ax, 0.0, waz};
		NaiveRotate (wheel->bodyVel, angles, &wheel->bodyVel);
    }

    tdble wvx = wheel->bodyVel.x * rel_normal_yz;
    tdble wvy = wheel->bodyVel.y * rel_normal_xz;
    tdble absolute_speed = sqrt(wvx*wvx + wvy*wvy);
    wvx -= wrl;
    wheel->bodyVel.x = wvx;
    wheel->bodyVel.y = wvy;

	BEGIN_PROFILE(timer_friction);
    tdble relative_speed = sqrt(wvx*wvx + wvy*wvy);
    tdble camber_gain = +0.1f;
    tdble camber_shift = camber_gain * rel_normal.x;
    if ((wheel->state & SIM_SUSP_EXT) != 0) {
	    sx = sy = sa = 0;
    } else if (absolute_speed < ABSOLUTE_SPEED_CUTOFF) {
	    sx = wvx/ABSOLUTE_SPEED_CUTOFF;
	    sy = wvy/ABSOLUTE_SPEED_CUTOFF;
	    sa = atan2(wvy, wvx);
    } else {
		// The division with absolute_speed is a bit of a hack. 
        // But the assumption is that the profile of friction
        // scales linearly with speed.
		sx = wvx/absolute_speed;
		sy = wvy/absolute_speed;
		sa = atan2(wvy, wvx);
    }
	s = sqrt(sx*sx+sy*sy);
    sa -= camber_shift;
    sx = cos(sa)*s;
    sy = sin(sa)*s;

	wcnt++;
	access_times = (float) wcnt;
	//if (index==0) {
	//wcnt--;
	//}

	if (wcnt<0) {
		//printf ("%f", reaction_force);
		if (index==3) {
			wcnt = 10;
			//printf ("#RCT\n");
		} else {
			//printf (" ");
		}
	}

    if (right_way_up) {
		if (fabs(absolute_speed) < 2.0f && fabs(wrl) < 2.0f) {
			car->carElt->_skid[index] = 0.0f;
		} else {
			car->carElt->_skid[index] =  MIN(1.0f, (s*reaction_force*0.0002f));
		}
		//0.0002*(MAX(0.2, MIN(s, 1.2)) - 0.2)*reaction_force;
		car->carElt->_reaction[index] = reaction_force;
    } else {
		car->carElt->_skid[index] = 0.0f;
		car->carElt->_reaction[index] = 0.0f;
    }
    
    stmp = MIN(s, 1.5f);

    /* MAGIC FORMULA */
    Bx = wheel->mfB * stmp;
    tdble dynamic_grip = wheel->mfT * sin(wheel->mfC * atan(Bx * (1 - wheel->mfE) + wheel->mfE * atan(Bx))) * (1.0f + stmp * simSkidFactor[car->carElt->_skillLevel]);

	//printf ("%f\n", simSkidFactor[car->carElt->_skillLevel]);

	/* load sensitivity */
    mu = wheel->mu * (wheel->lfMin + (wheel->lfMax - wheel->lfMin) * exp(wheel->lfK * reaction_force / wheel->opLoad));
    //mu = wheel->mu;
    
	tdble static_grip = wheel->condition * reaction_force * mu * wheel->trkPos.seg->surface->kFriction;
	//tdble static_grip = wheel->condition * reaction_force * mu * wheel->trkPos.seg->surface->kFriction/0.7f;

    F = dynamic_grip * static_grip;
    // This is the steering torque
	{
		tdble Bx = wheel->mfB * (sa);// + camber_shift);
        //printf ("%f %f\n", sa, camber_shift);
		car->carElt->_wheelFy(index) =  (tdble)(cos(sa)*wheel->mfT * sin(wheel->mfC * atan(Bx * (1 - wheel->mfE) + wheel->mfE * atan(Bx))) * (1.0 + stmp * simSkidFactor[car->carElt->_skillLevel]) * static_grip);
	}
	END_PROFILE(timer_friction);

	BEGIN_PROFILE(timer_temperature);
	if (car->options->tyre_temperature) {
    	// heat transfer function with air
		tdble htrf = (tdble)((0.002 + fabs(absolute_speed)*0.0005)*SimDeltaTime);
		tdble T_current = wheel->T_current;
		tdble T_operating = wheel->T_operating;
		tdble T_range = wheel->T_range;
		tdble mfT;
		// friction heat transfer
		T_current += (tdble)(0.00003*((fabs(relative_speed)+0.1*fabs(wrl))*reaction_force)*SimDeltaTime);
		T_current = (tdble)(T_current * (1.0-htrf) + htrf * 25.0);	
		tdble dist = (T_current - T_operating)/T_range;
		//mfT = 100.0f * exp(-0.5f*(dist*dist))/T_range;
		mfT = 0.85f + 3.0f * exp(-0.5f*(dist*dist))/T_range;
		if (T_current>200.0) T_current=200.0;
		wheel->mfT = mfT;
		wheel->T_current = T_current;
	
    }

    if (car->options->tyre_damage > 0.0f && s>0.01f) {
		tdble compound_melt_point = wheel->T_operating + wheel->T_range;
		tdble adherence = wheel->Ca * 500.0f; 
		tdble melt = (exp (2.0f*(wheel->T_current - compound_melt_point)/compound_melt_point)) * car->options->tyre_damage;
		tdble removal = exp (2.0f*(F - adherence)/adherence);
		tdble wheel_damage = (tdble)(0.001 * melt * relative_speed * removal / (2.0 * M_PI * wheel->radius * wheel->width * wheel->Ca));

		if (wheel_damage>0.01f) {
			wheel_damage = 0.01f;
		}
		tdble delta_dam = wheel_damage * SimDeltaTime;
		wheel->condition -= 0.5f*delta_dam;
		if (wheel->condition < 0.5f) wheel->condition = 0.5f;
    } else {
		wheel->mfT = 1.0f;
	}
	END_PROFILE(timer_temperature);

	BEGIN_PROFILE(timer_force_calculation);

    wheel->rollRes = reaction_force * wheel->trkPos.seg->surface->kRollRes;
    car->carElt->priv.wheel[index].rollRes = wheel->rollRes;

    // Calculate friction forces
    Ft2 = 0.0f;
    tdble Fn2 = 0.0f;
	tdble epsilon = 0.00001f;
	if (s > epsilon) {
		/* wheel axis based - no control after an angle*/
		if (rel_normal.z > MIN_NORMAL_Z) {
			// When the tyre is tilted there is less surface
			// touching the road. Modelling effect simply with rel_normal_xz.
			// Constant 1.05f for equality with simuv2.
			tdble sur_f = 1.05f * rel_normal_xz;
			sur_f = 1.0;
			Ft2 = - sur_f*F*sx/s;
			Fn2 = - sur_f*F*sy/s;
		} else {
			Ft2 = 0.0f;
			Fn2 = 0.0f;
		}
        //Ft2 -= camber_shift*F;
    } else {
		tdble sur_f = rel_normal_xz;
		Ft2 = - sur_f*F*sx/epsilon;
		Fn2 = - sur_f*F*sy/epsilon;
	}

    Ft2 -= tanh(wvx) * fabs(wheel->rollRes);
    Fn2 -= tanh(wvy) * fabs(wheel->rollRes);
	wheel->forces.x = Ft2 * rel_normal_yz;
	wheel->forces.y = Fn2 * rel_normal_xz; 
	wheel->forces.z = Ft2 * rel_normal.x + Fn2 * rel_normal.y;

	END_PROFILE(timer_force_calculation);
	
	if (0) {
		// EXPERIMENTAL code - estimate amount of mass linked to this
		// wheel. Maybe useful for adjusting the slope of the 
		// static friction function. Currently not used.
		tdble Ftot = sqrt(Ft2*Ft2 + Fn2*Fn2);
		tdble ds = wheel->s_old-s;
		tdble EF = wheel->Em * ds;
		tdble dF = wheel->F_old - EF;
		wheel->Em += (float)(0.1 * dF*ds);
		wheel->F_old = Ftot;
		wheel->s_old = s;
	}

    wheel->relPos.az = waz;
    if (rel_normal.z > MIN_NORMAL_Z) {
		right_way_up = true;
    } else {
		right_way_up = false;
    }

    if (car->collide_timer < 0.00) {
        right_way_up = false;
    }

    if (right_way_up==false) {
		Fn = 0.0f;
		Ft = 0.0f;
		wheel->forces.x = 0.0f;
		wheel->forces.y = 0.0f;
		wheel->forces.z = 0.0f;
		Ft2 = 0.0f;
		wheel->spinTq = 0.0f;
    } else {
		BEGIN_PROFILE (timer_wheel_to_car);
		t3Dd f;
		// send friction and reaction forces to the car
		// normally we would not need to add f.z here, as that
		// would be purely coming from the suspension. However
		// that would only be the case if the wheels were really
		// independent objects. Right now their position is determined
		// in update ride, so we have no choice but to transmit the
		// suspension-parallel friction forces magically to the car.
		f.x = wheel->forces.x;
		f.y = wheel->forces.y;
		f.z = wheel->forces.z;

		// TODO: Check whether this is correct.
		angles.x = wheel->relPos.ax + asin(rel_normal.x);
		angles.y = asin(rel_normal.y);
		angles.z = waz;
		NaiveInverseRotate (f, angles, &wheel->forces);
		// transmit reaction forces to the car	
		wheel->forces.x +=(Ft* CosA - Fn * SinA);
		wheel->forces.y +=(Ft* SinA + Fn * CosA);

		//RELAXATION2(wheel->forces.x, wheel->preFn, 50.0f);
		//RELAXATION2(wheel->forces.y, wheel->preFt, 50.0f);

		wheel->forces.z = f_z + wheel->bump_force; // only suspension acts on z axis.
		car->carElt->_wheelFx(index) = wheel->forces.x;
		car->carElt->_wheelFz(index) = wheel->forces.z;

		wheel->spinTq = (Ft2  + tanh(wrl)*fabs(wheel->rollRes))* adjRadius;
		wheel->sa = sa;
		wheel->sx = sx;
		END_PROFILE (timer_wheel_to_car);
    }
    wheel->feedBack.spinVel = wheel->spinVel;
    wheel->feedBack.Tq = wheel->spinTq;
    wheel->feedBack.brkTq = wheel->brake.Tq;
	car->carElt->_tyreT_mid(index) = wheel->T_current;
	car->carElt->_tyreCondition(index) = wheel->condition;
	car->carElt->_wheelSlipSide(index) = wvy;
	car->carElt->_wheelSlipAccel(index) = wvx;

}
Beispiel #4
0
void
SimWheelUpdateForce(tCar *car, int index)
{
	tWheel *wheel = &(car->wheel[index]);
	tdble axleFz = wheel->axleFz;
	tdble vt, v, v2, wrl; // wheel related velocity
	tdble Fn, Ft;
	tdble waz;
	tdble CosA, SinA;
	tdble s, sa, sx, sy; // slip vector
	tdble stmp, F, Bx;
	tdble mu;
	tdble reaction_force = 0.0f;
	wheel->state = 0;

	// VERTICAL STUFF CONSIDERING SMALL PITCH AND ROLL ANGLES
	// update suspension force
	SimSuspUpdate(&(wheel->susp));

	// check suspension state
	wheel->state |= wheel->susp.state;
	if ((wheel->state & SIM_SUSP_EXT) == 0) {
		wheel->forces.z = axleFz + wheel->susp.force;
		reaction_force = wheel->forces.z;
        wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass;
		if (wheel->forces.z < 0.0f) {
			wheel->forces.z = 0.0f;
		}
	} else {
        if (wheel->rel_vel < 0.0) {
            wheel->rel_vel = 0.0;
        }
        wheel->rel_vel -= SimDeltaTime * wheel->susp.force / wheel->mass;
		wheel->forces.z = 0.0f;
	}

	// update wheel coord, center relative to GC
	wheel->relPos.z = - wheel->susp.x / wheel->susp.spring.bellcrank + wheel->radius;

	// HORIZONTAL FORCES
	waz = wheel->steer + wheel->staticPos.az;
	CosA = cos(waz);
	SinA = sin(waz);

	// tangent velocity.
	vt = wheel->bodyVel.x * CosA + wheel->bodyVel.y * SinA;
	v2 = wheel->bodyVel.x * wheel->bodyVel.x + wheel->bodyVel.y * wheel->bodyVel.y;
	v = sqrt(v2);

	// slip angle
	if (v < 0.000001f) {
		sa = 0.0f;
	} else {
		sa = atan2(wheel->bodyVel.y, wheel->bodyVel.x) - waz;
	}
	NORM_PI_PI(sa);

	wrl = (wheel->spinVel + car->DynGC.vel.ay) * wheel->radius;
	if ((wheel->state & SIM_SUSP_EXT) != 0) {
		sx = sy = 0.0f;
	} else if (v < 0.000001f) {
		sx = wrl;
		sy = 0.0f;
	} else {
		sx = (vt - wrl) / v; /* target */
		// TODO
		// Commented out and reset because sometimes robots apply full throttle and the engine does not rev up
		// nor do the wheels move. I'm not sure if that is the cause, but its actually the only visible candidate
		// from simuv2 in 1.2.2 up to the current version...
		//sx = (vt - wrl) / fabs(vt);
		sy = sin(sa);
	}

	Ft = 0.0f;
	Fn = 0.0f;
	s = sqrt(sx*sx+sy*sy);

	{
		// calculate _skid and _reaction for sound.
		if (fabs(v) < 2.0f && fabs(wrl) < 2.0f) {
			car->carElt->_skid[index] = 0.0f;
		} else {
			car->carElt->_skid[index] =  MIN(1.0f, (s*reaction_force*0.0002f));
		}
		car->carElt->_reaction[index] = reaction_force;
	}

	stmp = MIN(s, 1.5f);

	// MAGIC FORMULA
	Bx = wheel->mfB * stmp;
	F = sin(wheel->mfC * atan(Bx * (1.0f - wheel->mfE) + wheel->mfE * atan(Bx))) * (1.0f + stmp * simSkidFactor[car->carElt->_skillLevel]);

	// load sensitivity
	mu = wheel->mu * (wheel->lfMin + (wheel->lfMax - wheel->lfMin) * exp(wheel->lfK * wheel->forces.z / wheel->opLoad));

	F *= wheel->forces.z * mu * wheel->trkPos.seg->surface->kFriction * (1.0f + 0.05f * sin(-wheel->staticPos.ax * 18.0f));	/* coeff */

	wheel->rollRes = wheel->forces.z * wheel->trkPos.seg->surface->kRollRes;
    car->carElt->priv.wheel[index].rollRes = wheel->rollRes;

	if (s > 0.000001f) {
		// wheel axis based
		Ft -= F * sx / s;
		Fn -= F * sy / s;
	}

	RELAXATION2(Fn, wheel->preFn, 50.0f);
	RELAXATION2(Ft, wheel->preFt, 50.0f);

	wheel->relPos.az = waz;

	wheel->forces.x = Ft * CosA - Fn * SinA;
	wheel->forces.y = Ft * SinA + Fn * CosA;
	wheel->spinTq = Ft * wheel->radius;
	wheel->sa = sa;
	wheel->sx = sx;

	wheel->feedBack.spinVel = wheel->spinVel;
	wheel->feedBack.Tq = wheel->spinTq;
	wheel->feedBack.brkTq = wheel->brake.Tq;

	car->carElt->_wheelSlipSide(index) = sy*v;
	car->carElt->_wheelSlipAccel(index) = sx*v;
	car->carElt->_reaction[index] = reaction_force;
}