浏览代码

Merge pull request #2760 from IbraheemOsama/ParticleAnimation

Particle animation
David Catuhe 8 年之前
父节点
当前提交
11614cff4c

文件差异内容过多而无法显示
+ 8808 - 8787
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 22 - 22
dist/preview release/babylon.js


文件差异内容过多而无法显示
+ 147 - 13
dist/preview release/babylon.max.js


文件差异内容过多而无法显示
+ 8808 - 8787
dist/preview release/babylon.module.d.ts


文件差异内容过多而无法显示
+ 22 - 22
dist/preview release/babylon.worker.js


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

@@ -14,6 +14,7 @@
 - New VirtualKeyboard for Babylon.GUI. [Doc here](http://doc.babylonjs.com/overviews/gui#virtualkeyboard) ([deltakosh](https://github.com/deltakosh) / [adam](https://github.com/abow))
 - Added support for depth pre-pass rendering. [Doc here](http://doc.babylonjs.com/tutorials/transparency_and_how_meshes_are_rendered#depth-pre-pass-meshes) ([deltakosh](https://github.com/deltakosh))
 - Added support for Windows Motion Controllers ([Lewis Weaver](https://github.com/leweaver))
+- Added support for Particle animation in ParticleSystem ([Ibraheem Osama](https://github.com/IbraheemOsama))
 
 ## Updates
 - New `camera.storeState()` and `camera.restoreState()` functions to store / restore cameras position / rotation / fov. (Doc here)[http://doc.babylonjs.com/tutorials/cameras#state] ([deltakosh](https://github.com/deltakosh))

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

@@ -1,4 +1,5 @@
 module BABYLON {
+
     export class Particle {
         public position = Vector3.Zero();
         public direction = Vector3.Zero();
@@ -10,6 +11,58 @@
         public angle = 0;
         public angularSpeed = 0;
 
+        private _currentFrameCounter = 0;
+        public cellIndex: number = 0;
+
+        constructor(private particleSystem: ParticleSystem) {
+            if (!this.particleSystem.isAnimationSheetEnabled) {
+                return;
+            }
+
+            this.cellIndex = this.particleSystem.startSpriteCellID;
+
+            if (this.particleSystem.spriteCellChangeSpeed == 0) {
+                this.updateCellIndex = this.updateCellIndexWithSpeedCalculated;
+            }
+            else {
+                this.updateCellIndex = this.updateCellIndexWithCustomSpeed;
+            }
+        }
+
+        public updateCellIndex: (scaledUpdateSpeed: number) => void;
+
+        private updateCellIndexWithSpeedCalculated(scaledUpdateSpeed: number): void {
+            //   (ageOffset / scaledUpdateSpeed) / available cells
+            var numberOfScaledUpdatesPerCell = ((this.lifeTime - this.age) / scaledUpdateSpeed) / (this.particleSystem.endSpriteCellID + 1 - this.cellIndex);
+
+            this._currentFrameCounter += scaledUpdateSpeed;
+            if (this._currentFrameCounter >= numberOfScaledUpdatesPerCell * scaledUpdateSpeed) {
+                this._currentFrameCounter = 0;
+                this.cellIndex++;
+                if (this.cellIndex > this.particleSystem.endSpriteCellID) {
+                    this.cellIndex = this.particleSystem.endSpriteCellID;
+                }
+            }
+        }
+
+        private updateCellIndexWithCustomSpeed(): void {
+            if (this._currentFrameCounter >= this.particleSystem.spriteCellChangeSpeed) {
+                this.cellIndex++;
+                this._currentFrameCounter = 0;
+                if (this.cellIndex > this.particleSystem.endSpriteCellID) {
+                    if (this.particleSystem.spriteCellLoop) {
+                        this.cellIndex = this.particleSystem.startSpriteCellID;
+                    }
+                    else {
+                        this.cellIndex = this.particleSystem.endSpriteCellID;
+                    }
+                }
+            }
+            else {
+                this._currentFrameCounter++;
+            }
+        }
+
         public copyTo(other: Particle) {
             other.position.copyFrom(this.position);
             other.direction.copyFrom(this.direction);
@@ -20,6 +73,8 @@
             other.size = this.size;
             other.angle = this.angle;
             other.angularSpeed = this.angularSpeed;
+            other.particleSystem = this.particleSystem;
+            other.cellIndex = this.cellIndex;
         }
     }
 } 

+ 128 - 21
src/Particles/babylon.particleSystem.ts

@@ -12,13 +12,13 @@
     export interface IParticleSystem {
         id: string;
         name: string;
-        emitter: AbstractMesh | Vector3;        
+        emitter: AbstractMesh | Vector3;
         renderingGroupId: number;
         layerMask: number;
-        isStarted(): boolean;  
-        animate(): void;   
-        render();  
-        dispose(): void; 
+        isStarted(): boolean;
+        animate(): void;
+        render();
+        dispose(): void;
         clone(name: string, newEmitter: any): IParticleSystem;
         serialize(): any;
 
@@ -59,7 +59,10 @@
 
         public customShader: any = null;
         public preventAutoStart: boolean = false;
-        
+
+        private _epsilon: number;
+
+
         /**
         * An event triggered when the system is disposed.
         * @type {BABYLON.Observable}
@@ -119,10 +122,30 @@
         private _actualFrame = 0;
         private _scaledUpdateSpeed: number;
 
-        constructor(public name: string, capacity: number, scene: Scene, customEffect?: Effect) {
+        // sheet animation
+        public startSpriteCellID = 0;
+        public endSpriteCellID = 0;
+        public spriteCellLoop = true;
+        public spriteCellChangeSpeed = 0;
+
+        public spriteCellWidth = 0;
+        public spriteCellHeight = 0;
+        private _vertexBufferSize = 11;
+
+        public get isAnimationSheetEnabled(): Boolean {
+            return this._isAnimationSheetEnabled;
+        }
+        // end of sheet animation
+
+        constructor(public name: string, capacity: number, scene: Scene, customEffect?: Effect, private _isAnimationSheetEnabled: boolean = false, epsilon: number = 0.01) {
             this.id = name;
             this._capacity = capacity;
 
+            this._epsilon = epsilon;
+            if (_isAnimationSheetEnabled) {
+                this._vertexBufferSize = 12;
+            }
+
             this._scene = scene || Engine.LastCreatedScene;
 
             this._customEffect = customEffect;
@@ -132,13 +155,18 @@
             this._createIndexBuffer();
 
             // 11 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY) + 1 filler
-            this._vertexData = new Float32Array(capacity * 11 * 4);
-            this._vertexBuffer = new Buffer(scene.getEngine(), this._vertexData, true, 11);
+            this._vertexData = new Float32Array(capacity * this._vertexBufferSize * 4);
+            this._vertexBuffer = new Buffer(scene.getEngine(), this._vertexData, true, this._vertexBufferSize);
 
             var positions = this._vertexBuffer.createVertexBuffer(VertexBuffer.PositionKind, 0, 3);
             var colors = this._vertexBuffer.createVertexBuffer(VertexBuffer.ColorKind, 3, 4);
             var options = this._vertexBuffer.createVertexBuffer("options", 7, 4);
 
+            if (this._isAnimationSheetEnabled) {
+                var cellIndexBuffer = this._vertexBuffer.createVertexBuffer("cellIndex", 11, 1);
+                this._vertexBuffers["cellIndex"] = cellIndexBuffer;
+            }
+
             this._vertexBuffers[VertexBuffer.PositionKind] = positions;
             this._vertexBuffers[VertexBuffer.ColorKind] = colors;
             this._vertexBuffers["options"] = options;
@@ -184,6 +212,10 @@
 
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
                         particle.direction.addInPlace(this._scaledGravity);
+
+                        if (this._isAnimationSheetEnabled) {
+                            particle.updateCellIndex(this._scaledUpdateSpeed);
+                        }
                     }
                 }
             }
@@ -192,7 +224,7 @@
         private _createIndexBuffer() {
             var indices = [];
             var index = 0;
-            for (var count = 0; count < this._capacity ; count++) {
+            for (var count = 0; count < this._capacity; count++) {
                 indices.push(index);
                 indices.push(index + 1);
                 indices.push(index + 2);
@@ -236,8 +268,36 @@
             this._stopped = true;
         }
 
+        // animation sheet
+
         public _appendParticleVertex(index: number, particle: Particle, offsetX: number, offsetY: number): void {
-            var offset = index * 11;
+            var offset = index * this._vertexBufferSize;
+            this._vertexData[offset] = particle.position.x;
+            this._vertexData[offset + 1] = particle.position.y;
+            this._vertexData[offset + 2] = particle.position.z;
+            this._vertexData[offset + 3] = particle.color.r;
+            this._vertexData[offset + 4] = particle.color.g;
+            this._vertexData[offset + 5] = particle.color.b;
+            this._vertexData[offset + 6] = particle.color.a;
+            this._vertexData[offset + 7] = particle.angle;
+            this._vertexData[offset + 8] = particle.size;
+            this._vertexData[offset + 9] = offsetX;
+            this._vertexData[offset + 10] = offsetY;
+        }
+
+        public _appendParticleVertexWithAnimation(index: number, particle: Particle, offsetX: number, offsetY: number): void {
+
+            if (offsetX === 0)
+                offsetX = this._epsilon;
+            else if (offsetX === 1)
+                offsetX = 1 - this._epsilon;
+
+            if (offsetY === 0)
+                offsetY = this._epsilon;
+            else if (offsetY === 1)
+                offsetY = 1 - this._epsilon;
+
+            var offset = index * this._vertexBufferSize;
             this._vertexData[offset] = particle.position.x;
             this._vertexData[offset + 1] = particle.position.y;
             this._vertexData[offset + 2] = particle.position.z;
@@ -249,6 +309,7 @@
             this._vertexData[offset + 8] = particle.size;
             this._vertexData[offset + 9] = offsetX;
             this._vertexData[offset + 10] = offsetY;
+            this._vertexData[offset + 11] = particle.cellIndex;
         }
 
         private _update(newParticles: number): void {
@@ -267,6 +328,7 @@
                 var emitterPosition = (<Vector3>this.emitter);
                 worldMatrix = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
             }
+
             var particle: Particle;
             for (var index = 0; index < newParticles; index++) {
                 if (this.particles.length === this._capacity) {
@@ -276,9 +338,11 @@
                 if (this._stockParticles.length !== 0) {
                     particle = this._stockParticles.pop();
                     particle.age = 0;
+                    particle.cellIndex = this.startSpriteCellID;
                 } else {
-                    particle = new Particle();
+                    particle = new Particle(this);
                 }
+
                 this.particles.push(particle);
 
                 var emitPower = randomNumber(this.minEmitPower, this.maxEmitPower);
@@ -312,15 +376,31 @@
                 defines.push("#define CLIPPLANE");
             }
 
+            if (this._isAnimationSheetEnabled) {
+                defines.push("#define ANIMATESHEET");
+            }
+
             // Effect
             var join = defines.join("\n");
             if (this._cachedDefines !== join) {
                 this._cachedDefines = join;
 
+                var attributesNamesOrOptions: any;
+                var effectCreationOption: any;
+
+                if (this._isAnimationSheetEnabled) {
+                    attributesNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options", "cellIndex"];
+                    effectCreationOption = ["invView", "view", "projection", "particlesInfos", "vClipPlane", "textureMask"];
+                }
+                else {
+                    attributesNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options"];
+                    effectCreationOption = ["invView", "view", "projection", "vClipPlane", "textureMask"]
+                }
+
                 this._effect = this._scene.getEngine().createEffect(
                     "particles",
-                    [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options"],
-                    ["invView", "view", "projection", "vClipPlane", "textureMask"],
+                    attributesNamesOrOptions,
+                    effectCreationOption,
                     ["diffuseSampler"], join);
             }
 
@@ -345,7 +425,7 @@
 
             this._scaledUpdateSpeed = this.updateSpeed * this._scene.getAnimationRatio();
 
-            // determine the number of particles we need to create   
+            // determine the number of particles we need to create
             var newParticles;
 
             if (this.manualEmitCount > -1) {
@@ -388,20 +468,41 @@
                 }
             }
 
+            // Animation sheet
+            if (this._isAnimationSheetEnabled) {
+                this.appendParticleVertexes = this.appenedParticleVertexesWithSheet;
+            }
+            else {
+                this.appendParticleVertexes = this.appenedParticleVertexesNoSheet;
+            }
+
             // Update VBO
             var offset = 0;
             for (var index = 0; index < this.particles.length; index++) {
                 var particle = this.particles[index];
-
-                this._appendParticleVertex(offset++, particle, 0, 0);
-                this._appendParticleVertex(offset++, particle, 1, 0);
-                this._appendParticleVertex(offset++, particle, 1, 1);
-                this._appendParticleVertex(offset++, particle, 0, 1);
+                this.appendParticleVertexes(offset, particle);
+                offset += 4;
             }
 
             this._vertexBuffer.update(this._vertexData);
         }
 
+        public appendParticleVertexes: (offset: number, particle: Particle) => void = null;
+
+        private appenedParticleVertexesWithSheet(offset: number, particle: Particle) {
+            this._appendParticleVertexWithAnimation(offset++, particle, 0, 0);
+            this._appendParticleVertexWithAnimation(offset++, particle, 1, 0);
+            this._appendParticleVertexWithAnimation(offset++, particle, 1, 1);
+            this._appendParticleVertexWithAnimation(offset++, particle, 0, 1);
+        }
+
+        private appenedParticleVertexesNoSheet(offset: number, particle: Particle) {
+            this._appendParticleVertex(offset++, particle, 0, 0);
+            this._appendParticleVertex(offset++, particle, 1, 0);
+            this._appendParticleVertex(offset++, particle, 1, 1);
+            this._appendParticleVertex(offset++, particle, 0, 1);
+        }
+
         public rebuild(): void {
             this._createIndexBuffer();
 
@@ -425,6 +526,12 @@
             effect.setTexture("diffuseSampler", this.particleTexture);
             effect.setMatrix("view", viewMatrix);
             effect.setMatrix("projection", this._scene.getProjectionMatrix());
+
+            if (this._isAnimationSheetEnabled) {
+                var baseSize = this.particleTexture.getBaseSize();
+                effect.setFloat3("particlesInfos", this.spriteCellWidth / baseSize.width, this.spriteCellHeight / baseSize.height, baseSize.width / this.spriteCellWidth);
+            }
+
             effect.setFloat4("textureMask", this.textureMask.r, this.textureMask.g, this.textureMask.b, this.textureMask.a);
 
             if (this._scene.clipPlane) {
@@ -525,7 +632,7 @@
             } else {
                 var emitterPosition = (<Vector3>this.emitter);
                 serializationObject.emitter = emitterPosition.asArray();
-            }       
+            }
 
             serializationObject.capacity = this.getCapacity();
 

+ 13 - 1
src/Shaders/particles.vertex.fx

@@ -2,10 +2,12 @@
 attribute vec3 position;
 attribute vec4 color;
 attribute vec4 options;
+attribute float cellIndex;
 
 // Uniforms
 uniform mat4 view;
 uniform mat4 projection;
+uniform vec3 particlesInfos; // x (number of rows) y(number of columns) z(rowSize)
 
 // Output
 varying vec2 vUV;
@@ -23,7 +25,7 @@ void main(void) {
 	float size = options.y;
 	float angle = options.x;
 	vec2 offset = options.zw;
-
+	
 	cornerPos = vec3(offset.x - 0.5, offset.y  - 0.5, 0.) * size;
 
 	// Rotate
@@ -37,7 +39,17 @@ void main(void) {
 	gl_Position = projection * vec4(viewPos, 1.0);   
 	
 	vColor = color;
+
+	#ifdef ANIMATESHEET
+	float rowOffset = floor(cellIndex / particlesInfos.z);
+    float columnOffset = cellIndex - rowOffset * particlesInfos.z;
+
+	vec2 uvScale = particlesInfos.xy;
+	vec2 uvOffset = vec2(offset.x , 1.0 - offset.y);
+	vUV = (uvOffset + vec2(columnOffset, rowOffset)) * uvScale;
+	#else
 	vUV = offset;
+	#endif
 
 	// Clip plane
 #ifdef CLIPPLANE