From a02512af146e889bd035f8129626d98298566706 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 15 Sep 2026 11:19:26 -0400 Subject: [PATCH 1/2] tweak(drawable): Interpolate physics transforms --- .../GameEngine/Include/GameClient/Drawable.h | 8 ++++++- .../GameEngine/Source/GameClient/Drawable.cpp | 24 +++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h index 7169b8cb51f..1490bb8690c 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h @@ -622,7 +622,13 @@ class Drawable : public Thing, Real m_totalYaw; ///< Current total yaw for this frame Real m_totalZ; - PhysicsXformInfo() : m_totalPitch(0), m_totalRoll(0), m_totalYaw(0), m_totalZ(0) { } + Real m_prevTotalPitch; + Real m_prevTotalRoll; + Real m_prevTotalYaw; + Real m_prevTotalZ; + + PhysicsXformInfo() : m_totalPitch(0), m_totalRoll(0), m_totalYaw(0), m_totalZ(0), + m_prevTotalPitch(0), m_prevTotalRoll(0), m_prevTotalYaw(0), m_prevTotalZ(0) { } }; Bool calcPhysicsXform(PhysicsXformInfo& info); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp index 54dff3325f2..2f48f594462 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp @@ -1355,13 +1355,29 @@ void Drawable::applyPhysicsXform(Matrix3D* mtx) // All calculations are originally catered to a 30 fps logic step. if (WW3D::Get_Sync_Frame_Time() != 0) { + m_physicsXform->m_prevTotalPitch = m_physicsXform->m_totalPitch; + m_physicsXform->m_prevTotalRoll = m_physicsXform->m_totalRoll; + m_physicsXform->m_prevTotalYaw = m_physicsXform->m_totalYaw; + m_physicsXform->m_prevTotalZ = m_physicsXform->m_totalZ; + calcPhysicsXform(*m_physicsXform); } - mtx->Translate(0.0f, 0.0f, m_physicsXform->m_totalZ); - mtx->Rotate_Y( m_physicsXform->m_totalPitch ); - mtx->Rotate_X( -m_physicsXform->m_totalRoll ); - mtx->Rotate_Z( m_physicsXform->m_totalYaw ); + // TheSuperHackers @tweak bobtista 14/09/2026 Interpolate the rendered transform between + // logic frames, so the motion stays smooth when the render rate is above the logic rate. + // The fractional sync time accumulates in logic time, so a full logic step is always MSEC_PER_LOGICFRAME_REAL. + const Real fractionalMs = (Real)WW3D::Get_Fractional_Sync_Milliseconds(); + const Real t = clamp(0.0f, fractionalMs / MSEC_PER_LOGICFRAME_REAL, 1.0f); + + const Real interpPitch = m_physicsXform->m_prevTotalPitch + t * (m_physicsXform->m_totalPitch - m_physicsXform->m_prevTotalPitch); + const Real interpRoll = m_physicsXform->m_prevTotalRoll + t * (m_physicsXform->m_totalRoll - m_physicsXform->m_prevTotalRoll); + const Real interpYaw = m_physicsXform->m_prevTotalYaw + t * (m_physicsXform->m_totalYaw - m_physicsXform->m_prevTotalYaw); + const Real interpZ = m_physicsXform->m_prevTotalZ + t * (m_physicsXform->m_totalZ - m_physicsXform->m_prevTotalZ); + + mtx->Translate(0.0f, 0.0f, interpZ); + mtx->Rotate_Y( interpPitch ); + mtx->Rotate_X( -interpRoll ); + mtx->Rotate_Z( interpYaw ); } } From e3606eff13c28f94827d7411e10aab8bb1a15df8 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 15 Sep 2026 11:19:37 -0400 Subject: [PATCH 2/2] tweak(drawable): Replicate physics interpolation to Generals --- .../GameEngine/Include/GameClient/Drawable.h | 8 ++++++- .../GameEngine/Source/GameClient/Drawable.cpp | 24 +++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameClient/Drawable.h b/Generals/Code/GameEngine/Include/GameClient/Drawable.h index bbe206ff429..4feaaa45d51 100644 --- a/Generals/Code/GameEngine/Include/GameClient/Drawable.h +++ b/Generals/Code/GameEngine/Include/GameClient/Drawable.h @@ -622,7 +622,13 @@ class Drawable : public Thing, Real m_totalYaw; ///< Current total yaw for this frame Real m_totalZ; - PhysicsXformInfo() : m_totalPitch(0), m_totalRoll(0), m_totalYaw(0), m_totalZ(0) { } + Real m_prevTotalPitch; + Real m_prevTotalRoll; + Real m_prevTotalYaw; + Real m_prevTotalZ; + + PhysicsXformInfo() : m_totalPitch(0), m_totalRoll(0), m_totalYaw(0), m_totalZ(0), + m_prevTotalPitch(0), m_prevTotalRoll(0), m_prevTotalYaw(0), m_prevTotalZ(0) { } }; Bool calcPhysicsXform(PhysicsXformInfo& info); diff --git a/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp b/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp index e482166c024..e4987cd3f87 100644 --- a/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp @@ -1353,13 +1353,29 @@ void Drawable::applyPhysicsXform(Matrix3D* mtx) // All calculations are originally catered to a 30 fps logic step. if (WW3D::Get_Sync_Frame_Time() != 0) { + m_physicsXform->m_prevTotalPitch = m_physicsXform->m_totalPitch; + m_physicsXform->m_prevTotalRoll = m_physicsXform->m_totalRoll; + m_physicsXform->m_prevTotalYaw = m_physicsXform->m_totalYaw; + m_physicsXform->m_prevTotalZ = m_physicsXform->m_totalZ; + calcPhysicsXform(*m_physicsXform); } - mtx->Translate(0.0f, 0.0f, m_physicsXform->m_totalZ); - mtx->Rotate_Y( m_physicsXform->m_totalPitch ); - mtx->Rotate_X( -m_physicsXform->m_totalRoll ); - mtx->Rotate_Z( m_physicsXform->m_totalYaw ); + // TheSuperHackers @tweak bobtista 14/09/2026 Interpolate the rendered transform between + // logic frames, so the motion stays smooth when the render rate is above the logic rate. + // The fractional sync time accumulates in logic time, so a full logic step is always MSEC_PER_LOGICFRAME_REAL. + const Real fractionalMs = (Real)WW3D::Get_Fractional_Sync_Milliseconds(); + const Real t = clamp(0.0f, fractionalMs / MSEC_PER_LOGICFRAME_REAL, 1.0f); + + const Real interpPitch = m_physicsXform->m_prevTotalPitch + t * (m_physicsXform->m_totalPitch - m_physicsXform->m_prevTotalPitch); + const Real interpRoll = m_physicsXform->m_prevTotalRoll + t * (m_physicsXform->m_totalRoll - m_physicsXform->m_prevTotalRoll); + const Real interpYaw = m_physicsXform->m_prevTotalYaw + t * (m_physicsXform->m_totalYaw - m_physicsXform->m_prevTotalYaw); + const Real interpZ = m_physicsXform->m_prevTotalZ + t * (m_physicsXform->m_totalZ - m_physicsXform->m_prevTotalZ); + + mtx->Translate(0.0f, 0.0f, interpZ); + mtx->Rotate_Y( interpPitch ); + mtx->Rotate_X( -interpRoll ); + mtx->Rotate_Z( interpYaw ); } }