Browse Source

Associated with #2033

David Catuhe 7 years ago
parent
commit
6d899221f9

+ 34 - 2
src/Engine/babylon.engine.ts

@@ -346,6 +346,9 @@
         private static _TEXTUREFORMAT_LUMINANCE_ALPHA = 2;
         private static _TEXTUREFORMAT_RGB = 4;
         private static _TEXTUREFORMAT_RGBA = 5;
+        private static _TEXTUREFORMAT_R32F = 6;
+        private static _TEXTUREFORMAT_RG32F = 7;
+        private static _TEXTUREFORMAT_RGB32F = 8;
 
         private static _TEXTURETYPE_UNSIGNED_INT = 0;
         private static _TEXTURETYPE_FLOAT = 1;
@@ -498,6 +501,18 @@
             return Engine._TEXTUREFORMAT_LUMINANCE;
         }
 
+        public static get TEXTUREFORMAT_R32F(): number {
+            return Engine._TEXTUREFORMAT_R32F;
+        }       
+        
+        public static get TEXTUREFORMAT_RG32F(): number {
+            return Engine._TEXTUREFORMAT_RG32F;
+        }       
+        
+        public static get TEXTUREFORMAT_RGB32F(): number {
+            return Engine._TEXTUREFORMAT_RGB32F;
+        }               
+
         public static get TEXTUREFORMAT_LUMINANCE_ALPHA(): number {
             return Engine._TEXTUREFORMAT_LUMINANCE_ALPHA;
         }
@@ -3353,11 +3368,18 @@
                     internalFormat = this._gl.LUMINANCE_ALPHA;
                     break;
                 case Engine.TEXTUREFORMAT_RGB:
+                case Engine.TEXTUREFORMAT_RGB32F:
                     internalFormat = this._gl.RGB;
                     break;
                 case Engine.TEXTUREFORMAT_RGBA:
                     internalFormat = this._gl.RGBA;
                     break;
+                case Engine.TEXTUREFORMAT_R32F:
+                    internalFormat = this._gl.RED;
+                    break;       
+                case Engine.TEXTUREFORMAT_RG32F:
+                    internalFormat = this._gl.RG;
+                    break;                                    
             }
 
             return internalFormat;
@@ -3368,8 +3390,8 @@
                 return;
             }
 
+            var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type, format);
             var internalFormat = this._getInternalFormat(format);
-            var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
             var textureType = this._getWebGLTextureType(type);
             this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
@@ -5536,12 +5558,22 @@
             return this._gl.UNSIGNED_BYTE;
         };
 
-        public _getRGBABufferInternalSizedFormat(type: number): number {
+        public _getRGBABufferInternalSizedFormat(type: number, format?: number): number {
             if (this._webGLVersion === 1) {
                 return this._gl.RGBA;
             }
 
             if (type === Engine.TEXTURETYPE_FLOAT) {
+                if (format) {
+                    switch(format) {
+                        case Engine.TEXTUREFORMAT_R32F:
+                            return this._gl.R32F;
+                        case Engine.TEXTUREFORMAT_RG32F:
+                            return this._gl.RG32F;
+                            case Engine.TEXTUREFORMAT_RGB32F:
+                            return this._gl.RGB32F;                            
+                    }                    
+                }
                 return this._gl.RGBA32F;
             }
             else if (type === Engine.TEXTURETYPE_HALF_FLOAT) {

+ 2 - 2
src/Mesh/babylon.buffer.ts

@@ -30,9 +30,9 @@
             this._instanceDivisor = instanced ? 1 : 0;
         }
 
-        public createVertexBuffer(kind: string, offset: number, size: number, stride?: number): VertexBuffer {
+        public createVertexBuffer(kind: string, offset: number, size: number, stride?: number, instanced?: boolean): VertexBuffer {
             // a lot of these parameters are ignored as they are overriden by the buffer
-            return new VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, this._instanced, offset, size);
+            return new VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, instanced !== undefined ? instanced : this._instanced, offset, size);
         }
 
         // Properties

+ 24 - 6
src/Mesh/babylon.vertexBuffer.ts

@@ -6,12 +6,26 @@
         private _size: number;
         private _stride: number;
         private _ownsBuffer: boolean;
+        private _instanced: boolean;        
+        private _instanceDivisor: number;
 
-        constructor(engine: any, data: FloatArray | Buffer, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number, instanced?: boolean, offset?: number, size?: number) {
-            if (!stride) {
-                stride = VertexBuffer.DeduceStride(kind);
+        /**
+         * Gets or sets the instance divisor when in instanced mode
+         */
+        public get instanceDivisor(): number {
+            return this._instanceDivisor;
+        }
+
+        public set instanceDivisor(value: number) {
+            this._instanceDivisor = value;
+            if (value == 0) {
+                this._instanced = false;
+            } else {
+                this._instanced = true;
             }
+        }        
 
+        constructor(engine: any, data: FloatArray | Buffer, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number, instanced?: boolean, offset?: number, size?: number) {
             if (data instanceof Buffer) {
                 if (!stride) {
                     stride = data.getStrideSize();
@@ -19,12 +33,16 @@
                 this._buffer = data;
                 this._ownsBuffer = false;
             } else {
+                if (!stride) {
+                    stride = VertexBuffer.DeduceStride(kind);
+                }
                 this._buffer = new Buffer(engine, <FloatArray>data, updatable, stride, postponeInternalCreation, instanced);
                 this._ownsBuffer = true;
             }
 
             this._stride = stride;
-
+            this._instanced = instanced !== undefined ? instanced : false;
+            this._instanceDivisor = instanced ? 1 : 0;
 
             this._offset = offset ? offset : 0;
             this._size = size ? size : stride;
@@ -94,14 +112,14 @@
          * Boolean : is the WebGLBuffer of the VertexBuffer instanced now ?
          */
         public getIsInstanced(): boolean {
-            return this._buffer.getIsInstanced();
+            return this._instanced;
         }
 
         /**
          * Returns the instancing divisor, zero for non-instanced (integer).  
          */
         public getInstanceDivisor(): number {
-            return this._buffer.instanceDivisor;
+            return this._instanceDivisor;
         }
 
         // Methods

+ 159 - 58
src/Particles/babylon.gpuParticleSystem.ts

@@ -33,15 +33,13 @@
         private _renderEffect: Effect;
         private _updateEffect: Effect;
 
-        private _updateBuffer: Buffer;
-        private _updateVAO: WebGLVertexArrayObject;
-        private _updateVertexBuffers: {[key: string]: VertexBuffer} = {};
-        private _renderBuffer: Buffer;
-        private _renderVAO: WebGLVertexArrayObject;
-        private _renderVertexBuffers: {[key: string]: VertexBuffer} = {};
-
-        private _sourceVAO: WebGLVertexArrayObject;
-        private _targetVAO: WebGLVertexArrayObject;
+        private _buffer0: Buffer;
+        private _buffer1: Buffer;
+        private _spriteBuffer: Buffer;
+        private _updateVAO = new Array<WebGLVertexArrayObject>();
+        private _renderVAO = new Array<WebGLVertexArrayObject>()
+
+        private _targetIndex = 0;
         private _sourceBuffer: Buffer;
         private _targetBuffer: Buffer;
 
@@ -49,7 +47,11 @@
         private _engine: Engine;
 
         private _currentRenderId = -1;    
-        private _started = true;    
+        private _started = false;    
+
+        private _timeDelta = 0;
+
+        private _randomTexture: RawTexture;
 
         /**
         * An event triggered when the system is disposed.
@@ -57,6 +59,21 @@
         public onDisposeObservable = new Observable<GPUParticleSystem>();
 
         /**
+         * The overall motion speed (0.01 is default update speed, faster updates = faster animation)
+         */
+        public updateSpeed = 0.01;        
+
+        /**
+         * The texture used to render each particle. (this can be a spritesheet)
+         */
+        public particleTexture: Nullable<Texture>;   
+        
+        /**
+         * Blend mode use to render the particle, it can be either ParticleSystem.BLENDMODE_ONEONE or ParticleSystem.BLENDMODE_STANDARD.
+         */
+        public blendMode = ParticleSystem.BLENDMODE_ONEONE;        
+
+        /**
          * Gets Wether the system has been started.
          * @returns True if it has been started, otherwise false.
          */
@@ -94,82 +111,124 @@
 
             this._scene.particleSystems.push(this);
 
-            this._renderEffect = new Effect("gpuRenderParticles", ["position", "age", "life", "velocity"], [], [], this._scene.getEngine());
-
             let updateEffectOptions: EffectCreationOptions = {
-                attributes: ["position", "age", "life", "velocity"],
-                uniformsNames: [],
+                attributes: ["position", "age", "life", "seed", "direction"],
+                uniformsNames: ["timeDelta", "generalRandom", "emitterWM"],
                 uniformBuffersNames: [],
-                samplers:[],
+                samplers:["randomSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
                 onError: null,
                 indexParameters: null,
                 maxSimultaneousLights: 0,                                                      
-                transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outVelocity"]
+                transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outSeed", "outDirection"]
             };
 
             this._updateEffect = new Effect("gpuUpdateParticles", updateEffectOptions, this._scene.getEngine());   
+
+            this._renderEffect = new Effect("gpuRenderParticles", ["position", "offset", "uv"], ["view", "projection"], ["textureSampler"], this._scene.getEngine());
+
+            // Random data
+            var d = [];
+            for (var i = 0; i < 4096; ++i) {
+                d.push(Math.random());
+                d.push(Math.random());
+                d.push(Math.random());
+            }
+            this._randomTexture = new RawTexture(new Float32Array(d), 4096, 1, Engine.TEXTUREFORMAT_RGB32F, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT)
+            this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
+            this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
         }
 
         /**
          * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
          */
         public animate(): void {
-            // Do nothing
+            if (this._currentRenderId === this._scene.getRenderId()) {
+                return;
+            }
+
+            this._currentRenderId = this._scene.getRenderId();
+            this._timeDelta = this.updateSpeed * this._scene.getAnimationRatio();               
+        }
+
+        private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {            
+            let updateVertexBuffers: {[key: string]: VertexBuffer} = {};
+            updateVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3);
+            updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
+            updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
+            updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 1);
+            updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 6, 3);
+           
+            let vao = this._engine.recordVertexArrayObject(updateVertexBuffers, null, this._updateEffect);
+            this._engine.bindArrayBuffer(null);
+
+            return vao;
         }
+
+        private _createRenderVAO(source: Buffer, spriteSource: Buffer): WebGLVertexArrayObject {            
+            let renderVertexBuffers: {[key: string]: VertexBuffer} = {};
+            renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, 9, true);
+            renderVertexBuffers["offset"] = spriteSource.createVertexBuffer("offset", 0, 2);
+            renderVertexBuffers["uv"] = spriteSource.createVertexBuffer("uv", 2, 2);
+           
+            let vao = this._engine.recordVertexArrayObject(renderVertexBuffers, null, this._renderEffect);
+            this._engine.bindArrayBuffer(null);
+
+            return vao;
+        }        
         
         private _initialize(): void {
-            if (this._renderVAO) {
+            if (this._buffer0) {
                 return;
             }
 
+            let engine = this._scene.getEngine();
             var data = new Array<float>();
             for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
               // position
               data.push(0.0);
               data.push(0.0);
               data.push(0.0);
-          
-              var life = 1 + Math.random() * 10; // TODO: var
+
+              // Age and life
+              var life = 1 + Math.random();// * 10; 
               data.push(life + 1); // create the particle as a dead one to create a new one at start
               data.push(life);
-          
-              // velocity
-              data.push(0.0);
+
+              // Seed
+              data.push(Math.random());
+
+              // direction
               data.push(0.0);
               data.push(0.0);
+              data.push(0.0);              
             }
 
+            // Sprite data
+            var spriteData = new Float32Array([1, 1,  1, 1,  -1, 1,  0, 1,
+                -1, -1,  0, 0,   1, 1,  1, 1,
+                -1, -1,  0, 0,   1, -1, 1, 0]);
+
+            // Buffers
+            this._buffer0 = new Buffer(engine, data, false, 9);
+            this._buffer1 = new Buffer(engine, data, false, 9);
+            this._spriteBuffer = new Buffer(engine, spriteData, false, 4);                                      
+
             // Update VAO
-            this._updateBuffer = new Buffer(this._scene.getEngine(), data, false, 0);
-            this._updateVertexBuffers["position"] = this._updateBuffer.createVertexBuffer("position", 0, 3, 3);
-            this._updateVertexBuffers["age"] = this._updateBuffer.createVertexBuffer("age", 3, 1, 1);
-            this._updateVertexBuffers["life"] = this._updateBuffer.createVertexBuffer("life", 4, 1, 1);
-            this._updateVertexBuffers["velocity"] = this._updateBuffer.createVertexBuffer("velocity", 5, 3, 3);
-           
-            this._updateVAO = this._engine.recordVertexArrayObject(this._updateVertexBuffers, null, this._updateEffect);
-            this._engine.bindArrayBuffer(null);
+            this._updateVAO.push(this._createUpdateVAO(this._buffer0));
+            this._updateVAO.push(this._createUpdateVAO(this._buffer1));
 
             // Render VAO
-            this._renderBuffer = new Buffer(this._scene.getEngine(), data, false, 0);
-            this._renderVertexBuffers["position"] = this._renderBuffer.createVertexBuffer("position", 0, 3, 3);
-            this._renderVertexBuffers["age"] = this._renderBuffer.createVertexBuffer("age", 3, 1, 1);
-            this._renderVertexBuffers["life"] = this._renderBuffer.createVertexBuffer("life", 4, 1, 1);
-            this._renderVertexBuffers["velocity"] = this._renderBuffer.createVertexBuffer("velocity", 5, 3, 3);
-           
-            this._renderVAO = this._engine.recordVertexArrayObject(this._renderVertexBuffers, null, this._renderEffect);  
-            this._engine.bindArrayBuffer(null);          
+            this._renderVAO.push(this._createRenderVAO(this._buffer1, this._spriteBuffer));
+            this._renderVAO.push(this._createRenderVAO(this._buffer0, this._spriteBuffer));
 
             // Links
-            this._sourceVAO = this._updateVAO;
-            this._targetVAO = this._renderVAO;
+            this._sourceBuffer = this._buffer0;
+            this._targetBuffer = this._buffer1;
 
-            this._sourceBuffer = this._updateBuffer;
-            this._targetBuffer = this._renderBuffer;
         }
-
         /**
          * Renders the particle system in its current state.
          * @returns the current number of particles.
@@ -182,18 +241,26 @@
             // Get everything ready to render
             this. _initialize();
             
-            if (this._currentRenderId === this._scene.getRenderId()) {
-                return 0;
-            }
-
-            this._currentRenderId = this._scene.getRenderId();            
-
             // Enable update effect
             this._engine.enableEffect(this._updateEffect);
-            this._engine.setState(false);            
+            this._engine.setState(false);    
+            
+            this._updateEffect.setFloat("timeDelta", this._timeDelta);
+            this._updateEffect.setFloat("generalRandom", Math.random());
+            this._updateEffect.setTexture("randomSampler", this._randomTexture);
+
+            let emitterWM: Matrix;
+            if ((<AbstractMesh>this.emitter).position) {
+                var emitterMesh = (<AbstractMesh>this.emitter);
+                emitterWM = emitterMesh.getWorldMatrix();
+            } else {
+                var emitterPosition = (<Vector3>this.emitter);
+                emitterWM = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
+            }            
+            this._updateEffect.setMatrix("emitterWM", emitterWM);
 
             // Bind source VAO
-            this._engine.bindVertexArrayObject(this._sourceVAO, null);
+            this._engine.bindVertexArrayObject(this._updateVAO[this._targetIndex], null);
 
             // Update
             this._engine.bindTransformFeedbackBuffer(this._targetBuffer.getBuffer());
@@ -206,17 +273,29 @@
 
             // Enable render effect
             this._engine.enableEffect(this._renderEffect);
+            this._renderEffect.setMatrix("view", this._scene.getViewMatrix());
+            this._renderEffect.setMatrix("projection", this._scene.getProjectionMatrix());
+            this._renderEffect.setTexture("textureSampler", this.particleTexture);
+
+            // Draw order
+            if (this.blendMode === ParticleSystem.BLENDMODE_ONEONE) {
+                this._engine.setAlphaMode(Engine.ALPHA_ONEONE);
+            } else {
+                this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
+            }            
 
             // Bind source VAO
-            this._engine.bindVertexArrayObject(this._targetVAO, null);
+            this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
 
             // Render
-            this._engine.drawArraysType(Material.PointListDrawMode, 0, this._capacity);            
+            this._engine.drawArraysType(Material.TriangleFillMode, 0, 6, this._capacity);   
+            this._engine.setAlphaMode(Engine.ALPHA_DISABLE);         
 
             // Switch VAOs
-            let tmpVAO = this._sourceVAO;
-            this._sourceVAO = this._targetVAO;
-            this._targetVAO = tmpVAO;
+            this._targetIndex++;
+            if (this._targetIndex === 2) {
+                this._targetIndex = 0;
+            }
 
             // Switch buffers
             let tmpBuffer = this._sourceBuffer;
@@ -242,7 +321,29 @@
                 this._scene.particleSystems.splice(index, 1);
             }
 
-            //TODO: this._dataBuffer.dispose();
+            if (this._buffer0) {
+                this._buffer0.dispose();
+                (<any>this._buffer0) = null;
+            }
+            if (this._buffer1) {
+                this._buffer1.dispose();
+                (<any>this._buffer1) = null;
+            }
+            
+            for (var index = 0; index < this._updateVAO.length; index++) {
+                this._engine.releaseVertexArrayObject(this._updateVAO[index]);
+            }
+            this._updateVAO = [];
+
+            for (var index = 0; index < this._renderVAO.length; index++) {
+                this._engine.releaseVertexArrayObject(this._renderVAO[index]);
+            }
+            this._renderVAO = [];            
+
+            if (this._randomTexture) {
+                this._randomTexture.dispose();
+                (<any>this._randomTexture) = null;
+            }
 
             // Callback
             this.onDisposeObservable.notifyObservers(this);

+ 8 - 1
src/Shaders/gpuRenderParticles.fragment.fx

@@ -1,4 +1,11 @@
+#version 300 es
+
+uniform sampler2D textureSampler;
+
+in vec2 vUV;
+
+out vec4 outFragColor;
 
 void main() {
-  gl_FragColor = vec4(1.0);
+  outFragColor = texture(textureSampler, vUV);
 }

+ 11 - 5
src/Shaders/gpuRenderParticles.vertex.fx

@@ -1,12 +1,18 @@
 #version 300 es
 
+uniform mat4 view;
+uniform mat4 projection;
+
 // Particles state
 in vec3 position;
-in float age;
-in float life;
-in vec3 velocity;
+in vec2 offset;
+in vec2 uv;
+
+out vec2 vUV;
 
 void main() {
-  gl_PointSize = 1.0;
-  gl_Position = vec4(position, 1.0);
+  vUV = uv;
+  vec4 viewPosition = view * vec4(position, 1.0);
+
+  gl_Position = projection * (viewPosition + vec4(offset * 0.1, 0, 1.0));
 }

+ 24 - 8
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -1,34 +1,50 @@
 #version 300 es
 
 uniform float timeDelta;
+uniform float generalRandom;
+uniform mat4 emitterWM;
+uniform sampler2D randomSampler;
 
 // Particles state
 in vec3 position;
 in float age;
 in float life;
-in vec3 velocity;
+in float seed;
+in vec3 direction;
 
 // Output
 out vec3 outPosition;
 out float outAge;
 out float outLife;
-out vec3 outVelocity;
+out float outSeed;
+out vec3 outDirection;
+
+float getRandomFloat() {
+  return texture(randomSampler, vec2(float(gl_VertexID) * generalRandom, 0)).r;
+}
+ vec3 getRandomVec3() {
+   return texture(randomSampler, vec2(float(gl_VertexID) * seed, 0)).rgb;
+ }
 
 void main() {
   if (age >= life) {
     // Create the particle at origin
-    outPosition = vec3(0, 0, 0);
+    outPosition = (emitterWM * vec4(0., 0., 0., 1.)).xyz;
 
     // Age and life
     outAge = 0.0;
-    outLife = life;
+    outLife = getRandomFloat() * 15.0;
+
+    // Seed
+    outSeed = seed;
 
-    // Initial velocity
-    outVelocity = vec3(0, 1, 0);
+    // Direction
+    outDirection = 2.0 * (getRandomVec3() -vec3(0.5, 0.5, 0.5));
   } else {
-    outPosition = position + velocity * timeDelta;
+    outPosition = position + direction * timeDelta;
     outAge = age + timeDelta;
     outLife = life;
-    outVelocity = velocity;
+    outSeed = seed;
+    outDirection = direction;
   }
 }

+ 5 - 0
src/babylon.mixins.ts

@@ -53,6 +53,11 @@ interface WebGLRenderingContext {
     HALF_FLOAT_OES: number;
     RGBA16F: number;
     RGBA32F: number;
+    R32F: number;
+    RG32F: number;
+    RGB32F: number;
+    RED: number;
+    RG: number;
 
     DEPTH24_STENCIL8: number;