|
@@ -1,4 +1,65 @@
|
|
|
module BABYLON {
|
|
|
+ /**
|
|
|
+ * Type of sub emitter
|
|
|
+ */
|
|
|
+ export enum SubEmitterType {
|
|
|
+ /**
|
|
|
+ * Attached to the particle over it's lifetime
|
|
|
+ */
|
|
|
+ ATTACHED,
|
|
|
+ /**
|
|
|
+ * Created when the particle dies
|
|
|
+ */
|
|
|
+ END
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sub emitter class used to emit particles from an existing particle
|
|
|
+ */
|
|
|
+ export class SubEmitter {
|
|
|
+ /**
|
|
|
+ * Type of the submitter (Default: END)
|
|
|
+ */
|
|
|
+ public type = SubEmitterType.END;
|
|
|
+ /**
|
|
|
+ * If the particle should inherit the direction from the particle it's attached to. (+Y will face the direction the particle is moving) (Default: false)
|
|
|
+ * Note: This only is supported when using an emitter of type Mesh
|
|
|
+ */
|
|
|
+ public inheritDirection = false;
|
|
|
+ /**
|
|
|
+ * How much of the attached particles speed should be added to the sub emitted particle (default: 0)
|
|
|
+ */
|
|
|
+ public inheritedVelocityAmount = 0;
|
|
|
+ /**
|
|
|
+ * Creates a sub emitter
|
|
|
+ * @param particleSystem the particle system to be used by the sub emitter
|
|
|
+ */
|
|
|
+ constructor(public particleSystem:ParticleSystem){
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Clones the sub emitter
|
|
|
+ */
|
|
|
+ clone():SubEmitter{
|
|
|
+ // Clone particle system
|
|
|
+ var emitter = this.particleSystem.emitter;
|
|
|
+ if(!emitter){
|
|
|
+ emitter = new Vector3();
|
|
|
+ }else if(emitter instanceof Vector3){
|
|
|
+ emitter = emitter.clone();
|
|
|
+ }else if(emitter instanceof AbstractMesh){
|
|
|
+ emitter = new Mesh("", emitter._scene);
|
|
|
+ }
|
|
|
+ var clone = new SubEmitter(this.particleSystem.clone("",emitter));
|
|
|
+
|
|
|
+ // Clone properties
|
|
|
+ clone.type = this.type;
|
|
|
+ clone.inheritDirection = this.inheritDirection;
|
|
|
+ clone.inheritedVelocityAmount = this.inheritedVelocityAmount;
|
|
|
+
|
|
|
+ clone.particleSystem._disposeEmitterOnDispose = true;
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* This represents a particle system in Babylon.
|
|
@@ -28,6 +89,10 @@
|
|
|
public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle) => void;
|
|
|
|
|
|
/**
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _inheritedVelocityOffset = new BABYLON.Vector3();
|
|
|
+ /**
|
|
|
* An event triggered when the system is disposed
|
|
|
*/
|
|
|
public onDisposeObservable = new Observable<ParticleSystem>();
|
|
@@ -88,9 +153,17 @@
|
|
|
|
|
|
// Sub-emitters
|
|
|
/**
|
|
|
- * this is the Sub-emitters templates that will be used to generate particle system when the particle dies, this property is used by the root particle system only.
|
|
|
+ * The Sub-emitters templates that will be used to generate the sub particle system to be associated with the system, this property is used by the root particle system only.
|
|
|
+ * When a particle is spawned, an array will be chosen at random and all the emitters in that array will be attached to the particle. (Default: [])
|
|
|
*/
|
|
|
- public subEmitters: ParticleSystem[];
|
|
|
+ public subEmitters: Array< ParticleSystem | SubEmitter | Array<SubEmitter> >;
|
|
|
+ // the subEmitters field above converted to a constant type
|
|
|
+ private _subEmitters: Array<Array<SubEmitter>>;
|
|
|
+ /**
|
|
|
+ * @hidden
|
|
|
+ * If the particle systems emitter should be disposed when the particle system is disposed
|
|
|
+ */
|
|
|
+ public _disposeEmitterOnDispose = false;
|
|
|
/**
|
|
|
* The current active Sub-systems, this property is used by the root particle system only.
|
|
|
*/
|
|
@@ -165,6 +238,13 @@
|
|
|
if (particle.age >= particle.lifeTime) { // Recycle by swapping with last particle
|
|
|
this._emitFromParticle(particle);
|
|
|
this.recycleParticle(particle);
|
|
|
+ if(particle._attachedSubEmitters){
|
|
|
+ particle._attachedSubEmitters.forEach((subEmitter)=>{
|
|
|
+ subEmitter.particleSystem.disposeOnStop = true;
|
|
|
+ subEmitter.particleSystem.stop();
|
|
|
+ });
|
|
|
+ particle._attachedSubEmitters = null;
|
|
|
+ }
|
|
|
index--;
|
|
|
continue;
|
|
|
}
|
|
@@ -296,11 +376,38 @@
|
|
|
if (this._isAnimationSheetEnabled) {
|
|
|
particle.updateCellIndex();
|
|
|
}
|
|
|
+
|
|
|
+ // Update the position of the attached sub-emitters to match their attached particle
|
|
|
+ if(particle._attachedSubEmitters && particle._attachedSubEmitters.length > 0){
|
|
|
+ particle._attachedSubEmitters.forEach((subEmitter)=>{
|
|
|
+ ParticleSystem._InheritParticleInfoToSubEmitter(subEmitter, particle);
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private static _InheritParticleInfoToSubEmitter(subEmitter:SubEmitter, particle:Particle){
|
|
|
+ if ((<AbstractMesh>subEmitter.particleSystem.emitter).position) {
|
|
|
+ var emitterMesh = (<AbstractMesh>subEmitter.particleSystem.emitter);
|
|
|
+ emitterMesh.position.copyFrom(particle.position);
|
|
|
+ if(subEmitter.inheritDirection){
|
|
|
+ emitterMesh.position.subtractToRef(particle.direction, BABYLON.Tmp.Vector3[0]);
|
|
|
+ // Look at using Y as forward
|
|
|
+ emitterMesh.lookAt(BABYLON.Tmp.Vector3[0], 0, Math.PI/2);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ var emitterPosition = (<Vector3>subEmitter.particleSystem.emitter);
|
|
|
+ emitterPosition.copyFrom(particle.position);
|
|
|
+ if(subEmitter.inheritDirection){
|
|
|
+ Tools.Warn("subEmitter.inheritDirection is not supported with non-mesh emitter type");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Set inheritedVelocityOffset to be used when new particles are created
|
|
|
+ particle.direction.scaleToRef(subEmitter.inheritedVelocityAmount/2, Tmp.Vector3[0]);
|
|
|
+ subEmitter.particleSystem._inheritedVelocityOffset.copyFrom(Tmp.Vector3[0]);
|
|
|
+ }
|
|
|
|
|
|
private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number, factor2?: number) {
|
|
|
let newGradient = new FactorGradient();
|
|
@@ -769,11 +876,24 @@
|
|
|
}, delay);
|
|
|
return;
|
|
|
}
|
|
|
+ // Convert the subEmitters field to the constant type field _subEmitters
|
|
|
+ this._subEmitters = new Array<Array<SubEmitter>>();
|
|
|
+ if(this.subEmitters){
|
|
|
+ this.subEmitters.forEach((subEmitter)=>{
|
|
|
+ if(subEmitter instanceof ParticleSystem){
|
|
|
+ this._subEmitters.push([new SubEmitter(subEmitter)]);
|
|
|
+ }else if(subEmitter instanceof SubEmitter){
|
|
|
+ this._subEmitters.push([subEmitter]);
|
|
|
+ }else if(subEmitter instanceof Array){
|
|
|
+ this._subEmitters.push(subEmitter);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
this._started = true;
|
|
|
this._stopped = false;
|
|
|
this._actualFrame = 0;
|
|
|
- if (this.subEmitters && this.subEmitters.length != 0) {
|
|
|
+ if (this._subEmitters && this._subEmitters.length != 0) {
|
|
|
this.activeSubSystems = new Array<ParticleSystem>();
|
|
|
}
|
|
|
|
|
@@ -865,11 +985,12 @@
|
|
|
* Its lifetime will start back at 0.
|
|
|
*/
|
|
|
public recycleParticle: (particle: Particle) => void = (particle) => {
|
|
|
- var lastParticle = <Particle>this._particles.pop();
|
|
|
- if (lastParticle !== particle) {
|
|
|
- lastParticle.copyTo(particle);
|
|
|
+ // move particle from activeParticle list to stock particles
|
|
|
+ var index = this.particles.indexOf(particle);
|
|
|
+ if(index > -1){
|
|
|
+ this.particles.splice(index, 1);
|
|
|
}
|
|
|
- this._stockParticles.push(lastParticle);
|
|
|
+ this._stockParticles.push(particle);
|
|
|
};
|
|
|
|
|
|
private _stopSubEmitters(): void {
|
|
@@ -892,6 +1013,19 @@
|
|
|
} else {
|
|
|
particle = new Particle(this);
|
|
|
}
|
|
|
+
|
|
|
+ // Attach emitters
|
|
|
+ if(this._subEmitters && this._subEmitters.length > 0){
|
|
|
+ var subEmitters = this._subEmitters[Math.floor(Math.random() * this._subEmitters.length)];
|
|
|
+ particle._attachedSubEmitters = [];
|
|
|
+ subEmitters.forEach((subEmitter)=>{
|
|
|
+ if(subEmitter.type == SubEmitterType.ATTACHED){
|
|
|
+ var newEmitter = subEmitter.clone();
|
|
|
+ (<Array<SubEmitter>>particle._attachedSubEmitters).push(newEmitter);
|
|
|
+ newEmitter.particleSystem.start();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
return particle;
|
|
|
}
|
|
|
|
|
@@ -907,16 +1041,20 @@
|
|
|
}
|
|
|
|
|
|
private _emitFromParticle: (particle: Particle) => void = (particle) => {
|
|
|
- if (!this.subEmitters || this.subEmitters.length === 0) {
|
|
|
+ if (!this._subEmitters || this._subEmitters.length === 0) {
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- var templateIndex = Math.floor(Math.random() * this.subEmitters.length);
|
|
|
-
|
|
|
- var subSystem = this.subEmitters[templateIndex].clone(this.name + "_sub", particle.position.clone());
|
|
|
- subSystem._rootParticleSystem = this;
|
|
|
- this.activeSubSystems.push(subSystem);
|
|
|
- subSystem.start();
|
|
|
+ var templateIndex = Math.floor(Math.random() * this._subEmitters.length);
|
|
|
+
|
|
|
+ this._subEmitters[templateIndex].forEach((subEmitter)=>{
|
|
|
+ if(subEmitter.type == SubEmitterType.END){
|
|
|
+ var subSystem = subEmitter.clone();
|
|
|
+ ParticleSystem._InheritParticleInfoToSubEmitter(subSystem, particle);
|
|
|
+ subSystem.particleSystem._rootParticleSystem = this;
|
|
|
+ this.activeSubSystems.push(subSystem.particleSystem);
|
|
|
+ subSystem.particleSystem.start();
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
// End of sub system methods
|
|
@@ -1099,6 +1237,9 @@
|
|
|
particle._initialStartSpriteCellID = this.startSpriteCellID;
|
|
|
particle._initialEndSpriteCellID = this.endSpriteCellID;
|
|
|
}
|
|
|
+
|
|
|
+ // Inherited Velocity
|
|
|
+ particle.direction.addInPlace(this._inheritedVelocityOffset);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1447,6 +1588,10 @@
|
|
|
|
|
|
this._removeFromRoot();
|
|
|
|
|
|
+ if(this._disposeEmitterOnDispose && !(<AbstractMesh>this.emitter).isDisposed){
|
|
|
+ (<AbstractMesh>this.emitter).dispose();
|
|
|
+ }
|
|
|
+
|
|
|
// Remove from scene
|
|
|
var index = this._scene.particleSystems.indexOf(this);
|
|
|
if (index > -1) {
|
|
@@ -1489,6 +1634,53 @@
|
|
|
result.particleTexture = new Texture(this.particleTexture.url, this._scene);
|
|
|
}
|
|
|
|
|
|
+ // Clone gradients
|
|
|
+ if(this._colorGradients){
|
|
|
+ this._colorGradients.forEach((v)=>{
|
|
|
+ result.addColorGradient(v.gradient, v.color1, v.color2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._dragGradients){
|
|
|
+ this._dragGradients.forEach((v)=>{
|
|
|
+ result.addDragGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._angularSpeedGradients){
|
|
|
+ this._angularSpeedGradients.forEach((v)=>{
|
|
|
+ result.addAngularSpeedGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._emitRateGradients){
|
|
|
+ this._emitRateGradients.forEach((v)=>{
|
|
|
+ result.addEmitRateGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._lifeTimeGradients){
|
|
|
+ this._lifeTimeGradients.forEach((v)=>{
|
|
|
+ result.addLifeTimeGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._limitVelocityGradients){
|
|
|
+ this._limitVelocityGradients.forEach((v)=>{
|
|
|
+ result.addLimitVelocityGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._sizeGradients){
|
|
|
+ this._sizeGradients.forEach((v)=>{
|
|
|
+ result.addSizeGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._startSizeGradients){
|
|
|
+ this._startSizeGradients.forEach((v)=>{
|
|
|
+ result.addStartSizeGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(this._velocityGradients){
|
|
|
+ this._velocityGradients.forEach((v)=>{
|
|
|
+ result.addVelocityGradient(v.gradient, v.factor1, v.factor2);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
if (!this.preventAutoStart) {
|
|
|
result.start();
|
|
|
}
|