Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
CBoat::ApplyWaterResistance: Incomplete work fixing slow boats at hig…
…h FPS. Fixed minor mathematical bug with Pow() and Abs(). Added detailed explanation of physics.
  • Loading branch information
Veyrdite committed Jul 4, 2021
1 parent 7ea8d5f commit 5adafb5cd06888c1a6ecd8970afa559fb36e2ce2
Showing with 74 additions and 0 deletions.
  1. +74 −0 src/vehicles/Boat.cpp
@@ -841,6 +841,79 @@ float fSeaPlaneWaterResistance = 30.0f;
void void
CBoat::ApplyWaterResistance(void) CBoat::ApplyWaterResistance(void)
{ {
#ifdef FIX_BUGS
// TODO: confirm if the explanation below makes sense
float depthResistance = 0.001f * pHandling->fSuspensionForceLevel * SQR(m_fVolumeUnderWater) * m_fMass;
if(GetModelIndex() == MI_SKIMMER)
depthResistance *= fSeaPlaneWaterResistance;
float fwdSpeed = DotProduct(GetMoveSpeed(), GetForward());

// Water resistances goes up with the square of boat speed (in real life it goes up by the
// cube? close enough!). An extra 0.05f fudge factor was probably put in to make sure the
// boat still has resistance at low speeds (ie auto-brakes to standstill).
float waterResistance = (SQR(fwdSpeed) + 0.05f) * Abs(depthResistance); // Abs() used defensively, negative numbers stuff things up later
// waterResistance will now be a small number like 0.002 or 0.015

// An odd use of Abs() was in the original binary. It's possible that the developers did not
// put this in intentionally, instead the compiler may have silently added it to avoid
// Pow() having to deal with negative numbers to a fractional power (undefined) later.
// Regardless it was done badly, making assumptions like vecMoveRes never accidentally
// being negative, so the use of Abs() has changed a little bit in this FIX_BUGS. In
// real gameplay these corner cases should rarely (never?) be encountered anyway.

// Our boat has different water resistances when travelling forwards (y axis) versus
// sideways (x axis). Boats tend to find it hard to move sideways.
float rx = Abs(pBoatHandling->vecMoveRes.x/(waterResistance + 1.0f)); // Abs() used defensively, negative numbers stuff things up later
float ry = Abs(pBoatHandling->vecMoveRes.y/(waterResistance + 1.0f));
float rz = Abs(pBoatHandling->vecMoveRes.z/(waterResistance + 1.0f));
// These rx, ry, rz resistance numbers will each be something like 0.8 or 0.9 or so

// Fun fact: the above equations are _approximately_ the same as:
//
// pBoatHandling->vecMoveRes.x * (1.0f - waterResistance)
//
// If you change the equations to this then boating feels about the same in-game.
// Which version makes more sense compared to physics in real life? Probably neither :P
// This second version of the equation is a little more efficient however (no division).

// Now how do we apply these rx ry rz resistance numbers to the boat speed?
// It's not simple:
//
// - We can't multiply them into the speed once per frame, because then players with
// higher framerates will get a lot more friction when boating (lower top speed).
//
// - We can't linearly modify each r number based off frametime, as higher FPS players
// will still end up with more friction. This is for the same reason why linearly
// reducing your bank account's yearly interest into monthly amounts but then
// compounding it monthly will yield you more money than just compounding it yearly.
//
// - We could try compounding each r number based off how many fixed units of time have
// passed (eg multiply itself by itself for every 1ms elapsed this frame). This will
// work fairly regardless of framerate.
//
// 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());

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;
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());

// What the hell? Why arbitrarily compound in one more factor of rrz?
// This is framerate dependent! Bah!
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 // TODO: figure out how this works
float resistance = 0.001f * pHandling->fSuspensionForceLevel * SQR(m_fVolumeUnderWater) * m_fMass; float resistance = 0.001f * pHandling->fSuspensionForceLevel * SQR(m_fVolumeUnderWater) * m_fMass;
if(GetModelIndex() == MI_SKIMMER) if(GetModelIndex() == MI_SKIMMER)
@@ -865,6 +938,7 @@ CBoat::ApplyWaterResistance(void)
m_vecMoveSpeed.z *= fz; m_vecMoveSpeed.z *= fz;
else else
m_vecMoveSpeed.z *= (1.0f - fz)*0.5f + fz; m_vecMoveSpeed.z *= (1.0f - fz)*0.5f + fz;
#endif
} }


RwObject* RwObject*

0 comments on commit 5adafb5

Please sign in to comment.