diff --git a/packages/core/src/particle/modules/CustomDataModule.ts b/packages/core/src/particle/modules/CustomDataModule.ts index 4ad2fa6fc9..8504e33505 100644 --- a/packages/core/src/particle/modules/CustomDataModule.ts +++ b/packages/core/src/particle/modules/CustomDataModule.ts @@ -186,6 +186,18 @@ export class CustomDataModule extends ParticleGeneratorModule { this._gradients.delete(name); } + /** + * Remove all custom data streams. + */ + clear(): void { + for (const name of this._curves.keys()) { + this.removeCurve(name); + } + for (const name of this._gradients.keys()) { + this.removeGradient(name); + } + } + /** * @internal */ diff --git a/packages/core/src/particle/modules/SubEmittersModule.ts b/packages/core/src/particle/modules/SubEmittersModule.ts index 90d19e5feb..dd090458ab 100644 --- a/packages/core/src/particle/modules/SubEmittersModule.ts +++ b/packages/core/src/particle/modules/SubEmittersModule.ts @@ -95,6 +95,17 @@ export class SubEmittersModule extends ParticleGeneratorModule { this._generator._setTransformFeedback(); } + /** + * Remove all sub-emitter slots. + */ + clear(): void { + if (this._subEmitters.length === 0) { + return; + } + this._subEmitters.length = 0; + this._generator._setTransformFeedback(); + } + override get enabled(): boolean { return this._enabled && this._generator._renderer.engine._hardwareRenderer.isWebGL2; } diff --git a/tests/src/core/particle/CustomData.test.ts b/tests/src/core/particle/CustomData.test.ts index 540f3b0461..1c66d76129 100644 --- a/tests/src/core/particle/CustomData.test.ts +++ b/tests/src/core/particle/CustomData.test.ts @@ -163,6 +163,17 @@ describe("CustomDataModule", function () { expect(customData.gradients.size).to.eq(0); }); + it("clear removes every stream", function () { + const customData = particleRenderer.generator.customData; + customData.addCurve("A", new ParticleCompositeCurve(1)); + customData.addGradient("B", new ParticleCompositeGradient(new Color())); + + customData.clear(); + + expect(customData.curves.size).to.eq(0); + expect(customData.gradients.size).to.eq(0); + }); + it("removeCurve / removeGradient zero out shaderData uniforms", function () { const customData = particleRenderer.generator.customData; const shaderData = particleRenderer.shaderData; diff --git a/tests/src/core/particle/SubEmitter.test.ts b/tests/src/core/particle/SubEmitter.test.ts index dc393becec..c6560463f1 100644 --- a/tests/src/core/particle/SubEmitter.test.ts +++ b/tests/src/core/particle/SubEmitter.test.ts @@ -90,6 +90,23 @@ describe("SubEmitter", () => { child.entity.destroy(); }); + it("clear removes every sub-emitter slot", () => { + const parent = createParticleRenderer(engine, "Parent_Clear"); + const child = createParticleRenderer(engine, "Child_Clear"); + + parent.generator.subEmitters.enabled = true; + parent.generator.subEmitters.addSubEmitter(child, ParticleSubEmitterType.Death); + expect((parent.generator as any)._useTransformFeedback).to.equal(true); + + parent.generator.subEmitters.clear(); + + expect(parent.generator.subEmitters.subEmitters).to.have.length(0); + expect((parent.generator as any)._useTransformFeedback).to.equal(false); + + parent.entity.destroy(); + child.entity.destroy(); + }); + it("Sub system's own EmissionModule does not double-fire when sub-emit drives it", () => { // The target renderer has its own t=0 burst AND is auto-playing on enable. // The slot must NOT read that burst and re-fire — sub system's own emission