瀏覽代碼

sub emitters update to support attached

Trevor Baron 7 年之前
父節點
當前提交
9f45e6ec0d
共有 3 個文件被更改,包括 217 次插入15 次删除
  1. 1 0
      dist/preview release/what's new.md
  2. 9 0
      src/Particles/babylon.particle.ts
  3. 207 15
      src/Particles/babylon.particleSystem.ts

+ 1 - 0
dist/preview release/what's new.md

@@ -39,6 +39,7 @@
   - Added support for noise textures. [Doc](http://doc.babylonjs.com/babylon101/particles#noise-texture)
   - Added support for emit rate gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#emit-rate-over-time)
   - Start size gradient support for particles. [Doc](http://doc.babylonjs.com/babylon101/particles#start-size-over-time) ([TrevorDev](https://github.com/TrevorDev))
+  - Attached sub emitters. [Doc](http://doc.babylonjs.com/how_to/sub_emitters) ([TrevorDev](https://github.com/TrevorDev))
   - Cylinder particle emitter and constructor in baseParticle [Doc](https://doc.babylonjs.com/babylon101/particles#cylinder-emitter) ([TrevorDev](https://github.com/TrevorDev))
 - Added SceneComponent to help decoupling Scene from its components. ([sebavan](http://www.github.com/sebavan))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))

+ 9 - 0
src/Particles/babylon.particle.ts

@@ -5,6 +5,11 @@
      * This is mainly define by its coordinates, direction, velocity and age.
      */
     export class Particle {
+        private static _Count = 0;
+        /**
+         * Unique ID of the particle
+         */
+        public id:number;
         /**
          * The world position of the particle in the scene.
          */
@@ -64,6 +69,9 @@
         public _initialDirection: Nullable<Vector3>;
 
         /** @hidden */
+        public _attachedSubEmitters: Nullable<Array<SubEmitter>> = null;
+
+        /** @hidden */
         public _initialStartSpriteCellID: number;
         public _initialEndSpriteCellID: number;
 
@@ -118,6 +126,7 @@
              * particleSystem the particle system the particle belongs to.
              */
             public particleSystem: ParticleSystem) {
+            this.id = Particle._Count++;
             if (!this.particleSystem.isAnimationSheetEnabled) {
                 return;
             }

+ 207 - 15
src/Particles/babylon.particleSystem.ts

@@ -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();
             }