Skip to content

refactor(particlesys): Cleanup retail volume particle depth handling - #3188

Open
Mauller wants to merge 1 commit into
TheSuperHackers:mainfrom
Mauller:Mauller/chore-cleanup-vol-particle-depth-handling
Open

refactor(particlesys): Cleanup retail volume particle depth handling#3188
Mauller wants to merge 1 commit into
TheSuperHackers:mainfrom
Mauller:Mauller/chore-cleanup-vol-particle-depth-handling

Conversation

@Mauller

@Mauller Mauller commented Aug 22, 2026

Copy link
Copy Markdown

This PR is a simple refactor to cleanup the handling of volume depth for volume type particles.

The particle system class originally returned a hard coded value from getVolumeParticleDepth() instead of returning the variable m_volumeParticleDepth.

This value is now retrieved from the particle template and exposed to configuration through the ini field of VolParticleDepth.

To preserve the retail particle behaviour, we identify the retail particles and set their particle depth to the original hard coded value. Otherwise the configured by ini value will be used.

@Mauller Mauller self-assigned this Aug 22, 2026
@Mauller Mauller added Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Refactor Edits the code with insignificant behavior changes, is never user facing labels Aug 22, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

ParticleSys: Make volume particle depth configurable while preserving retail defaults

🐞 Bug fix ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Read volume particle depth from particle templates instead of using a hard-coded constant.
• Add INI support for configuring volume particle depth via "VolParticleDepth".
• Preserve retail behavior by defaulting legacy volume particles to the original depth.
Diagram

graph TD
  A[/"INI: VolParticleDepth"/] --> B["ParticleSystemTemplate"] --> C["ParticleSystem ctor"] --> D["m_volumeParticleDepth"] --> E["getVolumeParticleDepth()"] --> F["Volume particle render"]
  C --> G{"PRESERVE_RETAIL_PARTICLES && depth==DEFAULT?"} --> H["Force OPTIMUM depth (6)"] --> D
  subgraph Legend
    direction LR
    _in[/"INI field"/] ~~~ _proc["Runtime/template" ] ~~~ _dec{"Decision"}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Move retail defaulting into template initialization/parsing
  • ➕ Keeps runtime constructor logic simpler (no retail-specific override in ParticleSystem ctor).
  • ➕ Ensures any other users of the template value see the same retail default consistently.
  • ➖ May require careful ordering to know particle type/name at parse time.
  • ➖ Could be harder to reason about if parsing and compatibility logic become intertwined.
2. Use OPTIMUM as the default and treat explicit INI as override
  • ➕ Eliminates the special-case check for DEFAULT implying retail.
  • ➕ Simplifies behavior: depth is always sensible unless explicitly changed.
  • ➖ Risky if DEFAULT has meaning beyond volume particles elsewhere.
  • ➖ Could unintentionally change non-retail assets that relied on DEFAULT semantics.

Recommendation: Current approach is reasonable for a low-risk refactor: it makes depth configurable while explicitly guarding retail parity. If retail-compatibility logic grows, consider relocating the defaulting into template initialization/parsing to keep ParticleSystem construction focused on value transfer.

Files changed (2) +10 / -2

Bug fix (1) +1 / -1
ParticleSys.hUse configured volume particle depth in accessor +1/-1

Use configured volume particle depth in accessor

• Updates ParticleSystem::getVolumeParticleDepth() to return the instance member m_volumeParticleDepth for VOLUME_PARTICLE instead of a hard-coded constant. Non-volume particles continue to return the default depth value.

Core/GameEngine/Include/GameClient/ParticleSys.h

Other (1) +9 / -1
ParticleSys.cppWire volume depth from template/INI and preserve retail default +9/-1

Wire volume depth from template/INI and preserve retail default

• Initializes ParticleSystem::m_volumeParticleDepth from the template value rather than forcing the default. Adds a retail-compatibility fallback that restores the legacy OPTIMUM depth when no INI depth is configured, and introduces the new INI field parse entry "VolParticleDepth" to populate ParticleSystemTemplate::m_volumeParticleDepth.

Core/GameEngine/Source/GameClient/System/ParticleSys.cpp

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Vol depth 0 ignored 🐞 Bug ≡ Correctness
Description
When PRESERVE_RETAIL_PARTICLES is enabled, any VOLUME_PARTICLE with m_volumeParticleDepth equal to
DEFAULT_VOLUME_PARTICLE_DEPTH (0) is forcibly changed to OPTIMUM_VOLUME_PARTICLE_DEPTH (6), so an
INI value of VolParticleDepth=0 cannot be honored. This makes it impossible to explicitly configure
depth 0 for volume particles and can unintentionally enable expensive volume rendering when authors
expected depth 0 to disable it.
Code

Core/GameEngine/Source/GameClient/System/ParticleSys.cpp[R1201-1204]

+	// In retail, volume particle depth was not setup through ini and was hard coded to a particle depth of 6
+	if (m_particleType == ParticleType::VOLUME_PARTICLE && m_volumeParticleDepth == DEFAULT_VOLUME_PARTICLE_DEPTH)
+	{
+		m_volumeParticleDepth = OPTIMUM_VOLUME_PARTICLE_DEPTH;
Evidence
The PR’s new retail-compatibility block forces depth 0 to 6 for all volume particles, while the
codebase documents 0 as the default “disable volume” depth and the renderer only uses volume
rendering when depth > 1. Because INI parsing cannot distinguish “unset” from an explicit 0, the
override prevents intentionally configuring depth 0.

Core/GameEngine/Source/GameClient/System/ParticleSys.cpp[1193-1205]
Core/GameEngine/Include/GameClient/ParticleSys.h[60-62]
Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp[328-334]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`VolParticleDepth` is parsed into `m_volumeParticleDepth` with a default of `DEFAULT_VOLUME_PARTICLE_DEPTH` (0). Under `PRESERVE_RETAIL_PARTICLES`, the constructor treats `m_volumeParticleDepth == 0` as “retail/unset” and overwrites it to 6, which also overwrites an explicit INI configuration of `VolParticleDepth=0`.

### Issue Context
- `DEFAULT_VOLUME_PARTICLE_DEPTH` is documented as “not to do the volume thing” (0).
- Volume rendering is only used when depth > 1.
- Current logic cannot tell whether depth==0 came from “field missing” or “field explicitly set to 0”.

### Fix Focus Areas
- Implement an explicit "was specified" signal (or sentinel value) for `VolParticleDepth`, and only apply the retail override when the field was not specified.
- file: Core/GameEngine/Source/GameClient/System/ParticleSys.cpp[2756-2760]
- file: Core/GameEngine/Source/GameClient/System/ParticleSys.cpp[1193-1205]
- file: Core/GameEngine/Include/GameClient/ParticleSys.h[60-63]

### Suggested approach
- Add a `bool m_volumeParticleDepthSpecified` (or similar) to `ParticleSystemTemplate` (or `ParticleSystemInfo` if appropriate).
- Replace the parse-table entry for `VolParticleDepth` with a custom parser that sets both `m_volumeParticleDepth` and `m_volumeParticleDepthSpecified=true`.
- Change the retail-compat block to:
 - if volume particle AND `!m_volumeParticleDepthSpecified` then set to `OPTIMUM_VOLUME_PARTICLE_DEPTH`.
 - otherwise honor the configured value, including 0.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can commit Qodo's fix in one click with committable suggestions (GitHub & GitLab)

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread Core/GameEngine/Source/GameClient/System/ParticleSys.cpp Outdated
Comment thread Core/GameEngine/Include/GameClient/ParticleSys.h
Comment thread Core/GameEngine/Source/GameClient/System/ParticleSys.cpp Outdated
// In retail, volume particle depth was not setup through ini and was hard coded to a particle depth of 6
if (m_particleType == ParticleType::VOLUME_PARTICLE && m_volumeParticleDepth == DEFAULT_VOLUME_PARTICLE_DEPTH)
{
m_volumeParticleDepth = OPTIMUM_VOLUME_PARTICLE_DEPTH;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks strange. I would expect it defaults to OPTIMUM_VOLUME_PARTICLE_DEPTH in the template, not every particle system.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all particle systems are volume particles so the default is zero / DEFAULT_VOLUME_PARTICLE_DEPTH.

It also act's as a guard in the rendering pathway as only volume particles with a depth greater than 1 get rendered.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But arent the particle templates stored in advance? Why not move it in there?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I managed to find where they are initialised and moved the PRESERVE_RETAIL_PATICLES block there for this and smudges.

@@ -1197,6 +1197,12 @@ ParticleSystem::ParticleSystem( const ParticleSystemTemplate *sysTemplate,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one maybe also needs moving into the template. Because it will always be the same.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@Mauller
Mauller force-pushed the Mauller/chore-cleanup-vol-particle-depth-handling branch from bbbc607 to a596261 Compare August 22, 2026 14:53
}

// In retail, volume particle depth was not setup through ini and was hard coded to a particle depth of 6
if (sysTemplate->m_particleType == ParticleSystemInfo::VOLUME_PARTICLE && sysTemplate->m_volumeParticleDepth == DEFAULT_VOLUME_PARTICLE_DEPTH)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means if someone now sets VolParticleDepth to 0 in the INI explicitly, then it overwrites it here. That does not seem right. Perhaps it should only set it if the INI field was not set. Or is 0 an invalid setting for Volume particles?

@Mauller Mauller Aug 22, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zero and One are invalid settings anyway.

There are tests in the render code for the volume depth being greater than One.

The value is also initialised to zero in the template.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the particle depth is set to zero or one, then the volume particle just gets rendered as a standard particle in this instance.

void PointGroupClass::RenderVolumeParticle(RenderInfoClass &rinfo, unsigned int depth )
{

	if ( depth <= 1 ) //oops,wrong number
	{
		Render( rinfo );
		return;
	}

Which kind of voids the point of it being a volume particle. But seems more like a safety net.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When looking further into the RenderVolumeParticle code the reciprocal of the depth is take which would cause a divide by zero error if 0 was a valid depth. Not sure why One is not considered though from the quick glance i took.

@OmarAglan

Copy link
Copy Markdown

The overall direction looks good, but I think two issues should be addressed before merging:

  1. The retail fallback cannot distinguish an omitted VolParticleDepth from an explicitly configured value of 0. Depths 0 and 1 are handled safely by the rendering path by falling back to standard particle rendering, so 0 appears to be a meaningful way to disable volume rendering. It may be cleaner to default volume templates to OPTIMUM_VOLUME_PARTICLE_DEPTH and allow the INI value to override it, including with 0.

  2. VolParticleDepth is parsed, but the Generals and Zero Hour _writeSingleParticleSystem() implementations do not write it. Because the particle editor regenerates ParticleSystem.ini, saving the file would silently discard a configured depth.

Other than these configuration and round-trip concerns, the change looks clean and the CI results are good.

@Mauller
Mauller force-pushed the Mauller/chore-cleanup-vol-particle-depth-handling branch from a596261 to 291a2e3 Compare August 22, 2026 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker Refactor Edits the code with insignificant behavior changes, is never user facing ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants