Fix many boat performance issues at high FPS, but cornering/handling still not perfect.

This commit is contained in:
Veyrdite 2021-07-24 21:49:34 +10:00
parent e0dfa8b971
commit 6f8ba21552
2 changed files with 71 additions and 14 deletions

View File

@ -343,6 +343,7 @@ CBoat::ProcessControl(void)
bIsDrowning = false;
}
// Apply buoyancy impulse the first time (why twice?)
m_fVolumeUnderWater = mod_Buoyancy.m_volumeUnderWater;
m_vecBuoyancePoint = buoyancePoint;
if(GetModelIndex() == MI_SKIMMER && GetUp().z < -0.5f && Abs(m_vecMoveSpeed.x) < 0.2f && Abs(m_vecMoveSpeed.y) < 0.2f)
@ -352,13 +353,17 @@ CBoat::ProcessControl(void)
if(bSeparateTurnForce)
ApplyTurnForce(0.4f*buoyanceImpulse, buoyancePoint);
// TODO: what is this?
if(GetModelIndex() == MI_SKIMMER)
if(m_skimmerThingTimer != 0.0f ||
GetForward().z < -0.5f && GetUp().z > -0.5f && m_vecMoveSpeed.z < -0.15f &&
buoyanceImpulse.z > 0.01f*m_fMass * GRAVITY*CTimer::GetTimeStep() &&
buoyanceImpulse.z < 0.4f*m_fMass * GRAVITY*CTimer::GetTimeStep()){
#ifdef FIX_BUGS
// buoyanceImpulse already has a factor of timestep in it
float turnImpulse = -0.00017f*GetForward().z*buoyanceImpulse.z * m_fMass*CTimer::GetDefaultTimeStep();
#else
float turnImpulse = -0.00017f*GetForward().z*buoyanceImpulse.z * m_fMass*CTimer::GetTimeStep();
#endif
ApplyTurnForce(turnImpulse*GetForward(), GetUp());
bBoatInWater = false;
//BUG? aren't we forgetting the timestep here?
@ -370,15 +375,21 @@ CBoat::ProcessControl(void)
m_skimmerThingTimer = 0.0f;
}
// Apply buoyancy impulse the second time (why twice?)
if(!onLand && bBoatInWater && GetUp().z > 0.0f){
#ifdef FIX_BUGS
// buoyanceImpulse already has a factor of timestep in it
float impulse = m_vecMoveSpeed.MagnitudeSqr()*pBoatHandling->fAqPlaneForce*buoyanceImpulse.z*CTimer::GetDefaultTimeStep()*0.5f;
#else
float impulse = m_vecMoveSpeed.MagnitudeSqr()*pBoatHandling->fAqPlaneForce*buoyanceImpulse.z*CTimer::GetTimeStep()*0.5f;
#endif
if(GetModelIndex() == MI_SKIMMER)
impulse *= 1.0f + m_fGasPedal;
else if(m_fGasPedal > 0.05f)
impulse *= m_fGasPedal;
else
impulse = 0.0f;
impulse = Min(impulse, GRAVITY*pBoatHandling->fAqPlaneLimit*m_fMass*CTimer::GetTimeStep());
impulse = Min(impulse, GRAVITY*pBoatHandling->fAqPlaneLimit*m_fMass*CTimer::GetTimeStep()); // Both sides have a factor of TimeStep, therefore this Min() is not a problem at high FPS
ApplyMoveForce(impulse*GetUp());
ApplyTurnForce(impulse*GetUp(), buoyancePoint - pBoatHandling->fAqPlaneOffset*GetForward());
}
@ -491,7 +502,11 @@ CBoat::ProcessControl(void)
CVector propellerForce = propellerDepth * Multiply3x3(GetMatrix(), force*CVector(-steerSin, 0.0f, 0.0f));
ApplyMoveForce(propellerForce * CTimer::GetTimeStep());
ApplyTurnForce(propellerForce * CTimer::GetTimeStep(), propeller);
#ifdef FIX_BUGS
float rightForce = -steerSin * force * 0.75f/steerFactor * CTimer::GetTimeStep();
#else
float rightForce = -steerSin * force * 0.75f/steerFactor * Max(CTimer::GetTimeStep(), 0.01f);
#endif
ApplyTurnForce(GetRight() * rightForce, GetUp());
}
}else
@ -501,7 +516,12 @@ CBoat::ProcessControl(void)
CVector right = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f));
float rightSpeed = DotProduct(m_vecMoveSpeed, right);
float impulse = 0.1f*pHandling->fSuspensionBias * m_fMass * m_fVolumeUnderWater * rightSpeed * CTimer::GetTimeStep();
ApplyMoveForce(right - impulse * 0.3f * CVector(-right.y, right.x, 0.0f));
#ifdef FIX_BUGS
// Fix boat perf at high FPS: ensure both terms have a (correct) factor of timestep before doing subtraction
ApplyMoveForce(right * CTimer::GetTimeStepFix() - impulse * 0.3f * CVector(-right.y, right.x, 0.0f));
#else
ApplyMoveForce(right - impulse * 0.3f * CVector(-right.y, right.x, 0.0f));
#endif
}
if(GetStatus() == STATUS_PLAYER && CPad::GetPad(0)->GetHandBrake()){
@ -519,25 +539,45 @@ CBoat::ProcessControl(void)
m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MAX_Y-100.0f))*0.01f); // north
m_vecMoveSpeed.y = Max(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MIN_Y+100.0f))*0.01f); // south
// Apply water resistance to linear movement
if(!onLand && bBoatInWater && !bSeparateTurnForce)
ApplyWaterResistance();
// Apply water resistance to rotation
if((GetModelIndex() != MI_SKIMMER || m_skimmerThingTimer == 0.0f) && !bSeparateTurnForce){
// No idea what exactly is going on here besides drag in YZ
#ifdef FIX_BUGS
// Rockstar's attempts to make this framerate independent are totally wrong.
// Rules of thumb:
// - use Pow(x,time) if you multiply the result into the velocity
// - use x*time if you add the result into the velocity
// We have to disable one of these Pow() methods and then add our own correction at the end.
float fxfake = Pow(pBoatHandling->vecTurnRes.x, CTimer::GetDefaultTimeStep());
float fy = Pow(pBoatHandling->vecTurnRes.y, CTimer::GetTimeStep());
float fz = Pow(pBoatHandling->vecTurnRes.z, CTimer::GetTimeStep());
m_vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); // invert - to local space
float drag = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fxfake;
m_vecTurnSpeed.y *= fy;
m_vecTurnSpeed.z *= fz;
float forceUp = -(1.0f - drag) * m_vecTurnSpeed.x * m_fTurnMass;
m_vecTurnSpeed = Multiply3x3(GetMatrix(), m_vecTurnSpeed); // back to world
CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass);
ApplyTurnForce(forceUp*GetUp() * CTimer::GetTimeStepFix(), com + GetForward());
#else
float fx = Pow(pBoatHandling->vecTurnRes.x, CTimer::GetTimeStep());
float fy = Pow(pBoatHandling->vecTurnRes.y, CTimer::GetTimeStep());
float fz = Pow(pBoatHandling->vecTurnRes.z, CTimer::GetTimeStep());
m_vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); // invert - to local space
// TODO: figure this out
float magic = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fx;
float drag = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fx;
m_vecTurnSpeed.y *= fy;
m_vecTurnSpeed.z *= fz;
float forceUp = (magic - 1.0f) * m_vecTurnSpeed.x * m_fTurnMass;
float forceUp = (drag - 1.0f) * m_vecTurnSpeed.x * m_fTurnMass;
m_vecTurnSpeed = Multiply3x3(GetMatrix(), m_vecTurnSpeed); // back to world
CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass);
ApplyTurnForce(forceUp*GetUp(), com + GetForward());
#endif
}
m_nDeltaVolumeUnderWater = (m_fVolumeUnderWater-m_fPrevVolumeUnderWater)*10000;
// Falling into water
@ -555,6 +595,9 @@ CBoat::ProcessControl(void)
speedFwd *= -m_nDeltaVolumeUnderWater * 0.01f * pHandling->fBrakeBias;
CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp);
CVector splashImpulse = speed * m_fMass;
#ifdef FIX_BUGS
splashImpulse *= CTimer::GetTimeStepFix();
#endif
ApplyMoveForce(splashImpulse);
ApplyTurnForce(splashImpulse, buoyancePoint);
}
@ -893,26 +936,29 @@ CBoat::ApplyWaterResistance(void)
//
// We don't actually have to limit ourselves to a fixed time unit (like 1ms chunks),
// instead we can raise the resistance to some power of time using Pow().
float rrx = Pow(rx, 0.5f*CTimer::GetTimeStep()); // Why 0.5f? Taste?
float rry = Pow(ry, 0.5f*CTimer::GetTimeStep());
float rrz = Pow(rz, 0.5f*CTimer::GetTimeStep());
float rrx = Pow(rx, 0.5f*CTimer::GetTimeStep()); // Why 0.5f? Taste?
float rry = Pow(ry, 0.5f*CTimer::GetTimeStep());
float rrz = Pow(rz, 0.5f*CTimer::GetTimeStep());
float rryfake = Pow(ry, 0.5f*CTimer::GetDefaultTimeStep()); // Fudge factor needed for other equations to make sense at high FPS
m_vecMoveSpeed = Multiply3x3(m_vecMoveSpeed, GetMatrix()); // convert velocities to boat-local space (y = boat forwards, x = sideways, z = up/down)
m_vecMoveSpeed.x *= rrx;
m_vecMoveSpeed.y *= rry;
m_vecMoveSpeed.z *= rrz;
float force = (rry - 1.0f) * m_vecMoveSpeed.y * m_fMass;
float force = (rryfake - 1.0f) * m_vecMoveSpeed.y * m_fMass;
m_vecMoveSpeed = Multiply3x3(GetMatrix(), m_vecMoveSpeed); // back to world space
// Is this for "flipping the boat over"? Seems to have almost zero effect normally?
ApplyTurnForce(force*GetForward(), -GetUp());
// WTH is this for?
ApplyTurnForce(force*GetForward()*CTimer::GetTimeStepFix(), -GetUp());
// What the hell? Why arbitrarily compound in one more factor of rrz?
// This is framerate dependent! Bah!
// This is framerate dependent! Bah! I suspect it has very little effect.
/*
if(m_vecMoveSpeed.z > 0.0f)
m_vecMoveSpeed.z *= rrz;
else
m_vecMoveSpeed.z *= (1.0f - rrz)*0.5f + rrz;
*/
#else
// TODO: figure out how this works
float resistance = 0.001f * pHandling->fSuspensionForceLevel * SQR(m_fVolumeUnderWater) * m_fMass;

View File

@ -109,6 +109,17 @@ cBuoyancy::ProcessBuoyancyBoat(CVehicle *veh, float buoyancy, CVector *point, CV
float volume = SimpleSumBuoyancyData(waterLevel, waterPosition);
float upImpulse = volume * volDiv * buoyancy * CTimer::GetTimeStep();
CVector speed = veh->GetSpeed(Multiply3x3(veh->GetMatrix(), CVector(x, y, 0.0f)));
#ifdef FIX_BUGS
// "GetSpeed" seems to depend on framerate (bad).
// This ruins boat performance at high FPS. Approximate sequence of events:
// - "speed" goes tiny at high FPS
// - "damp" goes high as a result
// - high dampening reduces the buoyancy forces that keep the boat above the water
// - boats sit _slightly_ lower in water (you have to disable ALL waves to see this, it's a very small change)
// - all of boat handling performance changes because a different amount of the boat is underwater
// Finding this was a PITA!
speed /= CTimer::GetTimeStepFix();
#endif
float damp = 1.0f - DotProduct(speed, waterNormal)*veh->pHandling->fSuspensionDampingLevel;
float finalImpulse = upImpulse*Max(damp, 0.0f);
impulse->z += finalImpulse;