Skip to main content
The ParticleEngine manages all particle effects in the game, from breaking blocks to spell effects, providing visual feedback for player actions and environmental effects.

Overview

class ParticleEngine {
public:
    ParticleEngine(Level *level, Textures *textures);
    
    void add(shared_ptr<Particle> p);
    void tick();
    void render(shared_ptr<Entity> player, float a);
    void renderLit(shared_ptr<Entity> player, float a);
};
See: ParticleEngine.h:11

Texture Types

Particles are organized by texture source:
static const int MISC_TEXTURE = 0;           // particles.png
static const int TERRAIN_TEXTURE = 1;        // terrain.png
static const int ITEM_TEXTURE = 2;           // gui/items.png
static const int ENTITY_PARTICLE_TEXTURE = 3;// Entity-based particles
static const int DRAGON_BREATH_TEXTURE = 4;  // Dragon breath effect

static const int TEXTURE_COUNT = 5;
See: ParticleEngine.h:18-24

Particle Management

Storage Structure

Particles are stored in deques organized by dimension and texture type:
deque<shared_ptr<Particle>> particles[3][TEXTURE_COUNT];
Dimensions:
  • 0 = Overworld
  • 1 = Nether
  • 2 = The End
See: ParticleEngine.h:29

Particle Limits

To maintain performance, particle counts are capped:
static const int MAX_PARTICLES_PER_LAYER = 200;      // Standard limit
static const int MAX_DRAGON_BREATH_PARTICLES = 1000; // Special case
See: ParticleEngine.h:14-15 When limits are reached, oldest particles are removed (FIFO):
if ((t != DRAGON_BREATH_TEXTURE && particles[l][t].size() >= MAX_PARTICLES_PER_LAYER) 
    || particles[l][t].size() >= MAX_DRAGON_BREATH_PARTICLES)
    particles[l][t].pop_front();
See: ParticleEngine.cpp:34

Particle Base Class

class Particle : public Entity {
protected:
    int texX, texY;        // Texture coordinates
    float uo, vo;          // UV offsets
    int age;               // Current age in ticks
    int lifetime;          // Maximum lifetime
    float size;            // Particle size
    float gravity;         // Gravity effect
    float rCol, gCol, bCol;// Color
    float alpha;           // Transparency
    Icon *tex;            // Texture reference
public:
    static double xOff, yOff, zOff;  // Camera offset
};
See: Particle.h:11

Particle Lifecycle

  1. Creation - Particle spawned at position with velocity
  2. Tick - Update position, check collisions, age
  3. Render - Draw billboarded quad
  4. Removal - Age exceeds lifetime or marked removed

Rendering

virtual void render(Tesselator *t, float a, float xa, float ya, float za,
                   float xa2, float za2);
Particles are rendered as billboarded quads that always face the camera. Parameters:
  • t - Tesselator for vertex submission
  • a - Frame interpolation alpha
  • xa, za - Camera right vector
  • ya - Camera up vector
  • xa2, za2 - Camera forward vector
See: Particle.h:44

Particle Types

Terrain Particles

TerrainParticle

Used when breaking blocks:
class TerrainParticle : public Particle {
    // Uses terrain texture atlas
    // Shows block texture
};
Spawned via:
void ParticleEngine::destroy(int x, int y, int z, int tid, int data)
Creates 4×4×4 = 64 particles per block broken. See: ParticleEngine.cpp:171

Crack Particles

void ParticleEngine::crack(int x, int y, int z, int face)
Creates particles when hitting a block (mining). See: ParticleEngine.cpp:189

Misc Texture Particles

BubbleParticle

class BubbleParticle : public Particle {
    virtual void tick();
};
See: BubbleParticle.h:4
  • Rises in water
  • Pops at surface
  • Used in water and lava

SmokeParticle

  • Various smoke types
  • Torch smoke
  • Fire smoke
  • Velocity and gravity affected

FlameParticle

  • Fire particles
  • Torch flames
  • Entity burning effect

ExplodeParticle

  • Explosion debris
  • TNT explosions
  • Creeper explosions

HeartParticle

  • Animal breeding
  • Taming success
  • Rises upward

NoteParticle

  • Note block particles
  • Colored based on pitch
  • Musical note icon

NetherPortalParticle

  • Portal effect
  • Purple particles
  • Drifting motion

EnderParticle

  • Enderman teleport effect
  • End portal particles
  • Purple/black particles

SpellParticle

  • Potion effects
  • Beacon effects
  • Colored based on effect type

CritParticle / CritParticle2

  • Critical hit effect
  • Enhanced damage indicator

RedDustParticle

  • Redstone wire
  • Redstone torch
  • Red sparkles

SnowShovelParticle

  • Snow digging
  • White particles

SplashParticle

  • Water splash
  • Rain drops
  • Fishing bobber

DripParticle

  • Water dripping from blocks
  • Lava dripping
  • Falls downward

LavaParticle

  • Lava surface particles
  • Fire-like behavior

SuspendedParticle / SuspendedTownParticle

  • Floating particles in air
  • Village/town ambient particles

FootstepParticle

  • Walking particles
  • Various block types

WaterDropParticle

  • Water droplets
  • Rain particles

EchantmentTableParticle

  • Enchantment table runes
  • Float toward book
  • Galactic alphabet symbols

HugeExplosionParticle / HugeExplosionSeedParticle

  • Large explosion effects
  • Ender dragon death
  • TNT explosion core

DragonBreathParticle

class DragonBreathParticle : public Particle {
    // Dragon breath attack
    // Lingering effect
};
Has special higher limit (1000 particles).

Item Texture Particles

BreakingItemParticle

class BreakingItemParticle : public Particle {
    virtual int getParticleTexture() { return ITEM_TEXTURE; }
    virtual void render(Tesselator *t, float a, float xa, float ya, float za,
                       float xa2, float za2);
};
See: BreakingItemParticle.h:4 Used when:
  • Breaking tools
  • Eating food
  • Throwing items

Entity Particles

TakeAnimationParticle

  • Item pickup animation
  • Moves toward player
  • Rendered in lit pass

PlayerCloudParticle

  • Player-specific effects
  • Custom particle system

Rendering Process

Main Render

void ParticleEngine::render(shared_ptr<Entity> player, float a)
See: ParticleEngine.cpp:59 Process:
  1. Calculate camera vectors from Camera class
  2. Set static offsets for camera position
  3. For each texture type:
    • Skip entity particles (rendered separately)
    • Skip if no particles
    • Bind appropriate texture
    • Begin tesselation
    • Render all particles of this type
    • End tesselation

Lit Particle Render

void ParticleEngine::renderLit(shared_ptr<Entity> player, float a)
See: ParticleEngine.cpp:121 Renders entity particles (e.g., TakeAnimationParticle) with proper lighting.

Lighting Integration

if (SharedConstants::TEXTURE_LIGHTING) {
    t->tex2(p->getLightColor(a));
}
See: ParticleEngine.cpp:104 Particles can receive world lighting via the second texture unit.

Particle Update

void ParticleEngine::tick()
See: ParticleEngine.cpp:38 Process:
  1. Iterate all dimensions
  2. Iterate all texture types
  3. Tick each particle
  4. Remove particles marked as removed
  5. Use swap-and-pop for efficient removal
if (p->removed) {
    particles[l][tt][i] = particles[l][tt].back();
    particles[l][tt].pop_back();
    i--;  // Check swapped particle
}
See: ParticleEngine.cpp:48

Dimension Handling

Particles are dimension-aware:
int l = level->dimension->id == 0 ? 0 : 
        (level->dimension->id == -1 ? 1 : 2);
  • Dimension 0: Overworld
  • Dimension -1: Nether (stored in index 1)
  • Other: The End (stored in index 2)
See: ParticleEngine.cpp:33 Only particles in the current dimension are rendered.

Adding Particles

Direct Add

void ParticleEngine::add(shared_ptr<Particle> p)
See: ParticleEngine.h:36 Directly adds a particle to the appropriate queue.

Via LevelRenderer

void LevelRenderer::addParticle(ePARTICLE_TYPE eParticleType, 
                                double x, double y, double z,
                                double xa, double ya, double za);
Factory method that creates appropriate particle type.

Performance Considerations

Particle Batching

Particles are batched by texture type:
for (unsigned int i = 0; i < particles[l][tt].size(); i++) {
    if(t->hasMaxVertices()) {
        t->end();
        t->begin();
    }
    p->render(t, a, xa, ya, za, xa2, za2);
}
See: ParticleEngine.cpp:95 Minimizes texture binds and draw calls.

Memory Management

Using deque instead of vector:
  • Efficient front removal (FIFO)
  • No reallocation on push_back
  • Stable iteration

Camera Offset

Static camera offset avoids per-particle calculation:
Particle::xOff = (player->xOld + (player->x - player->xOld) * a);
Particle::yOff = (player->yOld + (player->y - player->yOld) * a);
Particle::zOff = (player->zOld + (player->z - player->zOld) * a);
See: ParticleEngine.cpp:69

Statistics

wstring ParticleEngine::countParticles()
See: ParticleEngine.cpp:208 Returns total particle count for current dimension (for debug display).

Integration with Game Systems

Block Breaking

When a block is destroyed:
particleEngine->destroy(x, y, z, tileId, data);
Creates terrain particles showing the block’s texture.

Block Mining

When hitting a block:
particleEngine->crack(x, y, z, face);
Creates a single particle from the hit face.

Level Events

Various level events trigger particles:
  • Redstone activation
  • Potion effects
  • Enchanting
  • Breeding
  • Weather effects

Custom Particles

To create a custom particle:
  1. Extend Particle base class
  2. Override tick() for custom behavior
  3. Override render() if needed
  4. Set texture via setMiscTex() or custom icon
  5. Add to engine via add()
Example structure:
class MyCustomParticle : public Particle {
public:
    MyCustomParticle(Level *level, double x, double y, double z)
        : Particle(level, x, y, z, 0, 0, 0) {
        // Set particle properties
        lifetime = 20;  // 1 second
        size = 0.5f;
        gravity = 0.1f;
        setMiscTex(0);  // Particle texture index
    }
    
    virtual void tick() override {
        // Custom behavior
        Particle::tick();  // Call base
    }
};

Build docs developers (and LLMs) love