|
@@ -1,94 +1,321 @@
|
|
module BABYLON {
|
|
module BABYLON {
|
|
|
|
+ /**
|
|
|
|
+ * Interface representing a particle system in Babylon.
|
|
|
|
+ * This groups the common functionalities that needs to be implemented in order to create a particle system.
|
|
|
|
+ * A particle system represents a way to manage particles (@see Particle) from their emission to their animation and rendering.
|
|
|
|
+ */
|
|
export interface IParticleSystem {
|
|
export interface IParticleSystem {
|
|
|
|
+ /**
|
|
|
|
+ * The id of the Particle system.
|
|
|
|
+ */
|
|
id: string;
|
|
id: string;
|
|
|
|
+ /**
|
|
|
|
+ * The name of the Particle system.
|
|
|
|
+ */
|
|
name: string;
|
|
name: string;
|
|
|
|
+ /**
|
|
|
|
+ * The emitter represents the Mesh or position we are attaching the particle system to.
|
|
|
|
+ */
|
|
emitter: Nullable<AbstractMesh | Vector3>;
|
|
emitter: Nullable<AbstractMesh | Vector3>;
|
|
|
|
+ /**
|
|
|
|
+ * The rendering group used by the Particle system to chose when to render.
|
|
|
|
+ */
|
|
renderingGroupId: number;
|
|
renderingGroupId: number;
|
|
|
|
+ /**
|
|
|
|
+ * The layer mask we are rendering the particles through.
|
|
|
|
+ */
|
|
layerMask: number;
|
|
layerMask: number;
|
|
|
|
+ /**
|
|
|
|
+ * Gets if the particle system has been started.
|
|
|
|
+ * @return true if the system has been started, otherwise false.
|
|
|
|
+ */
|
|
isStarted(): boolean;
|
|
isStarted(): boolean;
|
|
|
|
+ /**
|
|
|
|
+ * Animates the particle system for this frame.
|
|
|
|
+ */
|
|
animate(): void;
|
|
animate(): void;
|
|
|
|
+ /**
|
|
|
|
+ * Renders the particle system in its current state.
|
|
|
|
+ * @returns the current number of particles.
|
|
|
|
+ */
|
|
render(): number;
|
|
render(): number;
|
|
|
|
+ /**
|
|
|
|
+ * Dispose the particle system and frees its associated resources.
|
|
|
|
+ */
|
|
dispose(): void;
|
|
dispose(): void;
|
|
|
|
+ /**
|
|
|
|
+ * Clones the particle system.
|
|
|
|
+ * @param name The name of the cloned object
|
|
|
|
+ * @param newEmitter The new emitter to use
|
|
|
|
+ * @returns the cloned particle system
|
|
|
|
+ */
|
|
clone(name: string, newEmitter: any): Nullable<IParticleSystem>;
|
|
clone(name: string, newEmitter: any): Nullable<IParticleSystem>;
|
|
|
|
+ /**
|
|
|
|
+ * Serializes the particle system to a JSON object.
|
|
|
|
+ * @returns the JSON object
|
|
|
|
+ */
|
|
serialize(): any;
|
|
serialize(): any;
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Rebuild the particle system
|
|
|
|
+ */
|
|
rebuild(): void
|
|
rebuild(): void
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * This represents a particle system in Babylon.
|
|
|
|
+ * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
|
|
|
|
+ * Particles can take different shapes while emitted like box, sphere, cone or you can write your custom function.
|
|
|
|
+ * @example https://doc.babylonjs.com/babylon101/particles
|
|
|
|
+ */
|
|
export class ParticleSystem implements IDisposable, IAnimatable, IParticleSystem {
|
|
export class ParticleSystem implements IDisposable, IAnimatable, IParticleSystem {
|
|
- // Statics
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Source color is added to the destination color without alpha affecting the result.
|
|
|
|
+ */
|
|
public static BLENDMODE_ONEONE = 0;
|
|
public static BLENDMODE_ONEONE = 0;
|
|
|
|
+ /**
|
|
|
|
+ * Blend current color and particle color using particle’s alpha.
|
|
|
|
+ */
|
|
public static BLENDMODE_STANDARD = 1;
|
|
public static BLENDMODE_STANDARD = 1;
|
|
|
|
|
|
- // Members
|
|
|
|
|
|
+ /**
|
|
|
|
+ * List of animations used by the particle system.
|
|
|
|
+ */
|
|
public animations: Animation[] = [];
|
|
public animations: Animation[] = [];
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * The id of the Particle system.
|
|
|
|
+ */
|
|
public id: string;
|
|
public id: string;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The friendly name of the Particle system.
|
|
|
|
+ */
|
|
|
|
+ public name: string;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The rendering group used by the Particle system to chose when to render.
|
|
|
|
+ */
|
|
public renderingGroupId = 0;
|
|
public renderingGroupId = 0;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The emitter represents the Mesh or position we are attaching the particle system to.
|
|
|
|
+ */
|
|
public emitter: Nullable<AbstractMesh | Vector3> = null;
|
|
public emitter: Nullable<AbstractMesh | Vector3> = null;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The density of particles, the rate of particle flow
|
|
|
|
+ */
|
|
public emitRate = 10;
|
|
public emitRate = 10;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * If you want to launch only a few particles at once, that can be done, as well.
|
|
|
|
+ */
|
|
public manualEmitCount = -1;
|
|
public manualEmitCount = -1;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The overall motion speed (0.01 is default update speed, faster updates = faster animation)
|
|
|
|
+ */
|
|
public updateSpeed = 0.01;
|
|
public updateSpeed = 0.01;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The amount of time the particle system is running (depends of the overall speed above).
|
|
|
|
+ */
|
|
public targetStopDuration = 0;
|
|
public targetStopDuration = 0;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Specifies whether the particle system will be disposed once it reaches the end of the animation.
|
|
|
|
+ */
|
|
public disposeOnStop = false;
|
|
public disposeOnStop = false;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Minimum power of emitting particles.
|
|
|
|
+ */
|
|
public minEmitPower = 1;
|
|
public minEmitPower = 1;
|
|
|
|
+ /**
|
|
|
|
+ * Maximum power of emitting particles.
|
|
|
|
+ */
|
|
public maxEmitPower = 1;
|
|
public maxEmitPower = 1;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Minimum life time of emitting particles.
|
|
|
|
+ */
|
|
public minLifeTime = 1;
|
|
public minLifeTime = 1;
|
|
|
|
+ /**
|
|
|
|
+ * Maximum life time of emitting particles.
|
|
|
|
+ */
|
|
public maxLifeTime = 1;
|
|
public maxLifeTime = 1;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Minimum Size of emitting particles.
|
|
|
|
+ */
|
|
public minSize = 1;
|
|
public minSize = 1;
|
|
|
|
+ /**
|
|
|
|
+ * Maximum Size of emitting particles.
|
|
|
|
+ */
|
|
public maxSize = 1;
|
|
public maxSize = 1;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
|
|
|
|
+ */
|
|
public minAngularSpeed = 0;
|
|
public minAngularSpeed = 0;
|
|
|
|
+ /**
|
|
|
|
+ * Maximum angular speed of emitting particles (Z-axis rotation for each particle).
|
|
|
|
+ */
|
|
public maxAngularSpeed = 0;
|
|
public maxAngularSpeed = 0;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * The texture used to render each particle. (this can be a spritesheet)
|
|
|
|
+ */
|
|
public particleTexture: Nullable<Texture>;
|
|
public particleTexture: Nullable<Texture>;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * The layer mask we are rendering the particles through.
|
|
|
|
+ */
|
|
public layerMask: number = 0x0FFFFFFF;
|
|
public layerMask: number = 0x0FFFFFFF;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * This can help using your own shader to render the particle system.
|
|
|
|
+ * The according effect will be created
|
|
|
|
+ */
|
|
public customShader: any = null;
|
|
public customShader: any = null;
|
|
- public preventAutoStart: boolean = false;
|
|
|
|
-
|
|
|
|
- private _epsilon: number;
|
|
|
|
-
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * An event triggered when the system is disposed.
|
|
|
|
- * @type {BABYLON.Observable}
|
|
|
|
- */
|
|
|
|
- public onDisposeObservable = new Observable<ParticleSystem>();
|
|
|
|
-
|
|
|
|
- private _onDisposeObserver: Nullable<Observer<ParticleSystem>>;
|
|
|
|
- public set onDispose(callback: () => void) {
|
|
|
|
- if (this._onDisposeObserver) {
|
|
|
|
- this.onDisposeObservable.remove(this._onDisposeObserver);
|
|
|
|
- }
|
|
|
|
- this._onDisposeObserver = this.onDisposeObservable.add(callback);
|
|
|
|
- }
|
|
|
|
|
|
+ * By default particle system starts as soon as they are created. This prevents the
|
|
|
|
+ * automatic start to happen and let you decide when to start emitting particles.
|
|
|
|
+ */
|
|
|
|
+ public preventAutoStart: boolean = false;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * This function can be defined to provide custom update for active particles.
|
|
|
|
+ * This function will be called instead of regular update (age, position, color, etc.).
|
|
|
|
+ * Do not forget that this function will be called on every frame so try to keep it simple and fast :)
|
|
|
|
+ */
|
|
public updateFunction: (particles: Particle[]) => void;
|
|
public updateFunction: (particles: Particle[]) => void;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Callback triggered when the particle animation is ending.
|
|
|
|
+ */
|
|
public onAnimationEnd: Nullable<() => void> = null;
|
|
public onAnimationEnd: Nullable<() => void> = null;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Blend mode use to render the particle, it can be either ParticleSystem.BLENDMODE_ONEONE or ParticleSystem.BLENDMODE_STANDARD.
|
|
|
|
+ */
|
|
public blendMode = ParticleSystem.BLENDMODE_ONEONE;
|
|
public blendMode = ParticleSystem.BLENDMODE_ONEONE;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Forces the particle to write their depth information to the depth buffer. This can help preventing other draw calls
|
|
|
|
+ * to override the particles.
|
|
|
|
+ */
|
|
public forceDepthWrite = false;
|
|
public forceDepthWrite = false;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * You can use gravity if you want to give an orientation to your particles.
|
|
|
|
+ */
|
|
public gravity = Vector3.Zero();
|
|
public gravity = Vector3.Zero();
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
|
|
|
|
+ */
|
|
public direction1 = new Vector3(0, 1.0, 0);
|
|
public direction1 = new Vector3(0, 1.0, 0);
|
|
|
|
+ /**
|
|
|
|
+ * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
|
|
|
|
+ */
|
|
public direction2 = new Vector3(0, 1.0, 0);
|
|
public direction2 = new Vector3(0, 1.0, 0);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Minimum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
|
|
|
|
+ */
|
|
public minEmitBox = new Vector3(-0.5, -0.5, -0.5);
|
|
public minEmitBox = new Vector3(-0.5, -0.5, -0.5);
|
|
|
|
+ /**
|
|
|
|
+ * Maximum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
|
|
|
|
+ */
|
|
public maxEmitBox = new Vector3(0.5, 0.5, 0.5);
|
|
public maxEmitBox = new Vector3(0.5, 0.5, 0.5);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Random color of each particle after it has been emitted, between color1 and color2 vectors.
|
|
|
|
+ */
|
|
public color1 = new Color4(1.0, 1.0, 1.0, 1.0);
|
|
public color1 = new Color4(1.0, 1.0, 1.0, 1.0);
|
|
|
|
+ /**
|
|
|
|
+ * Random color of each particle after it has been emitted, between color1 and color2 vectors.
|
|
|
|
+ */
|
|
public color2 = new Color4(1.0, 1.0, 1.0, 1.0);
|
|
public color2 = new Color4(1.0, 1.0, 1.0, 1.0);
|
|
|
|
+ /**
|
|
|
|
+ * Color the particle will have at the end of its lifetime.
|
|
|
|
+ */
|
|
public colorDead = new Color4(0, 0, 0, 1.0);
|
|
public colorDead = new Color4(0, 0, 0, 1.0);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * An optional mask to filter some colors out of the texture, or filter a part of the alpha channel.
|
|
|
|
+ */
|
|
public textureMask = new Color4(1.0, 1.0, 1.0, 1.0);
|
|
public textureMask = new Color4(1.0, 1.0, 1.0, 1.0);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The particle emitter type defines the emitter used by the particle system.
|
|
|
|
+ * It can be for example box, sphere, or cone...
|
|
|
|
+ */
|
|
public particleEmitterType: IParticleEmitterType;
|
|
public particleEmitterType: IParticleEmitterType;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * This function can be defined to specify initial direction for every new particle.
|
|
|
|
+ * It by default use the emitterType defined function.
|
|
|
|
+ */
|
|
public startDirectionFunction: (emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle) => void;
|
|
public startDirectionFunction: (emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle) => void;
|
|
|
|
+ /**
|
|
|
|
+ * This function can be defined to specify initial position for every new particle.
|
|
|
|
+ * It by default use the emitterType defined function.
|
|
|
|
+ */
|
|
public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle) => void;
|
|
public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle) => void;
|
|
|
|
|
|
- private particles = new Array<Particle>();
|
|
|
|
|
|
+ /**
|
|
|
|
+ * If using a spritesheet (isAnimationSheetEnabled), defines if the sprite animation should loop between startSpriteCellID and endSpriteCellID or not.
|
|
|
|
+ */
|
|
|
|
+ public spriteCellLoop = true;
|
|
|
|
+ /**
|
|
|
|
+ * If using a spritesheet (isAnimationSheetEnabled) and spriteCellLoop defines the speed of the sprite loop.
|
|
|
|
+ */
|
|
|
|
+ public spriteCellChangeSpeed = 0;
|
|
|
|
+ /**
|
|
|
|
+ * If using a spritesheet (isAnimationSheetEnabled) and spriteCellLoop defines the first sprite cell to display.
|
|
|
|
+ */
|
|
|
|
+ public startSpriteCellID = 0;
|
|
|
|
+ /**
|
|
|
|
+ * If using a spritesheet (isAnimationSheetEnabled) and spriteCellLoop defines the last sprite cell to display.
|
|
|
|
+ */
|
|
|
|
+ public endSpriteCellID = 0;
|
|
|
|
+ /**
|
|
|
|
+ * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell width to use.
|
|
|
|
+ */
|
|
|
|
+ public spriteCellWidth = 0;
|
|
|
|
+ /**
|
|
|
|
+ * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell height to use.
|
|
|
|
+ */
|
|
|
|
+ public spriteCellHeight = 0;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * An event triggered when the system is disposed.
|
|
|
|
+ */
|
|
|
|
+ public onDisposeObservable = new Observable<ParticleSystem>();
|
|
|
|
|
|
|
|
+ private _onDisposeObserver: Nullable<Observer<ParticleSystem>>;
|
|
|
|
+ /**
|
|
|
|
+ * Sets a callback that will be triggered when the system is disposed.
|
|
|
|
+ */
|
|
|
|
+ public set onDispose(callback: () => void) {
|
|
|
|
+ if (this._onDisposeObserver) {
|
|
|
|
+ this.onDisposeObservable.remove(this._onDisposeObserver);
|
|
|
|
+ }
|
|
|
|
+ this._onDisposeObserver = this.onDisposeObservable.add(callback);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Gets wether an animation sprite sheet is enabled or not on the particle system.
|
|
|
|
+ */
|
|
|
|
+ public get isAnimationSheetEnabled(): Boolean {
|
|
|
|
+ return this._isAnimationSheetEnabled;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private _particles = new Array<Particle>();
|
|
|
|
+ private _epsilon: number;
|
|
private _capacity: number;
|
|
private _capacity: number;
|
|
private _scene: Scene;
|
|
private _scene: Scene;
|
|
private _stockParticles = new Array<Particle>();
|
|
private _stockParticles = new Array<Particle>();
|
|
@@ -100,40 +327,38 @@
|
|
private _effect: Effect;
|
|
private _effect: Effect;
|
|
private _customEffect: Nullable<Effect>;
|
|
private _customEffect: Nullable<Effect>;
|
|
private _cachedDefines: string;
|
|
private _cachedDefines: string;
|
|
-
|
|
|
|
private _scaledColorStep = new Color4(0, 0, 0, 0);
|
|
private _scaledColorStep = new Color4(0, 0, 0, 0);
|
|
private _colorDiff = new Color4(0, 0, 0, 0);
|
|
private _colorDiff = new Color4(0, 0, 0, 0);
|
|
private _scaledDirection = Vector3.Zero();
|
|
private _scaledDirection = Vector3.Zero();
|
|
private _scaledGravity = Vector3.Zero();
|
|
private _scaledGravity = Vector3.Zero();
|
|
private _currentRenderId = -1;
|
|
private _currentRenderId = -1;
|
|
-
|
|
|
|
private _alive: boolean;
|
|
private _alive: boolean;
|
|
private _started = false;
|
|
private _started = false;
|
|
private _stopped = false;
|
|
private _stopped = false;
|
|
private _actualFrame = 0;
|
|
private _actualFrame = 0;
|
|
private _scaledUpdateSpeed: number;
|
|
private _scaledUpdateSpeed: number;
|
|
-
|
|
|
|
- // sheet animation
|
|
|
|
- public startSpriteCellID = 0;
|
|
|
|
- public endSpriteCellID = 0;
|
|
|
|
- public spriteCellLoop = true;
|
|
|
|
- public spriteCellChangeSpeed = 0;
|
|
|
|
-
|
|
|
|
- public spriteCellWidth = 0;
|
|
|
|
- public spriteCellHeight = 0;
|
|
|
|
private _vertexBufferSize = 11;
|
|
private _vertexBufferSize = 11;
|
|
|
|
+ private _isAnimationSheetEnabled: boolean;
|
|
|
|
|
|
- public get isAnimationSheetEnabled(): Boolean {
|
|
|
|
- return this._isAnimationSheetEnabled;
|
|
|
|
- }
|
|
|
|
- // end of sheet animation
|
|
|
|
-
|
|
|
|
- constructor(public name: string, capacity: number, scene: Scene, customEffect: Nullable<Effect> = null, private _isAnimationSheetEnabled: boolean = false, epsilon: number = 0.01) {
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Instantiates a particle system.
|
|
|
|
+ * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
|
|
|
|
+ * @param name The name of the particle system
|
|
|
|
+ * @param capacity The max number of particles alive at the same time
|
|
|
|
+ * @param scene The scene the particle system belongs to
|
|
|
|
+ * @param customEffect a custom effect used to change the way particles are rendered by default
|
|
|
|
+ * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
|
|
|
|
+ * @param epsilon Offset used to render the particles
|
|
|
|
+ */
|
|
|
|
+ constructor(name: string, capacity: number, scene: Scene, customEffect: Nullable<Effect> = null, isAnimationSheetEnabled: boolean = false, epsilon: number = 0.01) {
|
|
this.id = name;
|
|
this.id = name;
|
|
|
|
+ this.name = name;
|
|
|
|
+
|
|
this._capacity = capacity;
|
|
this._capacity = capacity;
|
|
|
|
|
|
this._epsilon = epsilon;
|
|
this._epsilon = epsilon;
|
|
- if (_isAnimationSheetEnabled) {
|
|
|
|
|
|
+ this._isAnimationSheetEnabled = isAnimationSheetEnabled;
|
|
|
|
+ if (isAnimationSheetEnabled) {
|
|
this._vertexBufferSize = 12;
|
|
this._vertexBufferSize = 12;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -214,8 +439,13 @@
|
|
this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
|
|
this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * "Recycles" one of the particle by copying it back to the "stock" of particles and removing it from the active list.
|
|
|
|
+ * Its lifetime will start back at 0.
|
|
|
|
+ * @param particle The particle to recycle
|
|
|
|
+ */
|
|
public recycleParticle(particle: Particle): void {
|
|
public recycleParticle(particle: Particle): void {
|
|
- var lastParticle = <Particle>this.particles.pop();
|
|
|
|
|
|
+ var lastParticle = <Particle>this._particles.pop();
|
|
|
|
|
|
if (lastParticle !== particle) {
|
|
if (lastParticle !== particle) {
|
|
lastParticle.copyTo(particle);
|
|
lastParticle.copyTo(particle);
|
|
@@ -223,30 +453,49 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Gets the maximum number of particles active at the same time.
|
|
|
|
+ * @returns The max number of active particles.
|
|
|
|
+ */
|
|
public getCapacity(): number {
|
|
public getCapacity(): number {
|
|
return this._capacity;
|
|
return this._capacity;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Gets Wether there are still active particles in the system.
|
|
|
|
+ * @returns True if it is alive, otherwise false.
|
|
|
|
+ */
|
|
public isAlive(): boolean {
|
|
public isAlive(): boolean {
|
|
return this._alive;
|
|
return this._alive;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Gets Wether the system has been started.
|
|
|
|
+ * @returns True if it has been started, otherwise false.
|
|
|
|
+ */
|
|
public isStarted(): boolean {
|
|
public isStarted(): boolean {
|
|
return this._started;
|
|
return this._started;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Starts the particle system and begins to emit.
|
|
|
|
+ */
|
|
public start(): void {
|
|
public start(): void {
|
|
this._started = true;
|
|
this._started = true;
|
|
this._stopped = false;
|
|
this._stopped = false;
|
|
this._actualFrame = 0;
|
|
this._actualFrame = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Stops the particle system.
|
|
|
|
+ */
|
|
public stop(): void {
|
|
public stop(): void {
|
|
this._stopped = true;
|
|
this._stopped = true;
|
|
}
|
|
}
|
|
|
|
|
|
- // animation sheet
|
|
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * @ignore (for internal use only)
|
|
|
|
+ */
|
|
public _appendParticleVertex(index: number, particle: Particle, offsetX: number, offsetY: number): void {
|
|
public _appendParticleVertex(index: number, particle: Particle, offsetX: number, offsetY: number): void {
|
|
var offset = index * this._vertexBufferSize;
|
|
var offset = index * this._vertexBufferSize;
|
|
this._vertexData[offset] = particle.position.x;
|
|
this._vertexData[offset] = particle.position.x;
|
|
@@ -262,8 +511,10 @@
|
|
this._vertexData[offset + 10] = offsetY;
|
|
this._vertexData[offset + 10] = offsetY;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * @ignore (for internal use only)
|
|
|
|
+ */
|
|
public _appendParticleVertexWithAnimation(index: number, particle: Particle, offsetX: number, offsetY: number): void {
|
|
public _appendParticleVertexWithAnimation(index: number, particle: Particle, offsetX: number, offsetY: number): void {
|
|
-
|
|
|
|
if (offsetX === 0)
|
|
if (offsetX === 0)
|
|
offsetX = this._epsilon;
|
|
offsetX = this._epsilon;
|
|
else if (offsetX === 1)
|
|
else if (offsetX === 1)
|
|
@@ -291,9 +542,9 @@
|
|
|
|
|
|
private _update(newParticles: number): void {
|
|
private _update(newParticles: number): void {
|
|
// Update current
|
|
// Update current
|
|
- this._alive = this.particles.length > 0;
|
|
|
|
|
|
+ this._alive = this._particles.length > 0;
|
|
|
|
|
|
- this.updateFunction(this.particles);
|
|
|
|
|
|
+ this.updateFunction(this._particles);
|
|
|
|
|
|
// Add new ones
|
|
// Add new ones
|
|
var worldMatrix;
|
|
var worldMatrix;
|
|
@@ -308,7 +559,7 @@
|
|
|
|
|
|
var particle: Particle;
|
|
var particle: Particle;
|
|
for (var index = 0; index < newParticles; index++) {
|
|
for (var index = 0; index < newParticles; index++) {
|
|
- if (this.particles.length === this._capacity) {
|
|
|
|
|
|
+ if (this._particles.length === this._capacity) {
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -320,30 +571,30 @@
|
|
particle = new Particle(this);
|
|
particle = new Particle(this);
|
|
}
|
|
}
|
|
|
|
|
|
- this.particles.push(particle);
|
|
|
|
|
|
+ this._particles.push(particle);
|
|
|
|
|
|
- var emitPower = ParticleSystem.randomNumber(this.minEmitPower, this.maxEmitPower);
|
|
|
|
|
|
+ var emitPower = Scalar.RandomRange(this.minEmitPower, this.maxEmitPower);
|
|
|
|
|
|
- if (this.startDirectionFunction) {
|
|
|
|
- this.startDirectionFunction(emitPower, worldMatrix, particle.direction, particle);
|
|
|
|
|
|
+ if (this.startPositionFunction) {
|
|
|
|
+ this.startPositionFunction(worldMatrix, particle.position, particle);
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
- this.particleEmitterType.startDirectionFunction(emitPower, worldMatrix, particle.direction, particle);
|
|
|
|
|
|
+ this.particleEmitterType.startPositionFunction(worldMatrix, particle.position, particle);
|
|
}
|
|
}
|
|
|
|
|
|
- if (this.startPositionFunction) {
|
|
|
|
- this.startPositionFunction(worldMatrix, particle.position, particle);
|
|
|
|
|
|
+ if (this.startDirectionFunction) {
|
|
|
|
+ this.startDirectionFunction(emitPower, worldMatrix, particle.direction, particle);
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
- this.particleEmitterType.startPositionFunction(worldMatrix, particle.position, particle);
|
|
|
|
|
|
+ this.particleEmitterType.startDirectionFunction(emitPower, worldMatrix, particle.direction, particle);
|
|
}
|
|
}
|
|
|
|
|
|
- particle.lifeTime = ParticleSystem.randomNumber(this.minLifeTime, this.maxLifeTime);
|
|
|
|
|
|
+ particle.lifeTime = Scalar.RandomRange(this.minLifeTime, this.maxLifeTime);
|
|
|
|
|
|
- particle.size = ParticleSystem.randomNumber(this.minSize, this.maxSize);
|
|
|
|
- particle.angularSpeed = ParticleSystem.randomNumber(this.minAngularSpeed, this.maxAngularSpeed);
|
|
|
|
|
|
+ particle.size = Scalar.RandomRange(this.minSize, this.maxSize);
|
|
|
|
+ particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
|
|
|
|
|
|
- var step = ParticleSystem.randomNumber(0, 1.0);
|
|
|
|
|
|
+ var step = Scalar.RandomRange(0, 1.0);
|
|
|
|
|
|
Color4.LerpToRef(this.color1, this.color2, step, particle.color);
|
|
Color4.LerpToRef(this.color1, this.color2, step, particle.color);
|
|
|
|
|
|
@@ -394,6 +645,9 @@
|
|
return this._effect;
|
|
return this._effect;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
|
|
|
|
+ */
|
|
public animate(): void {
|
|
public animate(): void {
|
|
if (!this._started)
|
|
if (!this._started)
|
|
return;
|
|
return;
|
|
@@ -457,17 +711,17 @@
|
|
|
|
|
|
// Animation sheet
|
|
// Animation sheet
|
|
if (this._isAnimationSheetEnabled) {
|
|
if (this._isAnimationSheetEnabled) {
|
|
- this.appendParticleVertexes = this.appenedParticleVertexesWithSheet;
|
|
|
|
|
|
+ this._appendParticleVertexes = this._appenedParticleVertexesWithSheet;
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
- this.appendParticleVertexes = this.appenedParticleVertexesNoSheet;
|
|
|
|
|
|
+ this._appendParticleVertexes = this._appenedParticleVertexesNoSheet;
|
|
}
|
|
}
|
|
|
|
|
|
// Update VBO
|
|
// Update VBO
|
|
var offset = 0;
|
|
var offset = 0;
|
|
- for (var index = 0; index < this.particles.length; index++) {
|
|
|
|
- var particle = this.particles[index];
|
|
|
|
- this.appendParticleVertexes(offset, particle);
|
|
|
|
|
|
+ for (var index = 0; index < this._particles.length; index++) {
|
|
|
|
+ var particle = this._particles[index];
|
|
|
|
+ this._appendParticleVertexes(offset, particle);
|
|
offset += 4;
|
|
offset += 4;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -476,22 +730,25 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- public appendParticleVertexes: Nullable<(offset: number, particle: Particle) => void> = null;
|
|
|
|
|
|
+ private _appendParticleVertexes: Nullable<(offset: number, particle: Particle) => void> = null;
|
|
|
|
|
|
- private appenedParticleVertexesWithSheet(offset: number, particle: Particle) {
|
|
|
|
|
|
+ private _appenedParticleVertexesWithSheet(offset: number, particle: Particle) {
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 0, 0);
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 0, 0);
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 1, 0);
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 1, 0);
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 1, 1);
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 1, 1);
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 0, 1);
|
|
this._appendParticleVertexWithAnimation(offset++, particle, 0, 1);
|
|
}
|
|
}
|
|
|
|
|
|
- private appenedParticleVertexesNoSheet(offset: number, particle: Particle) {
|
|
|
|
|
|
+ private _appenedParticleVertexesNoSheet(offset: number, particle: Particle) {
|
|
this._appendParticleVertex(offset++, particle, 0, 0);
|
|
this._appendParticleVertex(offset++, particle, 0, 0);
|
|
this._appendParticleVertex(offset++, particle, 1, 0);
|
|
this._appendParticleVertex(offset++, particle, 1, 0);
|
|
this._appendParticleVertex(offset++, particle, 1, 1);
|
|
this._appendParticleVertex(offset++, particle, 1, 1);
|
|
this._appendParticleVertex(offset++, particle, 0, 1);
|
|
this._appendParticleVertex(offset++, particle, 0, 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Rebuilds the particle system.
|
|
|
|
+ */
|
|
public rebuild(): void {
|
|
public rebuild(): void {
|
|
this._createIndexBuffer();
|
|
this._createIndexBuffer();
|
|
|
|
|
|
@@ -500,11 +757,15 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Renders the particle system in its current state.
|
|
|
|
+ * @returns the current number of particles.
|
|
|
|
+ */
|
|
public render(): number {
|
|
public render(): number {
|
|
var effect = this._getEffect();
|
|
var effect = this._getEffect();
|
|
|
|
|
|
// Check
|
|
// Check
|
|
- if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady() || !this.particles.length)
|
|
|
|
|
|
+ if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady() || !this._particles.length)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
var engine = this._scene.getEngine();
|
|
var engine = this._scene.getEngine();
|
|
@@ -547,12 +808,15 @@
|
|
engine.setDepthWrite(true);
|
|
engine.setDepthWrite(true);
|
|
}
|
|
}
|
|
|
|
|
|
- engine.drawElementsType(Material.TriangleFillMode, 0, this.particles.length * 6);
|
|
|
|
|
|
+ engine.drawElementsType(Material.TriangleFillMode, 0, this._particles.length * 6);
|
|
engine.setAlphaMode(Engine.ALPHA_DISABLE);
|
|
engine.setAlphaMode(Engine.ALPHA_DISABLE);
|
|
|
|
|
|
- return this.particles.length;
|
|
|
|
|
|
+ return this._particles.length;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Disposes the particle system and free the associated resources.
|
|
|
|
+ */
|
|
public dispose(): void {
|
|
public dispose(): void {
|
|
if (this._vertexBuffer) {
|
|
if (this._vertexBuffer) {
|
|
this._vertexBuffer.dispose();
|
|
this._vertexBuffer.dispose();
|
|
@@ -580,18 +844,36 @@
|
|
this.onDisposeObservable.clear();
|
|
this.onDisposeObservable.clear();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Creates a Sphere Emitter for the particle system. (emits along the sphere radius)
|
|
|
|
+ * @param radius The radius of the sphere to emit from
|
|
|
|
+ * @returns the emitter
|
|
|
|
+ */
|
|
public createSphereEmitter(radius = 1): SphereParticleEmitter {
|
|
public createSphereEmitter(radius = 1): SphereParticleEmitter {
|
|
var particleEmitter = new SphereParticleEmitter(radius);
|
|
var particleEmitter = new SphereParticleEmitter(radius);
|
|
this.particleEmitterType = particleEmitter;
|
|
this.particleEmitterType = particleEmitter;
|
|
return particleEmitter;
|
|
return particleEmitter;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Creates a Directed Sphere Emitter for the particle system. (emits between direction1 and direction2)
|
|
|
|
+ * @param radius The radius of the sphere to emit from
|
|
|
|
+ * @param direction1 Particles are emitted between the direction1 and direction2 from within the sphere
|
|
|
|
+ * @param direction2 Particles are emitted between the direction1 and direction2 from within the sphere
|
|
|
|
+ * @returns the emitter
|
|
|
|
+ */
|
|
public createDirectedSphereEmitter(radius = 1, direction1 = new Vector3(0, 1.0, 0), direction2 = new Vector3(0, 1.0, 0)): SphereDirectedParticleEmitter {
|
|
public createDirectedSphereEmitter(radius = 1, direction1 = new Vector3(0, 1.0, 0), direction2 = new Vector3(0, 1.0, 0)): SphereDirectedParticleEmitter {
|
|
var particleEmitter = new SphereDirectedParticleEmitter(radius, direction1, direction2)
|
|
var particleEmitter = new SphereDirectedParticleEmitter(radius, direction1, direction2)
|
|
this.particleEmitterType = particleEmitter;
|
|
this.particleEmitterType = particleEmitter;
|
|
return particleEmitter;
|
|
return particleEmitter;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Creates a Cone Emitter for the particle system. (emits from the cone to the particle position)
|
|
|
|
+ * @param radius The radius of the cone to emit from
|
|
|
|
+ * @param angle The base angle of the cone
|
|
|
|
+ * @returns the emitter
|
|
|
|
+ */
|
|
public createConeEmitter(radius = 1, angle = Math.PI / 4): ConeParticleEmitter {
|
|
public createConeEmitter(radius = 1, angle = Math.PI / 4): ConeParticleEmitter {
|
|
var particleEmitter = new ConeParticleEmitter(radius, angle);
|
|
var particleEmitter = new ConeParticleEmitter(radius, angle);
|
|
this.particleEmitterType = particleEmitter;
|
|
this.particleEmitterType = particleEmitter;
|
|
@@ -599,6 +881,14 @@
|
|
}
|
|
}
|
|
|
|
|
|
// this method needs to be changed when breaking changes will be allowed to match the sphere and cone methods and properties direction1,2 and minEmitBox,maxEmitBox to be removed from the system.
|
|
// this method needs to be changed when breaking changes will be allowed to match the sphere and cone methods and properties direction1,2 and minEmitBox,maxEmitBox to be removed from the system.
|
|
|
|
+ /**
|
|
|
|
+ * Creates a Box Emitter for the particle system. (emits between direction1 and direction2 from withing the box defined by minEmitBox and maxEmitBox)
|
|
|
|
+ * @param direction1 Particles are emitted between the direction1 and direction2 from within the box
|
|
|
|
+ * @param direction2 Particles are emitted between the direction1 and direction2 from within the box
|
|
|
|
+ * @param minEmitBox Particles are emitted from the box between minEmitBox and maxEmitBox
|
|
|
|
+ * @param maxEmitBox Particles are emitted from the box between minEmitBox and maxEmitBox
|
|
|
|
+ * @returns the emitter
|
|
|
|
+ */
|
|
public createBoxEmitter(direction1: Vector3, direction2: Vector3, minEmitBox: Vector3, maxEmitBox: Vector3): BoxParticleEmitter {
|
|
public createBoxEmitter(direction1: Vector3, direction2: Vector3, minEmitBox: Vector3, maxEmitBox: Vector3): BoxParticleEmitter {
|
|
var particleEmitter = new BoxParticleEmitter(this);
|
|
var particleEmitter = new BoxParticleEmitter(this);
|
|
this.direction1 = direction1;
|
|
this.direction1 = direction1;
|
|
@@ -609,18 +899,12 @@
|
|
return particleEmitter;
|
|
return particleEmitter;
|
|
}
|
|
}
|
|
|
|
|
|
- public static randomNumber(min: number, max: number): number {
|
|
|
|
- if (min === max) {
|
|
|
|
- return (min);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var random = Math.random();
|
|
|
|
-
|
|
|
|
- return ((random * (max - min)) + min);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // Clone
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Clones the particle system.
|
|
|
|
+ * @param name The name of the cloned object
|
|
|
|
+ * @param newEmitter The new emitter to use
|
|
|
|
+ * @returns the cloned particle system
|
|
|
|
+ */
|
|
public clone(name: string, newEmitter: any): ParticleSystem {
|
|
public clone(name: string, newEmitter: any): ParticleSystem {
|
|
var custom: Nullable<Effect> = null;
|
|
var custom: Nullable<Effect> = null;
|
|
var program: any = null;
|
|
var program: any = null;
|
|
@@ -650,6 +934,10 @@
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Serializes the particle system to a JSON object.
|
|
|
|
+ * @returns the JSON object
|
|
|
|
+ */
|
|
public serialize(): any {
|
|
public serialize(): any {
|
|
var serializationObject: any = {};
|
|
var serializationObject: any = {};
|
|
|
|
|
|
@@ -711,6 +999,13 @@
|
|
return serializationObject;
|
|
return serializationObject;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Parses a JSON object to create a particle system.
|
|
|
|
+ * @param parsedParticleSystem The JSON object to parse
|
|
|
|
+ * @param scene The scene to create the particle system in
|
|
|
|
+ * @param rootUrl The root url to use to load external dependencies like texture
|
|
|
|
+ * @returns the Parsed particle system
|
|
|
|
+ */
|
|
public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): ParticleSystem {
|
|
public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): ParticleSystem {
|
|
var name = parsedParticleSystem.name;
|
|
var name = parsedParticleSystem.name;
|
|
var custom: Nullable<Effect> = null;
|
|
var custom: Nullable<Effect> = null;
|