Skip to content

Conversation

@xezon
Copy link

@xezon xezon commented Jan 19, 2026

Merge with Rebase

This change merges all Particle System related code, including Particle Editor code. All individual changes are split into separate commits for best visibility.

Zero Hour gets

Generals gets

  • Removal of unused Drawable Particle Attachments
  • Addition of ParticleSystem::setSkipParentXfrm
  • Compile out of superfluous X and Y particle angles
  • Addition of SMUDGE particle type (used for Microwave Tank effect)
  • Optimization for particle update
  • Optimization for wind motion particle update when disabled
  • Change of math for aligning particle Z rotation with emitter direction

TODO

  • Add pull id to commits
  • Test all commits compile individually

@xezon xezon added this to the Code foundation build up milestone Jan 19, 2026
@xezon xezon added Bug Something is not working right, typically is user facing Major Severity: Minor < Major < Critical < Blocker Performance Is a performance concern Gen Relates to Generals ZH Relates to Zero Hour Unify Unifies code between Generals and Zero Hour labels Jan 19, 2026
@greptile-apps
Copy link

greptile-apps bot commented Jan 19, 2026

Greptile Summary

This PR successfully merges Particle System code between Generals and Zero Hour, bringing several optimizations and fixes to both codebases.

Major Changes:

  • Compiled out unused X/Y particle rotation angles using PARTICLE_USE_XY_ROTATION macro (set to 0), reducing memory footprint since particles only use Z-axis rotation due to billboard and ground-aligned placement modes
  • Removed unused Drawable particle attachments from Generals (the m_drawable pointer, attachToParticleSystem(), and detachFromParticleSystem() methods)
  • Added ParticleSystem::setSkipParentXfrm() method to both versions for controlling transform inheritance
  • Added SMUDGE particle type to Generals (already existed in Zero Hour, used for Microwave Tank effect)
  • Optimized particle updates: alpha updates only for non-additive shaders, wind motion guarded by early check
  • Fixed erroneous particle visibility thresholds in Zero Hour's Particle::isInvisible() (changed from 0.06f to 0.01f for additive RGB check, 0.02f to 0.01f for alpha check, 0.95f to 0.99f for subtractive check)
  • Changed angle calculation math to use new angleBetween() helper function for aligning particle Z rotation with emitter direction
  • Fixed indentation throughout Zero Hour particle code

Minor Issues Found:

  • GPL header in Generals/Code/Tools/ParticleEditor/Resource.h uses incorrect game title and copyright year
  • angleBetween() function computes vector lengths twice (once in guard, once in body) - minor performance opportunity

Confidence Score: 4/5

  • This PR is safe to merge with minor header corrections needed
  • The code changes are well-structured and achieve the stated goals of merging particle system code. The removal of drawable particle attachments is clean, the XY rotation compilation is properly guarded throughout, and the visibility threshold fixes address a real issue. Two minor corrections needed: incorrect GPL header metadata in Resource.h and a small performance optimization opportunity in angleBetween(). These don't affect runtime correctness but should be fixed for consistency.
  • Pay attention to Generals/Code/Tools/ParticleEditor/Resource.h which needs header corrections

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Include/GameClient/ParticleSys.h added PARTICLE_USE_XY_ROTATION macro to conditionally compile out unused X/Y rotation, added SMUDGE particle type, added setSkipParentXfrm method
Generals/Code/GameEngine/Source/GameClient/System/ParticleSys.cpp optimized particle updates (alpha only for non-additive, wind motion guarded), changed angle calculation to use angleBetween function, removed drawable management code, added PARTICLE_USE_XY_ROTATION conditionals, minor performance improvement opportunity in angleBetween
Generals/Code/Tools/ParticleEditor/Resource.h added GPL header with incorrect game title and copyright year
GeneralsMD/Code/GameEngine/Include/GameClient/ParticleSys.h added PARTICLE_USE_XY_ROTATION macro, fixed indentation for setSkipParentXfrm and m_skipParentXfrm, made update/isInvisible non-inline
GeneralsMD/Code/GameEngine/Source/GameClient/System/ParticleSys.cpp fixed particle visibility thresholds in isInvisible (0.06f->0.01f for additive, 0.02f->0.01f for alpha, 0.95f->0.99f for subtract), added PARTICLE_USE_XY_ROTATION conditionals, fixed indentation, minor performance improvement opportunity in angleBetween

Sequence Diagram

sequenceDiagram
    participant PST as ParticleSystemTemplate
    participant PS as ParticleSystem
    participant P as Particle
    participant PI as ParticleInfo
    
    Note over PST,P: Particle System Initialization
    PST->>PS: Constructor(template)
    PS->>PS: Initialize m_skipParentXfrm = false
    Note over PS: Set angle/angularRate from template<br/>(conditionally for XY if PARTICLE_USE_XY_ROTATION)
    
    Note over PST,P: Particle Creation
    PS->>PS: generateParticleInfo()
    PS->>PI: Create ParticleInfo
    Note over PI: Copy angle/angularRate values<br/>(conditionally for XY if PARTICLE_USE_XY_ROTATION)
    PI-->>PS: Return info
    PS->>P: Particle(system, info)
    Note over P: Initialize angles and rates<br/>(conditionally for XY if PARTICLE_USE_XY_ROTATION)
    
    Note over PST,P: Particle Update Loop
    PS->>PS: update()
    PS->>PS: Check if windMotion != NOT_USED
    alt Parent has transform
        PS->>PS: Apply parent transform<br/>(unless m_skipParentXfrm)
    end
    PS->>P: update()
    P->>P: Update position & velocity
    alt Wind motion enabled
        P->>P: doWindMotion()
    end
    P->>P: Update angleZ & angularRateZ<br/>(XY angles compiled out)
    alt particleUpTowardsEmitter
        P->>P: Calculate emitterDir
        P->>P: angleZ = angleBetween(upVec, emitterDir) + PI
    end
    P->>P: Update size & sizeRate
    alt Shader != ADDITIVE
        P->>P: Update alpha & alphaRate
    end
    P->>P: Update color via keyframes
    P->>P: isInvisible() check<br/>(fixed thresholds in ZH)
    alt Particle invisible or lifetime expired
        P-->>PS: Return false (dead)
    else
        P-->>PS: Return true (alive)
    end
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (2)

  1. Generals/Code/GameEngine/Source/GameClient/System/ParticleSys.cpp, line 217-220 (link)

    logic: The null check calls length() twice per vector unnecessarily, then calls it again below. Since length() involves a square root calculation (sqrt(x*x + y*y)), this is wasteful. Calculate once and check for zero length:

  2. GeneralsMD/Code/GameEngine/Source/GameClient/System/ParticleSys.cpp, line 217-220 (link)

    logic: The null check calls length() twice per vector unnecessarily, then calls it again below. Since length() involves a square root calculation (sqrt(x*x + y*y)), this is wasteful. Calculate once and check for zero length:

14 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@xezon
Copy link
Author

xezon commented Jan 19, 2026

Very fair comment, but this is EA code and outside the scope of this change.

Edit: I have prepared an optimization for later.

@xezon xezon force-pushed the xezon/unify-particlesystem-code branch from ff4e1b2 to df1cc68 Compare January 20, 2026 17:11
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (1)

  1. GeneralsMD/Code/GameEngine/Source/GameClient/System/ParticleSys.cpp, line 3457-3459 (link)

    style: inefficient to compute vecA->length() and vecB->length() twice each - the guard condition calls .length(), then it's called again below

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

14 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals Major Severity: Minor < Major < Critical < Blocker Performance Is a performance concern Unify Unifies code between Generals and Zero Hour ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants