From 99eebdcb76782e1a930fe4e5413ae46b50dabe2c Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Sun, 28 Sep 2025 12:01:31 +0200 Subject: [PATCH 1/4] tweak(water): Decouple water movement time step from render update --- .../Include/W3DDevice/GameClient/W3DWater.h | 2 +- .../W3DDevice/GameClient/Water/W3DWater.cpp | 47 +++++++++---------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h index 1385651dada..eeedcac8ac7 100644 --- a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h @@ -168,7 +168,7 @@ class WaterRenderObjClass : public Snapshot, Int m_numIndices; ///getActualLogicTimeScaleOverFpsRatio(); - if( TheGameLogic ) - currLogicFrame = TheGameLogic->getFrame(); - - // we only process things if the logic frame has changed - if( lastLogicFrame != currLogicFrame ) { + constexpr const Real MagicOffset = 0.0125 * 33 / 5000; ///< the work of top Munkees; do not question it - m_riverVOrigin += 0.002f; - m_riverXOffset += (Real)(0.0125*33/5000); - m_riverYOffset += (Real)(2*0.0125*33/5000); - if (m_riverXOffset > 1) m_riverXOffset -= 1; - if (m_riverYOffset > 1) m_riverYOffset -= 1; - if (m_riverXOffset < -1) m_riverXOffset += 1; - if (m_riverYOffset < -1) m_riverYOffset += 1; - m_iBumpFrame++; - if (m_iBumpFrame >= NUM_BUMP_FRAMES) { - m_iBumpFrame = 0; - } + m_riverVOrigin += 0.002f * timeScale; + m_riverXOffset += (Real)(MagicOffset * timeScale); + m_riverYOffset += (Real)(2 * MagicOffset * timeScale); + + if (m_riverXOffset > 1.0f) + m_riverXOffset -= (Int)m_riverXOffset; + + if (m_riverYOffset > 1.0f) + m_riverYOffset -= (Int)m_riverYOffset; + + m_fBumpFrame += timeScale; + if (m_fBumpFrame >= NUM_BUMP_FRAMES) + m_fBumpFrame = 0.0f; // for vertex animated water we need to update the vector field if( m_doWaterGrid && m_meshInMotion == TRUE ) { const Real PREFERRED_HEIGHT_FUDGE = 1.0f; ///< this is close enough to at rest - const Real AT_REST_VELOCITY_FUDGE = 1.0f; ///< when we're close enought to at rest height and velocity we will stop + const Real AT_REST_VELOCITY_FUDGE = 1.0f; ///< when we're close enough to at rest height and velocity we will stop const Real WATER_DAMPENING = 0.93f; ///< use with up force of 15.0 Int i, j; Int mx = m_gridCellsX+1; @@ -1303,9 +1301,6 @@ void WaterRenderObjClass::update( void ) } - // mark the last logic frame we processed on - lastLogicFrame = currLogicFrame; - } } @@ -1846,7 +1841,7 @@ void WaterRenderObjClass::drawSea(RenderInfoClass & rinfo) m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP); m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP); - m_pDev->SetTexture( 0, m_pBumpTexture[m_iBumpFrame]); + m_pDev->SetTexture( 0, m_pBumpTexture[(Int)m_fBumpFrame]); #ifdef MIPMAP_BUMP_TEXTURE m_pDev->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_POINT ); m_pDev->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR ); From d1dd70fa4ee318e0dc330a5ef7cbc747d8d72014 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:32:24 +0200 Subject: [PATCH 2/4] Simplify the clamping code --- .../Source/W3DDevice/GameClient/Water/W3DWater.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp index 974764e13d7..4a81de5cdb6 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp @@ -1219,11 +1219,9 @@ void WaterRenderObjClass::update( void ) m_riverXOffset += (Real)(MagicOffset * timeScale); m_riverYOffset += (Real)(2 * MagicOffset * timeScale); - if (m_riverXOffset > 1.0f) - m_riverXOffset -= (Int)m_riverXOffset; - - if (m_riverYOffset > 1.0f) - m_riverYOffset -= (Int)m_riverYOffset; + // This moves offsets towards zero when smaller -1.0 or larger 1.0 + m_riverXOffset -= (Int)m_riverXOffset; + m_riverYOffset -= (Int)m_riverYOffset; m_fBumpFrame += timeScale; if (m_fBumpFrame >= NUM_BUMP_FRAMES) From f54ec02058fdd739e93ff541871baf2f7d2675a7 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:40:29 +0200 Subject: [PATCH 3/4] Fix compile warning --- .../Source/W3DDevice/GameClient/Water/W3DWater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp index 4a81de5cdb6..e9599a7a256 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp @@ -1213,7 +1213,7 @@ void WaterRenderObjClass::update( void ) const Real timeScale = TheGameEngine->getActualLogicTimeScaleOverFpsRatio(); { - constexpr const Real MagicOffset = 0.0125 * 33 / 5000; ///< the work of top Munkees; do not question it + constexpr const Real MagicOffset = 0.0125f * 33 / 5000; ///< the work of top Munkees; do not question it m_riverVOrigin += 0.002f * timeScale; m_riverXOffset += (Real)(MagicOffset * timeScale); From b36b7f52af9ac60b61b66192dff59affcdc23683 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:40:50 +0200 Subject: [PATCH 4/4] Replicate in Generals --- .../Include/W3DDevice/GameClient/W3DWater.h | 2 +- .../W3DDevice/GameClient/Water/W3DWater.cpp | 45 ++++++++----------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h index 3331b95cdde..6e9991fd665 100644 --- a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h +++ b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DWater.h @@ -168,7 +168,7 @@ class WaterRenderObjClass : public Snapshot, Int m_numIndices; ///getActualLogicTimeScaleOverFpsRatio(); - if( TheGameLogic ) - currLogicFrame = TheGameLogic->getFrame(); - - // we only process things if the logic frame has changed - if( lastLogicFrame != currLogicFrame ) { + constexpr const Real MagicOffset = 0.0125f * 33 / 5000; ///< the work of top Munkees; do not question it - m_riverVOrigin += 0.002f; - m_riverXOffset += (Real)(0.0125*33/5000); - m_riverYOffset += (Real)(2*0.0125*33/5000); - if (m_riverXOffset > 1) m_riverXOffset -= 1; - if (m_riverYOffset > 1) m_riverYOffset -= 1; - if (m_riverXOffset < -1) m_riverXOffset += 1; - if (m_riverYOffset < -1) m_riverYOffset += 1; - m_iBumpFrame++; - if (m_iBumpFrame >= NUM_BUMP_FRAMES) { - m_iBumpFrame = 0; - } + m_riverVOrigin += 0.002f * timeScale; + m_riverXOffset += (Real)(MagicOffset * timeScale); + m_riverYOffset += (Real)(2 * MagicOffset * timeScale); + + // This moves offsets towards zero when smaller -1.0 or larger 1.0 + m_riverXOffset -= (Int)m_riverXOffset; + m_riverYOffset -= (Int)m_riverYOffset; + + m_fBumpFrame += timeScale; + if (m_fBumpFrame >= NUM_BUMP_FRAMES) + m_fBumpFrame = 0.0f; // for vertex animated water we need to update the vector field if( m_doWaterGrid && m_meshInMotion == TRUE ) { const Real PREFERRED_HEIGHT_FUDGE = 1.0f; ///< this is close enough to at rest - const Real AT_REST_VELOCITY_FUDGE = 1.0f; ///< when we're close enought to at rest height and velocity we will stop + const Real AT_REST_VELOCITY_FUDGE = 1.0f; ///< when we're close enough to at rest height and velocity we will stop const Real WATER_DAMPENING = 0.93f; ///< use with up force of 15.0 Int i, j; Int mx = m_gridCellsX+1; @@ -1280,9 +1276,6 @@ void WaterRenderObjClass::update( void ) } - // mark the last logic frame we processed on - lastLogicFrame = currLogicFrame; - } } @@ -1823,7 +1816,7 @@ void WaterRenderObjClass::drawSea(RenderInfoClass & rinfo) m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP); m_pDev->SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP); - m_pDev->SetTexture( 0, m_pBumpTexture[m_iBumpFrame]); + m_pDev->SetTexture( 0, m_pBumpTexture[(Int)m_fBumpFrame]); #ifdef MIPMAP_BUMP_TEXTURE m_pDev->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_POINT ); m_pDev->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );