diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp index 1e0b33de..79311aad 100644 --- a/src/entities/Physical.cpp +++ b/src/entities/Physical.cpp @@ -590,7 +590,12 @@ CPhysical::ApplyAirResistance(void) }else if(GetStatus() != STATUS_GHOST){ float f = Pow(1.0f/Abs(1.0f + m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr()), CTimer::GetTimeStep()); m_vecMoveSpeed *= f; +#ifdef FIX_BUGS + // Fix too much friction at high FPS (evil cause of bad vehicle handling, rear tires unable to lose traction, etc!) + m_vecTurnSpeed *= Pow(0.99f, CTimer::GetTimeStepFix()); +#else m_vecTurnSpeed *= 0.99f; +#endif } } @@ -1071,6 +1076,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint) if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; +#ifdef FIX_BUGS + impulseA *= CTimer::GetTimeStepFix(); + impulseB *= CTimer::GetTimeStepFix(); +#endif impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; #ifdef FIX_BUGS @@ -1107,6 +1116,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint) if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; impulseB = (speedSum - fOtherSpeedB) * massB; +#ifdef FIX_BUGS + impulseA *= CTimer::GetTimeStepFix(); + impulseB *= CTimer::GetTimeStepFix(); +#endif impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit; @@ -1140,6 +1153,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint) if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * massA; impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; +#ifdef FIX_BUGS + impulseA *= CTimer::GetTimeStepFix(); + impulseB *= CTimer::GetTimeStepFix(); +#endif impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit; @@ -1174,6 +1191,10 @@ CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint) if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * massA; impulseB = (speedSum - fOtherSpeedB) * massB; +#ifdef FIX_BUGS + impulseA *= CTimer::GetTimeStepFix(); + impulseB *= CTimer::GetTimeStepFix(); +#endif impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit; @@ -1214,6 +1235,9 @@ CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint) // not really impulse but speed // maybe use ApplyFrictionMoveForce instead? fImpulse = -fOtherSpeed; +#ifdef FIX_BUGS + fImpulse *= CTimer::GetTimeStepFix(); +#endif impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass; if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; CVector vImpulse = frictionDir*fImpulse; @@ -1235,6 +1259,9 @@ CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint) frictionDir = vOtherSpeed * (1.0f/fOtherSpeed); #endif fImpulse = -fOtherSpeed * m_fMass; +#ifdef FIX_BUGS + fImpulse *= CTimer::GetTimeStepFix(); +#endif impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5; if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; ApplyFrictionMoveForce(frictionDir*fImpulse); diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp index 5e2b0c10..0a577c0c 100644 --- a/src/vehicles/Automobile.cpp +++ b/src/vehicles/Automobile.cpp @@ -857,8 +857,12 @@ CAutomobile::ProcessControl(void) (m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f)) ApplyTurnForce(-GRAVITY*Min(m_fTurnMass, 2500.0f)*GetUp(), -1.0f*GetForward()); } - +#ifdef FIX_BUGS + // Keep brake non-timestepped (so the later ProcessWheel() takes only non-timestepped inputs) + brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetDefaultTimeStep(); +#else brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); +#endif bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING); float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; float brakeBiasRear = neutralHandling ? 1.0f : 2.0f-pHandling->fBrakeBias; // looks like a bug, but it was correct in III... @@ -1058,12 +1062,7 @@ CAutomobile::ProcessControl(void) float rearBrake = brake; float rearTraction = traction; if(bIsHandbrakeOn){ -#ifdef FIX_BUGS - // Not sure if this is needed, but brake usually has timestep as a factor - rearBrake = 20000.0f * CTimer::GetTimeStepFix(); -#else rearBrake = 20000.0f; -#endif if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){ m_fTireTemperature += 0.005*CTimer::GetTimeStep(); if(m_fTireTemperature > 2.0f) @@ -1072,8 +1071,11 @@ CAutomobile::ProcessControl(void) }else if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)){ rearBrake = 0.0f; rearTraction = 0.0f; - // BUG: missing timestep +#ifdef FIX_BUGS + ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStepFix()); +#else ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()); +#endif }else if(m_fTireTemperature > 1.0f){ rearTraction *= m_fTireTemperature; } diff --git a/src/vehicles/Transmission.cpp b/src/vehicles/Transmission.cpp index 1dff2b30..9ad00a09 100644 --- a/src/vehicles/Transmission.cpp +++ b/src/vehicles/Transmission.cpp @@ -125,7 +125,12 @@ cTransmission::CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, fl float targetVelocity = Gears[gear].fMaxVelocity*speedMul*fCheat; float accel = (targetVelocity - fVelocity) * (fEngineAcceleration*accelMul) / Abs(targetVelocity); if(Abs(fVelocity) < Abs(Gears[gear].fMaxVelocity*fCheat)) +#ifdef FIX_BUGS + // The acceleration provided by a transmission+engine should not depend on framelength. + fAcceleration = gasPedal * accel; +#else fAcceleration = gasPedal * accel * CTimer::GetTimeStep(); +#endif else fAcceleration = 0.0f; return fAcceleration; diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp index d891e1e4..6dcca5e7 100644 --- a/src/vehicles/Vehicle.cpp +++ b/src/vehicles/Vehicle.cpp @@ -788,7 +788,14 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon bAlreadySkidding = false; #endif - // how much force we want to apply in these axes + // Velocity impulses fwd and right. Units are like meters per second. + // This function was written assuming a fixed FPS, so a correction is made + // right at the end before actually applying these impulses. + // + // Note that many functions in this engine deal with "force impulses" rather + // than "velocity impulses". They are directly related: F=ma. It is + // possible that the original devs actually used force impulses here but + // an optimising compiler re-arranged their maths. float fwd = 0.0f; float right = 0.0f; @@ -804,7 +811,12 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon bAlreadySkidding = true; *wheelState = WHEEL_STATE_NORMAL; +#ifdef FIX_BUGS + // Everything else here is timestep independent, let's stay with this theme to keep the rest of the FPS bugfixes simpler. + adhesion *= CTimer::GetDefaultTimeStep(); +#else adhesion *= CTimer::GetTimeStep(); +#endif if(bAlreadySkidding) adhesion *= pHandling->fTractionLoss; @@ -812,28 +824,17 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon if(contactSpeedRight != 0.0f){ // exert opposing force right = -contactSpeedRight/wheelsOnGround; - // BUG? - // contactSpeedRight is independent of framerate but right has timestep as a factor - // so we probably have to fix this - // fixing this causes jittery cars at 15fps, and causes the car to move backwards slowly at 18fps - // at 19fps, the effects are gone ... - //right *= CTimer::GetTimeStepFix(); if(wheelStatus == WHEEL_STATUS_BURST){ float fwdspeed = Min(contactSpeedFwd, fBurstSpeedMax); -#ifdef FIX_BUGS - // Keep the effect running at the same frequency even when the game is at high FPS - right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstTyreMod, fBurstTyreMod) * CTimer::GetLogicalFramesPassed(); -#else right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstTyreMod, fBurstTyreMod); -#endif } } if(bDriving){ fwd = thrust; - // limit sideways force (why?) + // Limit sideways forces applied by the tires to the max the tires can possibly do. if(right > 0.0f){ if(right > adhesion) right = adhesion; @@ -843,11 +844,6 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon } }else if(contactSpeedFwd != 0.0f){ fwd = -contactSpeedFwd/wheelsOnGround; -#ifdef FIX_BUGS - // contactSpeedFwd is independent of framerate but fwd has timestep as a factor - // so we probably have to fix this - fwd *= CTimer::GetTimeStepFix(); -#endif if(!bBraking){ if(m_fGasPedal < 0.01f){ @@ -859,9 +855,6 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass; else brake = mod_HandlingManager.fWheelFriction / pHandling->fMass; -#ifdef FIX_BUGS - brake *= CTimer::GetTimeStepFix(); -#endif } } @@ -888,10 +881,10 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon *wheelState = WHEEL_STATE_SKIDDING; } - float l = Sqrt(speedSq); + float speed = Sqrt(speedSq); float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss; - right *= adhesion * tractionLoss / l; - fwd *= adhesion * tractionLoss / l; + right *= adhesion * tractionLoss / speed; + fwd *= adhesion * tractionLoss / speed; } if(fwd != 0.0f || right != 0.0f){ @@ -923,9 +916,19 @@ CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelCon else turnDirection = direction; + + // Curious: there is a perfectly good ApplyMoveSpeed() function that + // takes "velocity impulses", but instead we use the ApplyMoveForce() + // function which takes a "force impulse" instead and then fix up the + // difference by multiplying in the vehicle mass (F=ma). Possibly + // evidence that an optimising compiler re-arranged the arithmetic? float impulse = speed*m_fMass; float turnImpulse = turnSpeed*GetMass(wheelContactPoint, turnDirection); +#ifdef FIX_BUGS + impulse = impulse * CTimer::GetTimeStepFix(); + turnImpulse = turnImpulse * CTimer::GetTimeStepFix(); +#endif ApplyMoveForce(impulse * direction); ApplyTurnForce(turnImpulse * turnDirection, wheelContactPoint); } @@ -949,7 +952,14 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee bAlreadySkidding = false; #endif - // how much force we want to apply in these axes + // Velocity impulses fwd and right. Units are like meters per second. + // This function was written assuming a fixed FPS, so a correction is made + // right at the end before actually applying these impulses. + // + // Note that many functions in this engine deal with "force impulses" rather + // than "velocity impulses". They are directly related: F=ma. It is + // possible that the original devs actually used force impulses here but + // an optimising compiler re-arranged their maths. float fwd = 0.0f; float right = 0.0f; @@ -966,7 +976,12 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee bAlreadySkidding = true; *wheelState = WHEEL_STATE_NORMAL; +#ifdef FIX_BUGS + // Everything else here is timestep independent, let's stay with this theme to keep the rest of the FPS bugfixes simpler. + adhesion *= CTimer::GetDefaultTimeStep(); +#else adhesion *= CTimer::GetTimeStep(); +#endif if(bAlreadySkidding) adhesion *= pHandling->fTractionLoss; @@ -979,20 +994,10 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee if(contactSpeedRight != 0.0f){ // exert opposing force right = -contactSpeedRight/wheelsOnGround; -#ifdef FIX_BUGS - // contactSpeedRight is independent of framerate but right has timestep as a factor - // so we probably have to fix this - right *= CTimer::GetTimeStepFix(); -#endif if(wheelStatus == WHEEL_STATUS_BURST){ float fwdspeed = Min(contactSpeedFwd, fBurstBikeSpeedMax); -#ifdef FIX_BUGS - // Keep the effect running at the same frequency even when the game is at high FPS - right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod) * CTimer::GetLogicalFramesPassed(); -#else right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod); -#endif } } @@ -1009,12 +1014,6 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee } }else if(contactSpeedFwd != 0.0f){ fwd = -contactSpeedFwd/wheelsOnGround; -#ifdef FIX_BUGS - // contactSpeedFwd is independent of framerate but fwd has timestep as a factor - // so we probably have to fix this - fwd *= CTimer::GetTimeStepFix(); -#endif - if(!bBraking){ if(m_fGasPedal < 0.01f){ if(IsBike()) @@ -1025,9 +1024,6 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass; else brake = mod_HandlingManager.fWheelFriction / m_fMass; -#ifdef FIX_BUGS - brake *= CTimer::GetTimeStepFix(); -#endif } } @@ -1078,6 +1074,10 @@ CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &whee float impulse = speed*m_fMass; float turnImpulse = speed*GetMass(wheelContactPoint, direction); +#ifdef FIX_BUGS + impulse = impulse * CTimer::GetTimeStepFix(); + turnImpulse = turnImpulse * CTimer::GetTimeStepFix(); +#endif CVector vTurnImpulse = turnImpulse * direction; ApplyMoveForce(impulse * direction);