From 8b47651d8aa5ae63a75156f1f3f36a8edb0abcba Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Tue, 11 Aug 2026 18:33:43 +0200 Subject: [PATCH 1/3] bugfix(scorches): Prevent gameplay scorches from evicting map scorches --- .../W3DDevice/GameClient/BaseHeightMap.h | 2 ++ .../W3DDevice/GameClient/BaseHeightMap.cpp | 21 +++++++++++++++++++ .../W3DDevice/GameClient/W3DTerrainVisual.cpp | 2 +- .../Code/Tools/WorldBuilder/src/wbview3d.cpp | 2 +- .../Code/Tools/WorldBuilder/src/wbview3d.cpp | 2 +- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h index f533fe72e4d..f6a257835f9 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/BaseHeightMap.h @@ -152,6 +152,7 @@ class BaseHeightMapRenderObjClass : public RenderObjClass, public DX8_CleanupHoo /// Update the diffuse value from static light info for one vertex. void doTheLight(VERTEX_FORMAT *vb, const Vector3*light, Vector3*normal, RefRenderObjListIterator *pLightsIterator, UnsignedByte alpha); void addScorch(Vector3 location, Real radius, Scorches type); + void addStaticScorch(Vector3 location, Real radius, Scorches type); void addTree(DrawableID id, Coord3D location, Real scale, Real angle, Real randomScaleAmount, const W3DTreeDrawModuleData *data); void removeAllTrees(); @@ -268,6 +269,7 @@ class BaseHeightMapRenderObjClass : public RenderObjClass, public DX8_CleanupHoo #endif W3DBridgeBuffer *m_bridgeBuffer; W3DScorchInterface *m_scorches; + W3DScorchInterface *m_staticScorches; W3DShroud *m_shroud; ///< Class for drawing the shroud over terrain. struct shoreLineTileInfo { Int m_xy; //x,y position of tile diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index 25c41156a89..b663b64c643 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp @@ -150,6 +150,7 @@ inline Int IABS(Int x) { if (x>=0) return x; return -x;}; Int BaseHeightMapRenderObjClass::freeMapResources() { m_scorches->freeBuffers(); + m_staticScorches->freeBuffers(); REF_PTR_RELEASE(m_vertexMaterialClass); REF_PTR_RELEASE(m_stageZeroTexture); @@ -171,6 +172,7 @@ void BaseHeightMapRenderObjClass::drawScorches() { ShaderClass::Invalidate(); if (m_map && Is_Hidden() == 0 && !ShaderClass::Is_Backface_Culling_Inverted()) { + m_staticScorches->drawScorches(*m_map); m_scorches->drawScorches(*m_map); } } @@ -214,6 +216,9 @@ BaseHeightMapRenderObjClass::~BaseHeightMapRenderObjClass() delete m_scorches; m_scorches = nullptr; + delete m_staticScorches; + m_staticScorches = nullptr; + delete [] m_shoreLineTilePositions; m_shoreLineTilePositions = nullptr; @@ -272,8 +277,10 @@ BaseHeightMapRenderObjClass::BaseHeightMapRenderObjClass() #endif #if DO_SCORCH m_scorches = NEW W3DScorch; + m_staticScorches = NEW W3DScorch; #else m_scorches = NEW W3DScorchDummy; + m_staticScorches = NEW W3DScorchDummy; #endif m_bridgeBuffer = NEW W3DBridgeBuffer; @@ -1821,6 +1828,7 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM scheduleFullUpdate(); m_scorches->invalidateBuffers(); + m_staticScorches->invalidateBuffers(); // If the textures aren't allocated (usually because of a hardware reset) need to allocate. Bool needToAllocate = false; @@ -1837,6 +1845,7 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM m_destAlphaTexture=MSGNEW("TextureClass") TextureClass(256,1,WW3D_FORMAT_A8R8G8B8,MIP_LEVELS_1); initDestAlphaLUT(); m_scorches->allocateBuffers(); + m_staticScorches->allocateBuffers(); m_vertexMaterialClass=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE); @@ -1854,6 +1863,7 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM void BaseHeightMapRenderObjClass::clearAllScorches() { m_scorches->clearAllScorches(); + m_staticScorches->clearAllScorches(); } //============================================================================= @@ -1866,6 +1876,16 @@ void BaseHeightMapRenderObjClass::addScorch(Vector3 location, Real radius, Scorc m_scorches->addScorch(location, radius, type); } +//============================================================================= +// BaseHeightMapRenderObjClass::addStaticScorch +//============================================================================= +/** Adds a permanent scorch mark loaded from the map. */ +//============================================================================= +void BaseHeightMapRenderObjClass::addStaticScorch(Vector3 location, Real radius, Scorches type) +{ + m_staticScorches->addScorch(location, radius, type); +} + //============================================================================= // BaseHeightMapRenderObjClass::getStaticDiffuse //============================================================================= @@ -2157,6 +2177,7 @@ void BaseHeightMapRenderObjClass::staticLightingChanged() // Cause the scorches to get updated with new lighting. m_scorches->invalidateBuffers(); + m_staticScorches->invalidateBuffers(); if (m_roadBuffer) m_roadBuffer->updateLighting(); diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index ad5d6386a98..e50d92a7a38 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -661,7 +661,7 @@ Bool W3DTerrainVisual::load( AsciiString filename ) Vector3 loc(pos->x, pos->y, pos->z); Real radius = d->getReal(TheKey_objectRadius); Scorches type = (Scorches)d->getInt(TheKey_scorchType); - m_terrainRenderObject->addScorch(loc, radius, type); + m_terrainRenderObject->addStaticScorch(loc, radius, type); } pMapObj = pMapObj->getNext(); } diff --git a/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp b/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp index 1d824b032f0..ba3fb4530e4 100644 --- a/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp @@ -973,7 +973,7 @@ void WbView3d::updateScorches() Scorches type = (Scorches) pMapObj->getProperties()->getInt(TheKey_scorchType); Vector3 loc(pos->x, pos->y, pos->z); - TheTerrainRenderObject->addScorch(loc, radius, type); + TheTerrainRenderObject->addStaticScorch(loc, radius, type); } } } diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp index 5f42dc261fa..a1f39f2fb3f 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp @@ -998,7 +998,7 @@ void WbView3d::updateScorches() Scorches type = (Scorches) pMapObj->getProperties()->getInt(TheKey_scorchType); Vector3 loc(pos->x, pos->y, pos->z); - TheTerrainRenderObject->addScorch(loc, radius, type); + TheTerrainRenderObject->addStaticScorch(loc, radius, type); } } } From 168202ac93055a87e5e3991864812535d65c674d Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Tue, 11 Aug 2026 21:30:42 +0200 Subject: [PATCH 2/3] Do not apply deduplication to static scorches --- .../Include/W3DDevice/GameClient/W3DScorch.h | 3 ++- .../W3DDevice/GameClient/BaseHeightMap.cpp | 4 ++-- .../Source/W3DDevice/GameClient/W3DScorch.cpp | 21 +++++++++++-------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h index 0e35dd4eec0..b3dc11d5878 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h @@ -43,7 +43,7 @@ class W3DScorchInterface class W3DScorch : public W3DScorchInterface { public: - W3DScorch(); + W3DScorch(bool dedupeScorches); virtual ~W3DScorch() override; virtual void allocateBuffers() override; ///< allocate static buffers for drawing scorch marks. @@ -80,6 +80,7 @@ class W3DScorch : public W3DScorchInterface TScorch m_scorches[MAX_SCORCH_MARKS]; Int m_numScorches; Int m_scorchesInBuffer; ///< how many are in the buffers. If less than numScorches, we need to update + Bool m_dedupeScorches; }; class W3DScorchDummy : public W3DScorchInterface diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index b663b64c643..b68daacb31e 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp @@ -276,8 +276,8 @@ BaseHeightMapRenderObjClass::BaseHeightMapRenderObjClass() m_roadBuffer = nullptr; #endif #if DO_SCORCH - m_scorches = NEW W3DScorch; - m_staticScorches = NEW W3DScorch; + m_scorches = NEW W3DScorch(true); + m_staticScorches = NEW W3DScorch(false); #else m_scorches = NEW W3DScorchDummy; m_staticScorches = NEW W3DScorchDummy; diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp index 04041b57877..858926b19e1 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp @@ -26,7 +26,7 @@ #include "W3DDevice/GameClient/WorldHeightMap.h" #include "WW3D2/dx8wrapper.h" -W3DScorch::W3DScorch() +W3DScorch::W3DScorch(bool dedupeScorches) : m_vertexScorch(nullptr) , m_indexScorch(nullptr) , m_scorchTexture(nullptr) @@ -34,6 +34,7 @@ W3DScorch::W3DScorch() , m_curNumScorchIndices(0) , m_numScorches(0) , m_scorchesInBuffer(0) + , m_dedupeScorches(dedupeScorches) {} W3DScorch::~W3DScorch() { freeBuffers(); } @@ -85,16 +86,18 @@ void W3DScorch::addScorch(Vector3 location, Real radius, Scorches type) m_numScorches--; } - Int i; - Real limit = radius / 4; - for (i = 0; i < m_numScorches; i++) + if (m_dedupeScorches) { - if (abs(location.X - m_scorches[i].location.X) < limit && - abs(location.Y - m_scorches[i].location.Y) < limit && - abs(radius - m_scorches[i].radius) < limit && - m_scorches[i].scorchType == type) + const Real limit = radius / 4; + for (Int i = 0; i < m_numScorches; i++) { - return; // basically a duplicate. + if (abs(location.X - m_scorches[i].location.X) < limit && + abs(location.Y - m_scorches[i].location.Y) < limit && + abs(radius - m_scorches[i].radius) < limit && + m_scorches[i].scorchType == type) + { + return; // basically a duplicate. + } } } From bcd59860db8d9bb6a3e3a455893a187893589413 Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Thu, 13 Aug 2026 11:15:07 +0200 Subject: [PATCH 3/3] Address review feedback --- .../Include/W3DDevice/GameClient/W3DScorch.h | 4 ++-- .../Source/W3DDevice/GameClient/BaseHeightMap.cpp | 3 ++- .../Source/W3DDevice/GameClient/W3DScorch.cpp | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h index b3dc11d5878..f14908b507c 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DScorch.h @@ -43,7 +43,7 @@ class W3DScorchInterface class W3DScorch : public W3DScorchInterface { public: - W3DScorch(bool dedupeScorches); + W3DScorch(bool deduplicateScorches); virtual ~W3DScorch() override; virtual void allocateBuffers() override; ///< allocate static buffers for drawing scorch marks. @@ -80,7 +80,7 @@ class W3DScorch : public W3DScorchInterface TScorch m_scorches[MAX_SCORCH_MARKS]; Int m_numScorches; Int m_scorchesInBuffer; ///< how many are in the buffers. If less than numScorches, we need to update - Bool m_dedupeScorches; + Bool m_deduplicateScorches; }; class W3DScorchDummy : public W3DScorchInterface diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index b68daacb31e..1db76d70f65 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp @@ -1879,7 +1879,8 @@ void BaseHeightMapRenderObjClass::addScorch(Vector3 location, Real radius, Scorc //============================================================================= // BaseHeightMapRenderObjClass::addStaticScorch //============================================================================= -/** Adds a permanent scorch mark loaded from the map. */ +/** TheSuperHackers @feature stephanmeesters 13/08/2026 Adds a permanent scorch mark loaded from the map. Static + * scorch marks are managed separately so adding gameplay scorch marks cannot evict them. */ //============================================================================= void BaseHeightMapRenderObjClass::addStaticScorch(Vector3 location, Real radius, Scorches type) { diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp index 858926b19e1..60a0189d2da 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DScorch.cpp @@ -26,7 +26,7 @@ #include "W3DDevice/GameClient/WorldHeightMap.h" #include "WW3D2/dx8wrapper.h" -W3DScorch::W3DScorch(bool dedupeScorches) +W3DScorch::W3DScorch(bool deduplicateScorches) : m_vertexScorch(nullptr) , m_indexScorch(nullptr) , m_scorchTexture(nullptr) @@ -34,7 +34,7 @@ W3DScorch::W3DScorch(bool dedupeScorches) , m_curNumScorchIndices(0) , m_numScorches(0) , m_scorchesInBuffer(0) - , m_dedupeScorches(dedupeScorches) + , m_deduplicateScorches(deduplicateScorches) {} W3DScorch::~W3DScorch() { freeBuffers(); } @@ -86,7 +86,7 @@ void W3DScorch::addScorch(Vector3 location, Real radius, Scorches type) m_numScorches--; } - if (m_dedupeScorches) + if (m_deduplicateScorches) { const Real limit = radius / 4; for (Int i = 0; i < m_numScorches; i++)