Skip to content

Commit 9ba92fa

Browse files
authored
tweak(tree): Decouple tree sway, topple and sink time step from render update (#1652)
1 parent eb7093e commit 9ba92fa

File tree

4 files changed

+51
-47
lines changed

4 files changed

+51
-47
lines changed

Generals/Code/GameEngine/Source/GameClient/Drawable/Update/SwayClientUpdate.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "GameClient/Drawable.h"
3434
#include "GameClient/Module/SwayClientUpdate.h"
35+
#include "Common/FramePacer.h"
3536
#include "Common/Player.h"
3637
#include "Common/PlayerList.h"
3738
#include "Common/ThingFactory.h"
@@ -114,7 +115,10 @@ void SwayClientUpdate::clientUpdate( void )
114115
return;
115116
}
116117

117-
m_curValue += m_curDelta;
118+
// TheSuperHackers @tweak The tree sway time step is now decoupled from the render update.
119+
const Real timeScale = TheFramePacer->getActualLogicTimeScaleOverFpsRatio();
120+
121+
m_curValue += m_curDelta * timeScale;
118122
if (m_curValue > 2*PI)
119123
m_curValue -= 2*PI;
120124
Real cosine = Cos(m_curValue);

GeneralsMD/Code/GameEngine/Source/GameClient/Drawable/Update/SwayClientUpdate.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "GameClient/Drawable.h"
3434
#include "GameClient/Module/SwayClientUpdate.h"
35+
#include "Common/FramePacer.h"
3536
#include "Common/Player.h"
3637
#include "Common/PlayerList.h"
3738
#include "Common/ThingFactory.h"
@@ -114,7 +115,10 @@ void SwayClientUpdate::clientUpdate( void )
114115
return;
115116
}
116117

117-
m_curValue += m_curDelta;
118+
// TheSuperHackers @tweak The tree sway time step is now decoupled from the render update.
119+
const Real timeScale = TheFramePacer->getActualLogicTimeScaleOverFpsRatio();
120+
121+
m_curValue += m_curDelta * timeScale;
118122
if (m_curValue > 2*PI)
119123
m_curValue -= 2*PI;
120124
Real cosine = Cos(m_curValue);

GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DTreeBuffer.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ typedef struct {
121121
Real m_angularAccumulation; ///< How much have I rotated so I know when to bounce.
122122
UnsignedInt m_options; ///< topple options
123123
Matrix3D m_mtx;
124-
UnsignedInt m_sinkFramesLeft; ///< Toppled trees sink into the terrain & disappear, how many frames left.
124+
Real m_sinkFramesLeft; ///< Toppled trees sink into the terrain & disappear, how many frames left.
125125

126126
} TTree;
127127

@@ -256,7 +256,6 @@ class W3DTreeBuffer : public Snapshot
256256
Real m_curSwayOffset[MAX_SWAY_TYPES];
257257
Real m_curSwayStep[MAX_SWAY_TYPES];
258258
Real m_curSwayFactor[MAX_SWAY_TYPES];
259-
Int m_lastLogicFrame;
260259

261260
W3DProjectedShadow *m_shadow;
262261

@@ -282,7 +281,7 @@ class W3DTreeBuffer : public Snapshot
282281

283282
Int getPartitionBucket(const Coord3D &pos) const;
284283

285-
void updateTopplingTree(TTree *tree);
284+
void updateTopplingTree(TTree *tree, Real timeScale);
286285
void applyTopplingForce( TTree *tree, const Coord3D* toppleDirection, Real toppleSpeed,
287286
UnsignedInt options );
288287

GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTreeBuffer.cpp

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ enum
6060
#include <string.h>
6161
#include <assetmgr.h>
6262
#include <texture.h>
63+
#include "Common/FramePacer.h"
6364
#include "Common/MapReaderWriterInfo.h"
6465
#include "Common/FileSystem.h"
6566
#include "Common/file.h"
@@ -1274,8 +1275,6 @@ void W3DTreeBuffer::clearAllTrees(void)
12741275
m_areaPartition[i] = END_OF_PARTITION;
12751276
}
12761277
m_numTreeTypes = 0;
1277-
1278-
m_lastLogicFrame = 0;
12791278
}
12801279

12811280
//=============================================================================
@@ -1537,34 +1536,20 @@ void W3DTreeBuffer::drawTrees(CameraClass * camera, RefRenderObjListIterator *pD
15371536
// if breeze changes, always process the full update, even if not visible,
15381537
// so that things offscreen won't 'pop' when first viewed
15391538
const BreezeInfo& info = TheScriptEngine->getBreezeInfo();
1540-
Bool pause = TheScriptEngine->isTimeFrozenScript() || TheScriptEngine->isTimeFrozenDebug();
1541-
if (TheGameLogic && TheGameLogic->isGamePaused()) {
1542-
pause = true;
1543-
}
1544-
1545-
// TheSuperHackers @bugfix Mauller 04/07/2025 decouple the tree sway position updates from the client fps
1546-
if (TheGameLogic) {
1547-
UnsignedInt currentFrame = TheGameLogic->getFrame();
1548-
if (m_lastLogicFrame == currentFrame) {
1549-
pause = true;
1550-
}
1551-
m_lastLogicFrame = currentFrame;
1539+
if (info.m_breezeVersion != m_curSwayVersion)
1540+
{
1541+
updateSway(info);
15521542
}
15531543

1554-
Int i;
1555-
if (!pause) {
1556-
if (info.m_breezeVersion != m_curSwayVersion)
1557-
{
1558-
updateSway(info);
1559-
}
1560-
}
1544+
// TheSuperHackers @tweak The tree sway, topple and sink time steps are now decoupled from the render update.
1545+
const Real timeScale = TheFramePacer->getActualLogicTimeScaleOverFpsRatio();
15611546
Vector3 swayFactor[MAX_SWAY_TYPES];
1562-
for (i=0; i<MAX_SWAY_TYPES; i++) {
1563-
if (!pause) {
1564-
m_curSwayOffset[i] += m_curSwayStep[i];
1565-
if (m_curSwayOffset[i] > NUM_SWAY_ENTRIES-1) {
1566-
m_curSwayOffset[i] -= NUM_SWAY_ENTRIES-1;
1567-
}
1547+
Int i;
1548+
for (i=0; i<MAX_SWAY_TYPES; i++)
1549+
{
1550+
m_curSwayOffset[i] += m_curSwayStep[i] * timeScale;
1551+
if (m_curSwayOffset[i] > NUM_SWAY_ENTRIES-1) {
1552+
m_curSwayOffset[i] -= NUM_SWAY_ENTRIES-1;
15681553
}
15691554
Int minOffset = REAL_TO_INT_FLOOR(m_curSwayOffset[i]);
15701555
if (minOffset>=0 && minOffset+1<NUM_SWAY_ENTRIES) {
@@ -1613,30 +1598,29 @@ void W3DTreeBuffer::drawTrees(CameraClass * camera, RefRenderObjListIterator *pD
16131598

16141599
// Update pushed aside and toppling trees.
16151600
for (curTree=0; curTree<m_numTrees; curTree++) {
1616-
if (pause) {
1617-
break;
1618-
}
16191601
Int type = m_trees[curTree].treeType;
16201602
if (type<0) { // deleted.
16211603
continue;
16221604
}
1605+
const W3DTreeDrawModuleData *moduleData = m_treeTypes[type].m_data;
16231606
if(m_trees[curTree].m_toppleState == TOPPLE_FALLING ||
16241607
m_trees[curTree].m_toppleState == TOPPLE_FOGGED) {
1625-
updateTopplingTree(m_trees+curTree);
1608+
updateTopplingTree(m_trees+curTree, timeScale);
16261609
} else if(m_trees[curTree].m_toppleState == TOPPLE_DOWN) {
1627-
if (m_treeTypes[type].m_data->m_killWhenToppled) {
1628-
if (m_trees[curTree].m_sinkFramesLeft==0) {
1610+
if (moduleData->m_killWhenToppled) {
1611+
if (m_trees[curTree].m_sinkFramesLeft <= 0.0f) {
16291612
m_trees[curTree].treeType = DELETED_TREE_TYPE; // delete it. [7/11/2003]
16301613
m_anythingChanged = true; // need to regenerate trees. [7/11/2003]
16311614
}
1632-
m_trees[curTree].m_sinkFramesLeft--;
1633-
m_trees[curTree].location.Z -= m_treeTypes[type].m_data->m_sinkDistance/m_treeTypes[type].m_data->m_sinkFrames;
1615+
const Real sinkDistancePerFrame = moduleData->m_sinkDistance / moduleData->m_sinkFrames;
1616+
m_trees[curTree].m_sinkFramesLeft -= timeScale;
1617+
m_trees[curTree].location.Z -= sinkDistancePerFrame * timeScale;
16341618
m_trees[curTree].m_mtx.Set_Translation(m_trees[curTree].location);
16351619
}
16361620
} else if (m_trees[curTree].pushAsideDelta!=0.0f) {
16371621
m_trees[curTree].pushAside += m_trees[curTree].pushAsideDelta;
16381622
if (m_trees[curTree].pushAside>=1.0f) {
1639-
m_trees[curTree].pushAsideDelta = -1.0/(Real)m_treeTypes[type].m_data->m_framesToMoveInward;
1623+
m_trees[curTree].pushAsideDelta = -1.0f/(Real)moduleData->m_framesToMoveInward;
16401624
} else if (m_trees[curTree].pushAside<=0.0f) {
16411625
m_trees[curTree].pushAsideDelta = 0.0f;
16421626
m_trees[curTree].pushAside = 0.0f;
@@ -1856,7 +1840,7 @@ static const Real ANGULAR_LIMIT = PI/2 - PI/64;
18561840
//-------------------------------------------------------------------------------------------------
18571841
///< Keep track of rotational fall distance, bounce and/or stop when needed.
18581842
//-------------------------------------------------------------------------------------------------
1859-
void W3DTreeBuffer::updateTopplingTree(TTree *tree)
1843+
void W3DTreeBuffer::updateTopplingTree(TTree *tree, Real timeScale)
18601844
{
18611845
//DLOG(Debug::Format("updating W3DTreeBuffer %08lx\n",this));
18621846
DEBUG_ASSERTCRASH(tree->m_toppleState != TOPPLE_UPRIGHT, ("hmm, we should be sleeping here"));
@@ -1880,21 +1864,20 @@ void W3DTreeBuffer::updateTopplingTree(TTree *tree)
18801864
tree->m_mtx.In_Place_Pre_Rotate_Y(ANGULAR_LIMIT * tree->m_toppleDirection.x);
18811865
if (d->m_killWhenToppled) {
18821866
// If got killed in the fog, just remove. jba [8/11/2003]
1883-
tree->m_sinkFramesLeft = 0;
1867+
tree->m_sinkFramesLeft = 0.0f;
18841868
}
18851869
return;
18861870
}
18871871
const Real VELOCITY_BOUNCE_LIMIT = 0.01f; // if the velocity after a bounce will be this or lower, just stop at zero
18881872
const Real VELOCITY_BOUNCE_SOUND_LIMIT = 0.03f; // and if this low, then skip the bounce sound
18891873

1890-
Real curVelToUse = tree->m_angularVelocity;
1874+
Real curVelToUse = tree->m_angularVelocity * timeScale;
18911875
if (tree->m_angularAccumulation + curVelToUse > ANGULAR_LIMIT)
18921876
curVelToUse = ANGULAR_LIMIT - tree->m_angularAccumulation;
18931877

18941878
tree->m_mtx.In_Place_Pre_Rotate_X(-curVelToUse * tree->m_toppleDirection.y);
18951879
tree->m_mtx.In_Place_Pre_Rotate_Y(curVelToUse * tree->m_toppleDirection.x);
18961880

1897-
18981881
tree->m_angularAccumulation += curVelToUse;
18991882
if ((tree->m_angularAccumulation >= ANGULAR_LIMIT) && (tree->m_angularVelocity > 0))
19001883
{
@@ -1926,7 +1909,7 @@ void W3DTreeBuffer::updateTopplingTree(TTree *tree)
19261909
}
19271910
else
19281911
{
1929-
tree->m_angularVelocity += tree->m_angularAcceleration;
1912+
tree->m_angularVelocity += tree->m_angularAcceleration * timeScale;
19301913
}
19311914

19321915
}
@@ -1948,7 +1931,11 @@ void W3DTreeBuffer::xfer( Xfer *xfer )
19481931
{
19491932

19501933
// version
1934+
#if RETAIL_COMPATIBLE_XFER_SAVE
19511935
XferVersion currentVersion = 1;
1936+
#else
1937+
XferVersion currentVersion = 2;
1938+
#endif
19521939
XferVersion version = currentVersion;
19531940
xfer->xferVersion( &version, currentVersion );
19541941

@@ -2008,7 +1995,17 @@ void W3DTreeBuffer::xfer( Xfer *xfer )
20081995
xfer->xferReal(&tree.m_angularAccumulation); ///< How much have I rotated so I know when to bounce.
20091996
xfer->xferUnsignedInt(&tree.m_options); ///< topple options
20101997
xfer->xferMatrix3D(&tree.m_mtx);
2011-
xfer->xferUnsignedInt(&tree.m_sinkFramesLeft); ///< Toppled trees sink into the terrain & disappear, how many frames left.
1998+
1999+
if (version <= 1)
2000+
{
2001+
UnsignedInt sinkFramesLeft = (UnsignedInt)tree.m_sinkFramesLeft;
2002+
xfer->xferUnsignedInt(&sinkFramesLeft); ///< Toppled trees sink into the terrain & disappear, how many frames left.
2003+
tree.m_sinkFramesLeft = (Real)sinkFramesLeft;
2004+
}
2005+
else
2006+
{
2007+
xfer->xferReal(&tree.m_sinkFramesLeft); ///< Toppled trees sink into the terrain & disappear, how many frames left.
2008+
}
20122009

20132010
if (xfer->getXferMode() == XFER_LOAD && treeType != DELETED_TREE_TYPE && treeType < m_numTreeTypes) {
20142011
Coord3D pos;

0 commit comments

Comments
 (0)