Skip to content

Commit

Permalink
fix: [#3310] Add clearParticles() back with fixed logic
Browse files Browse the repository at this point in the history
closes: #3310
  • Loading branch information
eonarheim committed Dec 6, 2024
1 parent 7d0b479 commit d919a34
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ are doing mtv adjustments during precollision.

### Fixed

- Fixed issue where `ex.ParticleEmitter.clearParticles()` did not work
- Fixed issue where the pointer `lastWorldPos` was not updated when the current `Camera` moved
- Fixed issue where `cancel()`'d events still bubbled to the top level input handlers
- Fixed issue where unexpected html HTML content from an image would silently hang the loader
Expand Down
4 changes: 4 additions & 0 deletions src/engine/Particles/GpuParticleEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ export class GpuParticleEmitter extends Actor {
this.renderer.emitParticles(particleCount);
}

public clearParticles() {
this.renderer.clearParticles();
}

draw(ctx: ExcaliburGraphicsContextWebGL, elapsed: number) {
ctx.draw<ParticleRenderer>('ex.particle', this.renderer, elapsed);
}
Expand Down
15 changes: 14 additions & 1 deletion src/engine/Particles/GpuParticleRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ export class GpuParticleRenderer {
this._initialized = true;
}

private _clearRequested = false;
clearParticles() {
this._particleData.fill(0);
this._clearRequested = true;
}

private _emitted: [life: number, index: number][] = [];
emitParticles(particleCount: number) {
const startIndex = this._particleIndex;
Expand Down Expand Up @@ -264,7 +270,14 @@ export class GpuParticleRenderer {
draw(gl: WebGL2RenderingContext) {
if (this._initialized) {
// Emit
this._uploadEmitted(gl);
if (this._clearRequested) {
gl.bindBuffer(gl.ARRAY_BUFFER, this._buffers[(this._drawIndex + 1) % 2]);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._particleData);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
this._clearRequested = false;
} else {
this._uploadEmitted(gl);
}

// Bind one buffer to ARRAY_BUFFER and the other to transform feedback buffer
gl.bindVertexArray(this._currentVao);
Expand Down
13 changes: 13 additions & 0 deletions src/engine/Particles/ParticleEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ export class ParticleEmitter extends Actor {
this.deadParticles.push(particle);
}

private _activeParticles: Particle[] = [];

/**
* Causes the emitter to emit particles
* @param particleCount Number of particles to emit right now
Expand All @@ -110,6 +112,13 @@ export class ParticleEmitter extends Actor {
this.addChild(p);
}
}
this._activeParticles.push(p);
}
}

public clearParticles() {
for (let i = 0; i < this._activeParticles.length; i++) {
this.removeParticle(this._activeParticles[i]);
}
}

Expand Down Expand Up @@ -177,6 +186,10 @@ export class ParticleEmitter extends Actor {
this.scene.world.remove(this.deadParticles[i], false);
this._particlePool.return(this.deadParticles[i]);
}
const index = this._activeParticles.indexOf(this.deadParticles[i]);
if (index > -1) {
this._activeParticles.splice(index, 1);
}
}
this.deadParticles.length = 0;
}
Expand Down
43 changes: 43 additions & 0 deletions src/spec/GpuParticleSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,49 @@ describe('A GPU particle', () => {
await expectAsync(engine.canvas).toEqualImage('src/spec/images/GpuParticlesSpec/particles.png');
});

it('should clear particles', async () => {
const emitter = new ex.GpuParticleEmitter({
pos: new ex.Vector(400, 100),
width: 20,
height: 30,
isEmitting: true,
emitRate: 5,
particle: {
minSpeed: 100,
maxSpeed: 200,
acc: ex.Vector.Zero.clone(),
minAngle: 0,
maxAngle: Math.PI / 2,
life: 4000,
opacity: 0.5,
fade: false,
startSize: 30,
endSize: 40,
beginColor: ex.Color.Red.clone(),
endColor: ex.Color.Blue.clone(),
graphic: null,
angularVelocity: 3,
randomRotation: false
},
focus: null,
focusAccel: null,
emitterType: ex.EmitterType.Circle,
radius: 20,
random: new ex.Random(1337)
});
engine.backgroundColor = ex.Color.Transparent;
engine.add(emitter);
emitter.emitParticles(10);
emitter.clearParticles();

engine.currentScene.update(engine, 100);
engine.currentScene.update(engine, 100);
engine.currentScene.update(engine, 100);
engine.currentScene.draw(engine.graphicsContext, 100);
engine.graphicsContext.flush();
await expectAsync(engine.canvas).toEqualImage('src/spec/images/GpuParticlesSpec/clear.png');
});

it("should emit particles and wrap it's ring buffer", async () => {
const emitter = new ex.GpuParticleEmitter({
pos: new ex.Vector(400, 100),
Expand Down
43 changes: 43 additions & 0 deletions src/spec/ParticleSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,49 @@ describe('A particle', () => {
await expectAsync(engine.canvas).toEqualImage('src/spec/images/ParticleSpec/Particles.png');
});

it('should clear particles', async () => {
const emitter = new ex.ParticleEmitter({
pos: new ex.Vector(400, 100),
width: 20,
height: 30,
isEmitting: true,
emitRate: 5,
particle: {
minSpeed: 100,
maxSpeed: 200,
acc: ex.Vector.Zero.clone(),
minAngle: 0,
maxAngle: Math.PI / 2,
life: 4000,
opacity: 0.5,
fade: false,
startSize: 30,
endSize: 40,
beginColor: ex.Color.Red.clone(),
endColor: ex.Color.Blue.clone(),
graphic: null,
angularVelocity: 3,
randomRotation: false
},
focus: null,
focusAccel: null,
emitterType: ex.EmitterType.Circle,
radius: 20,
random: new ex.Random(1337)
});
engine.backgroundColor = ex.Color.Transparent;
engine.add(emitter);
emitter.emitParticles(10);
emitter.clearParticles();

engine.currentScene.update(engine, 100);
engine.currentScene.update(engine, 100);
engine.currentScene.update(engine, 100);
engine.currentScene.draw(engine.graphicsContext, 100);
engine.graphicsContext.flush();
await expectAsync(engine.canvas).toEqualImage('src/spec/images/ParticleSpec/clear.png');
});

it('can be parented', async () => {
const emitter = new ex.ParticleEmitter({
pos: new ex.Vector(0, 0),
Expand Down
Binary file added src/spec/images/GpuParticlesSpec/clear.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/spec/images/ParticleSpec/clear.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d919a34

Please sign in to comment.