From 1034a5b05635331f2b14e78366dec8902154f9da Mon Sep 17 00:00:00 2001 From: Mauller <26652186+Mauller@users.noreply.github.com> Date: Sat, 22 Aug 2026 09:45:01 +0100 Subject: [PATCH] refactor(particlesys): Cleanup retail volume particle depth handling --- .../Include/GameClient/ParticleSys.h | 19 +++++---- .../Source/Common/INI/INIParticleSys.cpp | 1 + .../Source/GameClient/System/ParticleSys.cpp | 40 ++++++++++++++----- .../W3DDevice/GameClient/W3DParticleSys.cpp | 5 ++- .../Source/WWVegas/WW3D2/pointgr.cpp | 3 +- .../GameLogic/ScriptEngine/ScriptEngine.cpp | 4 ++ 6 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Core/GameEngine/Include/GameClient/ParticleSys.h b/Core/GameEngine/Include/GameClient/ParticleSys.h index acd504f6838..57c922f3bc4 100644 --- a/Core/GameEngine/Include/GameClient/ParticleSys.h +++ b/Core/GameEngine/Include/GameClient/ParticleSys.h @@ -57,9 +57,11 @@ enum ParticleSystemID CPP_11(: Int) INVALID_PARTICLE_SYSTEM_ID = 0 }; -#define MAX_VOLUME_PARTICLE_DEPTH ( 16 ) -#define DEFAULT_VOLUME_PARTICLE_DEPTH ( 0 )//The Default is not to do the volume thing! -#define OPTIMUM_VOLUME_PARTICLE_DEPTH ( 6 ) +constexpr const UnsignedInt INVALID_VOLUME_PARTICLE_DEPTH = 0; //The volume depth is not initialized +constexpr const UnsignedInt DEFAULT_VOLUME_PARTICLE_DEPTH = 1; //The Default is not to do the volume thing! +constexpr const UnsignedInt MIN_VOLUME_PARTICLE_DEPTH = 2; +constexpr const UnsignedInt OPTIMUM_VOLUME_PARTICLE_DEPTH = 6; +constexpr const UnsignedInt MAX_VOLUME_PARTICLE_DEPTH = 16; // TheSuperHackers @info The X and Y angles are not necessary for particles because there are only 2 placement modes: // Billboard (always facing camera) and Ground Aligned, which overwrite any rotations on the X and Y axis by design. @@ -511,6 +513,8 @@ class ParticleSystemTemplate : public MemoryPoolObject, protected ParticleSystem public: ParticleSystemTemplate( const AsciiString &name ); + void validate(); + AsciiString getName() const { return m_name; } // This function was made const because of update modules' module data being all const. @@ -603,10 +607,11 @@ class ParticleSystem : public MemoryPoolObject, void setInitialDelay( UnsignedInt delay ) { m_delayLeft = delay; } AsciiString getParticleTypeName() { return m_particleTypeName; } ///< return the name of the particles - Bool isUsingDrawables() { return (m_particleType == DRAWABLE) ? true : false; } - Bool isUsingStreak() { return (m_particleType == STREAK) ? true : false; } - Bool isUsingSmudge() { return (m_particleType == SMUDGE) ? true : false; } - UnsignedInt getVolumeParticleDepth() { return ( m_particleType == VOLUME_PARTICLE ) ? OPTIMUM_VOLUME_PARTICLE_DEPTH : 0; } + Bool isUsingDrawables() { return m_particleType == DRAWABLE; } + Bool isUsingStreak() { return m_particleType == STREAK; } + Bool isUsingSmudge() { return m_particleType == SMUDGE; } + Bool isUsingVolumeParticles() { return m_particleType == VOLUME_PARTICLE; } + const UnsignedInt getVolumeParticleDepth() const { return m_volumeParticleDepth; } Bool shouldBillboard() { return !m_isGroundAligned; } diff --git a/Core/GameEngine/Source/Common/INI/INIParticleSys.cpp b/Core/GameEngine/Source/Common/INI/INIParticleSys.cpp index fd0b84d5182..e72ae819664 100644 --- a/Core/GameEngine/Source/Common/INI/INIParticleSys.cpp +++ b/Core/GameEngine/Source/Common/INI/INIParticleSys.cpp @@ -54,4 +54,5 @@ void INI::parseParticleSystemDefinition( INI* ini ) // parse the ini definition ini->initFromINI( sysTemplate, sysTemplate->getFieldParse() ); + sysTemplate->validate(); } diff --git a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp index 4d6441d3855..0f3f0293653 100644 --- a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp +++ b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp @@ -776,7 +776,7 @@ ParticleSystemInfo::ParticleSystemInfo() m_windMotionEndAngleMax = TWO_PI; m_windMotionEndAngle = m_windMotionEndAngleMin; m_windMotionMovingToEndAngle = TRUE; - m_volumeParticleDepth = DEFAULT_VOLUME_PARTICLE_DEPTH; + m_volumeParticleDepth = INVALID_VOLUME_PARTICLE_DEPTH; } @@ -1095,7 +1095,7 @@ ParticleSystem::ParticleSystem( const ParticleSystemTemplate *sysTemplate, ///@todo: further formalize this parameter with an UnsignedInt field in the editor - m_volumeParticleDepth = DEFAULT_VOLUME_PARTICLE_DEPTH; + m_volumeParticleDepth = sysTemplate->m_volumeParticleDepth; m_driftVelocity = sysTemplate->m_driftVelocity; @@ -1190,15 +1190,6 @@ ParticleSystem::ParticleSystem( const ParticleSystemTemplate *sysTemplate, m_particleType = sysTemplate->m_particleType; m_particleTypeName = sysTemplate->m_particleTypeName; -#if PRESERVE_RETAIL_PARTICLES - // TheSuperHackers @info Hack to allow isUsingSmudge() functionality with retail smudge particles - // The retail data template for smudge particles is not correctly configured with the smudge particle type - if (m_particleType != ParticleType::SMUDGE && m_particleTypeName.startsWithNoCase("SMUDGE.")) - { - m_particleType = ParticleType::SMUDGE; - } -#endif - m_isStopped = false; // set up slave particle system, if any @@ -2698,6 +2689,9 @@ const FieldParse ParticleSystemTemplate::m_fieldParseTable[] = { "SizeRate", INI::parseGameClientRandomVariable, nullptr, offsetof( ParticleSystemTemplate, m_sizeRate ) }, { "SizeRateDamping", INI::parseGameClientRandomVariable, nullptr, offsetof( ParticleSystemTemplate, m_sizeRateDamping ) }, + // TheSuperHackers @feature Volume particle depth is now exposed for configuration + { "VolParticleDepth", INI::parseUnsignedInt, nullptr, offsetof(ParticleSystemTemplate, m_volumeParticleDepth) }, + { "Alpha1", ParticleSystemTemplate::parseRandomKeyframe, nullptr, offsetof( ParticleSystemTemplate, m_alphaKey[0] ) }, { "Alpha2", ParticleSystemTemplate::parseRandomKeyframe, nullptr, offsetof( ParticleSystemTemplate, m_alphaKey[1] ) }, { "Alpha3", ParticleSystemTemplate::parseRandomKeyframe, nullptr, offsetof( ParticleSystemTemplate, m_alphaKey[2] ) }, @@ -2866,6 +2860,30 @@ ParticleSystemTemplate::~ParticleSystemTemplate() } +// ------------------------------------------------------------------------------------------------ +void ParticleSystemTemplate::validate() +{ + // TheSuperHackers @info Initialise all volume particles that lack ini configuration to the optimum depth of 6 + // In retail, volume particle depth was not configurable through ini and was hard coded to a particle depth of 6 + if (m_particleType == ParticleSystemInfo::VOLUME_PARTICLE && m_volumeParticleDepth == INVALID_VOLUME_PARTICLE_DEPTH) + { + m_volumeParticleDepth = OPTIMUM_VOLUME_PARTICLE_DEPTH; + } + else + { + m_volumeParticleDepth = DEFAULT_VOLUME_PARTICLE_DEPTH; + } + +#if PRESERVE_RETAIL_PARTICLES + // TheSuperHackers @info Hack to allow isUsingSmudge() functionality with retail smudge particles + // The retail data template for smudge particles is not correctly configured with the smudge particle type + if (m_particleType != ParticleSystemInfo::SMUDGE && m_particleTypeName.startsWithNoCase("SMUDGE.")) + { + m_particleType = ParticleSystemInfo::SMUDGE; + } +#endif +} + // ------------------------------------------------------------------------------------------------ /** If returns non-null, it is a slave system for use ... the create slaves parameter * tells *this* slave system whether or not it should create any slaves itself diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp index c2a1b9c6fc4..c0a901ec7e6 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp @@ -326,9 +326,10 @@ void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo) m_pointGroup->Set_Point_Frame( 0 ); //RENDER IT! - if( sys->getVolumeParticleDepth() > 1 ) + const UnsignedInt volumeParticleDepth = sys->getVolumeParticleDepth(); + if( sys->isUsingVolumeParticles() && volumeParticleDepth > DEFAULT_VOLUME_PARTICLE_DEPTH ) { - m_pointGroup->RenderVolumeParticle( rinfo, sys->getVolumeParticleDepth() ); + m_pointGroup->RenderVolumeParticle( rinfo, volumeParticleDepth); } else m_pointGroup->Render( rinfo ); diff --git a/Core/Libraries/Source/WWVegas/WW3D2/pointgr.cpp b/Core/Libraries/Source/WWVegas/WW3D2/pointgr.cpp index b2eb666b7cd..56c47e0f29f 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/pointgr.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/pointgr.cpp @@ -1644,11 +1644,12 @@ void PointGroupClass::_Shutdown() * 12/03/2002 Mark Lorenzen Created. * * * *========================================================================*/ +#define DEFAULT_VOLUME_PARTICLE_DEPTH ( 1 ) #define MAX_VOLUME_PARTICLE_DEPTH ( 16 ) void PointGroupClass::RenderVolumeParticle(RenderInfoClass &rinfo, unsigned int depth ) { - if ( depth <= 1 ) //oops,wrong number + if ( depth <= DEFAULT_VOLUME_PARTICLE_DEPTH) //oops,wrong number { Render( rinfo ); return; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp index d10dff653a1..8bb3f96537e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp @@ -9725,6 +9725,7 @@ static const std::string F_SIZE = "Size"; static const std::string F_STARTSIZERATE ="StartSizeRate"; static const std::string F_SIZERATE = "SizeRate"; static const std::string F_SIZERATEDAMP = "SizeRateDamping"; +static const std::string F_VOLPARTICLEDEPTH = "VolParticleDepth"; static const std::string F_ALPHA1 = "Alpha1"; static const std::string F_ALPHA2 = "Alpha2"; @@ -9887,6 +9888,9 @@ void _writeSingleParticleSystem( File *out, ParticleSystemTemplate *templ ) sprintf(buff2, FORMAT_STRING, templ->m_sizeRateDamping.getMaximumValue()); thisEntry.append(SEP_HEAD).append(F_SIZERATEDAMP).append(EQ_WITH_SPACES).append(buff1).append(SEP_SPACE).append(buff2).append(SEP_EOL); + sprintf(buff1, "%d", templ->m_volumeParticleDepth); + thisEntry.append(SEP_HEAD).append(F_VOLPARTICLEDEPTH).append(EQ_WITH_SPACES).append(buff1).append(SEP_EOL); + sprintf(buff1, FORMAT_STRING, templ->m_alphaKey[0].var.getMinimumValue()); sprintf(buff2, FORMAT_STRING, templ->m_alphaKey[0].var.getMaximumValue()); sprintf(buff3, "%d", templ->m_alphaKey[0].frame);