Sfoglia il codice sorgente

Merge pull request #3736 from BabylonJS/gpuParticles

Gpu particles - Do not merge - ongoing
David Catuhe 7 anni fa
parent
commit
cc9eda7e72

File diff suppressed because it is too large
+ 6622 - 6516
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 49 - 49
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 341 - 77
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 49 - 49
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 7755 - 7649
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


File diff suppressed because it is too large
+ 48 - 48
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 128 - 32
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -1171,6 +1171,18 @@ var __extends = (this && this.__extends) || (function () {
             return this;
         };
         /**
+         * Sets a Color4 on a uniform variable
+         * @param uniformName defines the name of the variable
+         * @param color4 defines the value to be set
+         * @returns this effect.
+         */
+        Effect.prototype.setDirectColor4 = function (uniformName, color4) {
+            if (this._cacheFloat4(uniformName, color4.r, color4.g, color4.b, color4.a)) {
+                this._engine.setDirectColor4(this.getUniform(uniformName), color4);
+            }
+            return this;
+        };
+        /**
          * Resets the cache of effects.
          */
         Effect.ResetCache = function () {
@@ -10855,6 +10867,36 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Engine, "TEXTUREFORMAT_R32F", {
+            /**
+             * R32F
+             */
+            get: function () {
+                return Engine._TEXTUREFORMAT_R32F;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Engine, "TEXTUREFORMAT_RG32F", {
+            /**
+             * RG32F
+             */
+            get: function () {
+                return Engine._TEXTUREFORMAT_RG32F;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Engine, "TEXTUREFORMAT_RGB32F", {
+            /**
+             * RGB32F
+             */
+            get: function () {
+                return Engine._TEXTUREFORMAT_RGB32F;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Engine, "TEXTUREFORMAT_LUMINANCE_ALPHA", {
             get: function () {
                 return Engine._TEXTUREFORMAT_LUMINANCE_ALPHA;
@@ -12536,6 +12578,16 @@ var BABYLON;
                 return;
             this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
         };
+        /**
+         * Sets a Color4 on a uniform variable
+         * @param uniform defines the uniform location
+         * @param color4 defines the value to be set
+         */
+        Engine.prototype.setDirectColor4 = function (uniform, color4) {
+            if (!uniform)
+                return;
+            this._gl.uniform4f(uniform, color4.r, color4.g, color4.b, color4.a);
+        };
         // States
         Engine.prototype.setState = function (culling, zOffset, force, reverseSide) {
             if (zOffset === void 0) { zOffset = 0; }
@@ -12919,11 +12971,18 @@ var BABYLON;
                     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;
         };
@@ -12933,8 +12992,8 @@ var BABYLON;
             if (!texture) {
                 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));
@@ -14693,11 +14752,22 @@ var BABYLON;
             return this._gl.UNSIGNED_BYTE;
         };
         ;
-        Engine.prototype._getRGBABufferInternalSizedFormat = function (type) {
+        /** @ignore */
+        Engine.prototype._getRGBABufferInternalSizedFormat = function (type, format) {
             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) {
@@ -14968,6 +15038,9 @@ var BABYLON;
         Engine._TEXTUREFORMAT_LUMINANCE_ALPHA = 2;
         Engine._TEXTUREFORMAT_RGB = 4;
         Engine._TEXTUREFORMAT_RGBA = 5;
+        Engine._TEXTUREFORMAT_R32F = 6;
+        Engine._TEXTUREFORMAT_RG32F = 7;
+        Engine._TEXTUREFORMAT_RGB32F = 8;
         Engine._TEXTURETYPE_UNSIGNED_INT = 0;
         Engine._TEXTURETYPE_FLOAT = 1;
         Engine._TEXTURETYPE_HALF_FLOAT = 2;
@@ -24403,10 +24476,10 @@ var BABYLON;
             var max = new BABYLON.Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
             for (var index = 0; index < this.meshes.length; index++) {
                 var mesh = this.meshes[index];
+                mesh.computeWorldMatrix(true);
                 if (!mesh.subMeshes || mesh.subMeshes.length === 0 || mesh.infiniteDistance) {
                     continue;
                 }
-                mesh.computeWorldMatrix(true);
                 var boundingInfo = mesh.getBoundingInfo();
                 var minBox = boundingInfo.boundingBox.minimumWorld;
                 var maxBox = boundingInfo.boundingBox.maximumWorld;
@@ -25291,12 +25364,19 @@ var BABYLON;
             if (!postponeInternalCreation) {
                 this.create();
             }
-            this._instanced = instanced;
-            this._instanceDivisor = instanced ? 1 : 0;
         }
-        Buffer.prototype.createVertexBuffer = function (kind, offset, size, stride) {
+        /**
+         * Create a new {BABYLON.VertexBuffer} based on the current buffer
+         * @param kind defines the vertex buffer kind (position, normal, etc.)
+         * @param offset defines offset in the buffer (0 by default)
+         * @param size defines the size in floats of attributes (position is 3 for instance)
+         * @param stride defines the stride size in floats in the buffer (the offset to apply to reach next value when data is interleaved)
+         * @param instanced defines if the vertex buffer contains indexed data
+         * @returns the new vertex buffer
+         */
+        Buffer.prototype.createVertexBuffer = function (kind, offset, size, stride, instanced) {
             // a lot of these parameters are ignored as they are overriden by the buffer
-            return new BABYLON.VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, this._instanced, offset, size);
+            return new BABYLON.VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, instanced, offset, size);
         };
         // Properties
         Buffer.prototype.isUpdatable = function () {
@@ -25311,25 +25391,20 @@ var BABYLON;
         Buffer.prototype.getStrideSize = function () {
             return this._strideSize;
         };
-        Buffer.prototype.getIsInstanced = function () {
-            return this._instanced;
-        };
-        Object.defineProperty(Buffer.prototype, "instanceDivisor", {
-            get: function () {
-                return this._instanceDivisor;
-            },
-            set: function (value) {
-                this._instanceDivisor = value;
-                if (value == 0) {
-                    this._instanced = false;
-                }
-                else {
-                    this._instanced = true;
-                }
-            },
-            enumerable: true,
-            configurable: true
-        });
+        // public getIsInstanced(): boolean {
+        //     return this._instanced;
+        // }
+        // 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;
+        //     }
+        // }
         // Methods
         Buffer.prototype.create = function (data) {
             if (data === void 0) { data = null; }
@@ -25389,9 +25464,6 @@ var BABYLON;
 (function (BABYLON) {
     var VertexBuffer = /** @class */ (function () {
         function VertexBuffer(engine, data, kind, updatable, postponeInternalCreation, stride, instanced, offset, size) {
-            if (!stride) {
-                stride = VertexBuffer.DeduceStride(kind);
-            }
             if (data instanceof BABYLON.Buffer) {
                 if (!stride) {
                     stride = data.getStrideSize();
@@ -25400,14 +25472,38 @@ var BABYLON;
                 this._ownsBuffer = false;
             }
             else {
+                if (!stride) {
+                    stride = VertexBuffer.DeduceStride(kind);
+                }
                 this._buffer = new BABYLON.Buffer(engine, 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;
             this._kind = kind;
         }
+        Object.defineProperty(VertexBuffer.prototype, "instanceDivisor", {
+            /**
+             * Gets or sets the instance divisor when in instanced mode
+             */
+            get: function () {
+                return this._instanceDivisor;
+            },
+            set: function (value) {
+                this._instanceDivisor = value;
+                if (value == 0) {
+                    this._instanced = false;
+                }
+                else {
+                    this._instanced = true;
+                }
+            },
+            enumerable: true,
+            configurable: true
+        });
         VertexBuffer.prototype._rebuild = function () {
             if (!this._buffer) {
                 return;
@@ -25461,13 +25557,13 @@ var BABYLON;
          * Boolean : is the WebGLBuffer of the VertexBuffer instanced now ?
          */
         VertexBuffer.prototype.getIsInstanced = function () {
-            return this._buffer.getIsInstanced();
+            return this._instanced;
         };
         /**
          * Returns the instancing divisor, zero for non-instanced (integer).
          */
         VertexBuffer.prototype.getInstanceDivisor = function () {
-            return this._buffer.instanceDivisor;
+            return this._instanceDivisor;
         };
         // Methods
         /**
@@ -50339,7 +50435,7 @@ var BABYLON;
              */
             this.emitter = null;
             /**
-             * The density of particles, the rate of particle flow
+             * The maximum number of particles to emit per frame
              */
             this.emitRate = 10;
             /**

+ 128 - 32
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js

@@ -1157,6 +1157,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Sets a Color4 on a uniform variable
+         * @param uniformName defines the name of the variable
+         * @param color4 defines the value to be set
+         * @returns this effect.
+         */
+        Effect.prototype.setDirectColor4 = function (uniformName, color4) {
+            if (this._cacheFloat4(uniformName, color4.r, color4.g, color4.b, color4.a)) {
+                this._engine.setDirectColor4(this.getUniform(uniformName), color4);
+            }
+            return this;
+        };
+        /**
          * Resets the cache of effects.
          */
         Effect.ResetCache = function () {
@@ -10841,6 +10853,36 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Engine, "TEXTUREFORMAT_R32F", {
+            /**
+             * R32F
+             */
+            get: function () {
+                return Engine._TEXTUREFORMAT_R32F;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Engine, "TEXTUREFORMAT_RG32F", {
+            /**
+             * RG32F
+             */
+            get: function () {
+                return Engine._TEXTUREFORMAT_RG32F;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Engine, "TEXTUREFORMAT_RGB32F", {
+            /**
+             * RGB32F
+             */
+            get: function () {
+                return Engine._TEXTUREFORMAT_RGB32F;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Engine, "TEXTUREFORMAT_LUMINANCE_ALPHA", {
             get: function () {
                 return Engine._TEXTUREFORMAT_LUMINANCE_ALPHA;
@@ -12522,6 +12564,16 @@ var BABYLON;
                 return;
             this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
         };
+        /**
+         * Sets a Color4 on a uniform variable
+         * @param uniform defines the uniform location
+         * @param color4 defines the value to be set
+         */
+        Engine.prototype.setDirectColor4 = function (uniform, color4) {
+            if (!uniform)
+                return;
+            this._gl.uniform4f(uniform, color4.r, color4.g, color4.b, color4.a);
+        };
         // States
         Engine.prototype.setState = function (culling, zOffset, force, reverseSide) {
             if (zOffset === void 0) { zOffset = 0; }
@@ -12905,11 +12957,18 @@ var BABYLON;
                     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;
         };
@@ -12919,8 +12978,8 @@ var BABYLON;
             if (!texture) {
                 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));
@@ -14679,11 +14738,22 @@ var BABYLON;
             return this._gl.UNSIGNED_BYTE;
         };
         ;
-        Engine.prototype._getRGBABufferInternalSizedFormat = function (type) {
+        /** @ignore */
+        Engine.prototype._getRGBABufferInternalSizedFormat = function (type, format) {
             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) {
@@ -14954,6 +15024,9 @@ var BABYLON;
         Engine._TEXTUREFORMAT_LUMINANCE_ALPHA = 2;
         Engine._TEXTUREFORMAT_RGB = 4;
         Engine._TEXTUREFORMAT_RGBA = 5;
+        Engine._TEXTUREFORMAT_R32F = 6;
+        Engine._TEXTUREFORMAT_RG32F = 7;
+        Engine._TEXTUREFORMAT_RGB32F = 8;
         Engine._TEXTURETYPE_UNSIGNED_INT = 0;
         Engine._TEXTURETYPE_FLOAT = 1;
         Engine._TEXTURETYPE_HALF_FLOAT = 2;
@@ -24389,10 +24462,10 @@ var BABYLON;
             var max = new BABYLON.Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
             for (var index = 0; index < this.meshes.length; index++) {
                 var mesh = this.meshes[index];
+                mesh.computeWorldMatrix(true);
                 if (!mesh.subMeshes || mesh.subMeshes.length === 0 || mesh.infiniteDistance) {
                     continue;
                 }
-                mesh.computeWorldMatrix(true);
                 var boundingInfo = mesh.getBoundingInfo();
                 var minBox = boundingInfo.boundingBox.minimumWorld;
                 var maxBox = boundingInfo.boundingBox.maximumWorld;
@@ -25277,12 +25350,19 @@ var BABYLON;
             if (!postponeInternalCreation) {
                 this.create();
             }
-            this._instanced = instanced;
-            this._instanceDivisor = instanced ? 1 : 0;
         }
-        Buffer.prototype.createVertexBuffer = function (kind, offset, size, stride) {
+        /**
+         * Create a new {BABYLON.VertexBuffer} based on the current buffer
+         * @param kind defines the vertex buffer kind (position, normal, etc.)
+         * @param offset defines offset in the buffer (0 by default)
+         * @param size defines the size in floats of attributes (position is 3 for instance)
+         * @param stride defines the stride size in floats in the buffer (the offset to apply to reach next value when data is interleaved)
+         * @param instanced defines if the vertex buffer contains indexed data
+         * @returns the new vertex buffer
+         */
+        Buffer.prototype.createVertexBuffer = function (kind, offset, size, stride, instanced) {
             // a lot of these parameters are ignored as they are overriden by the buffer
-            return new BABYLON.VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, this._instanced, offset, size);
+            return new BABYLON.VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, instanced, offset, size);
         };
         // Properties
         Buffer.prototype.isUpdatable = function () {
@@ -25297,25 +25377,20 @@ var BABYLON;
         Buffer.prototype.getStrideSize = function () {
             return this._strideSize;
         };
-        Buffer.prototype.getIsInstanced = function () {
-            return this._instanced;
-        };
-        Object.defineProperty(Buffer.prototype, "instanceDivisor", {
-            get: function () {
-                return this._instanceDivisor;
-            },
-            set: function (value) {
-                this._instanceDivisor = value;
-                if (value == 0) {
-                    this._instanced = false;
-                }
-                else {
-                    this._instanced = true;
-                }
-            },
-            enumerable: true,
-            configurable: true
-        });
+        // public getIsInstanced(): boolean {
+        //     return this._instanced;
+        // }
+        // 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;
+        //     }
+        // }
         // Methods
         Buffer.prototype.create = function (data) {
             if (data === void 0) { data = null; }
@@ -25375,9 +25450,6 @@ var BABYLON;
 (function (BABYLON) {
     var VertexBuffer = /** @class */ (function () {
         function VertexBuffer(engine, data, kind, updatable, postponeInternalCreation, stride, instanced, offset, size) {
-            if (!stride) {
-                stride = VertexBuffer.DeduceStride(kind);
-            }
             if (data instanceof BABYLON.Buffer) {
                 if (!stride) {
                     stride = data.getStrideSize();
@@ -25386,14 +25458,38 @@ var BABYLON;
                 this._ownsBuffer = false;
             }
             else {
+                if (!stride) {
+                    stride = VertexBuffer.DeduceStride(kind);
+                }
                 this._buffer = new BABYLON.Buffer(engine, 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;
             this._kind = kind;
         }
+        Object.defineProperty(VertexBuffer.prototype, "instanceDivisor", {
+            /**
+             * Gets or sets the instance divisor when in instanced mode
+             */
+            get: function () {
+                return this._instanceDivisor;
+            },
+            set: function (value) {
+                this._instanceDivisor = value;
+                if (value == 0) {
+                    this._instanced = false;
+                }
+                else {
+                    this._instanced = true;
+                }
+            },
+            enumerable: true,
+            configurable: true
+        });
         VertexBuffer.prototype._rebuild = function () {
             if (!this._buffer) {
                 return;
@@ -25447,13 +25543,13 @@ var BABYLON;
          * Boolean : is the WebGLBuffer of the VertexBuffer instanced now ?
          */
         VertexBuffer.prototype.getIsInstanced = function () {
-            return this._buffer.getIsInstanced();
+            return this._instanced;
         };
         /**
          * Returns the instancing divisor, zero for non-instanced (integer).
          */
         VertexBuffer.prototype.getInstanceDivisor = function () {
-            return this._buffer.instanceDivisor;
+            return this._instanceDivisor;
         };
         // Methods
         /**
@@ -50325,7 +50421,7 @@ var BABYLON;
              */
             this.emitter = null;
             /**
-             * The density of particles, the rate of particle flow
+             * The maximum number of particles to emit per frame
              */
             this.emitRate = 10;
             /**

File diff suppressed because it is too large
+ 341 - 77
dist/preview release/es6.js


File diff suppressed because it is too large
+ 55 - 55
dist/preview release/viewer/babylon.viewer.js


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

@@ -2,6 +2,7 @@
 
 ## Major updates
 
+-Support for [GPU particles](DOC HERE) ([deltakosh](https://github.com/deltakosh))
 - Improved building process: We now run a full visual validation test for each pull request. Furthermore, code comments and what's new updates are now mandatory ([sebavan](https://github.com/sebavan))
 - Introduced texture binding atlas. This optimization allows the engine to reuse texture bindings instead of rebinding textures when they are not on constant sampler indexes ([deltakosh](https://github.com/deltakosh))
 - New [AnimationGroup class](http://doc.babylonjs.com/how_to/group) to control simultaneously multiple animations with different targets ([deltakosh](https://github.com/deltakosh))

+ 56 - 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,27 @@
             return Engine._TEXTUREFORMAT_LUMINANCE;
         }
 
+        /**
+         * R32F
+         */
+        public static get TEXTUREFORMAT_R32F(): number {
+            return Engine._TEXTUREFORMAT_R32F;
+        }       
+        
+        /**
+         * RG32F
+         */
+        public static get TEXTUREFORMAT_RG32F(): number {
+            return Engine._TEXTUREFORMAT_RG32F;
+        }       
+        
+        /**
+         * RGB32F
+         */
+        public static get TEXTUREFORMAT_RGB32F(): number {
+            return Engine._TEXTUREFORMAT_RGB32F;
+        }               
+
         public static get TEXTUREFORMAT_LUMINANCE_ALPHA(): number {
             return Engine._TEXTUREFORMAT_LUMINANCE_ALPHA;
         }
@@ -2925,6 +2949,18 @@
             this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
         }
 
+        /**
+         * Sets a Color4 on a uniform variable
+         * @param uniform defines the uniform location
+         * @param color4 defines the value to be set
+         */
+        public setDirectColor4(uniform: Nullable<WebGLUniformLocation>, color4: Color4): void {
+            if (!uniform)
+                return;
+
+            this._gl.uniform4f(uniform, color4.r, color4.g, color4.b, color4.a);
+        }        
+
         // States
         public setState(culling: boolean, zOffset: number = 0, force?: boolean, reverseSide = false): void {
             // Culling
@@ -3353,11 +3389,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 +3411,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 +5579,23 @@
             return this._gl.UNSIGNED_BYTE;
         };
 
-        public _getRGBABufferInternalSizedFormat(type: number): number {
+        /** @ignore */
+        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) {

+ 13 - 0
src/Materials/babylon.effect.ts

@@ -1384,6 +1384,19 @@
             return this;
         }
 
+        /**
+         * Sets a Color4 on a uniform variable
+         * @param uniformName defines the name of the variable
+         * @param color4 defines the value to be set
+         * @returns this effect.
+         */
+        public setDirectColor4(uniformName: string, color4: Color4): Effect {
+            if (this._cacheFloat4(uniformName, color4.r, color4.g, color4.b, color4.a)) {
+                this._engine.setDirectColor4(this.getUniform(uniformName), color4);
+            }
+            return this;
+        }
+
         // Statics
         /**
          * Store of each shader (The can be looked up using effect.key)

+ 27 - 23
src/Mesh/babylon.buffer.ts

@@ -5,8 +5,6 @@
         private _data: Nullable<FloatArray>;
         private _updatable: boolean;
         private _strideSize: number;
-        private _instanced: boolean;
-        private _instanceDivisor: number;
 
         constructor(engine: any, data: FloatArray, updatable: boolean, stride: number, postponeInternalCreation?: boolean, instanced: boolean = false) {
             if (engine instanceof Mesh) { // old versions of BABYLON.VertexBuffer accepted 'mesh' instead of 'engine'
@@ -25,14 +23,20 @@
             if (!postponeInternalCreation) { // by default
                 this.create();
             }
-
-            this._instanced = instanced;
-            this._instanceDivisor = instanced ? 1 : 0;
         }
 
-        public createVertexBuffer(kind: string, offset: number, size: number, stride?: number): VertexBuffer {
+        /**
+         * Create a new {BABYLON.VertexBuffer} based on the current buffer
+         * @param kind defines the vertex buffer kind (position, normal, etc.)
+         * @param offset defines offset in the buffer (0 by default)
+         * @param size defines the size in floats of attributes (position is 3 for instance)
+         * @param stride defines the stride size in floats in the buffer (the offset to apply to reach next value when data is interleaved)
+         * @param instanced defines if the vertex buffer contains indexed data
+         * @returns the new vertex buffer
+         */
+        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, offset, size);
         }
 
         // Properties
@@ -52,22 +56,22 @@
             return this._strideSize;
         }
 
-        public getIsInstanced(): boolean {
-            return this._instanced;
-        }
-
-        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;
-            }
-        }
+        // public getIsInstanced(): boolean {
+        //     return this._instanced;
+        // }
+
+        // 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;
+        //     }
+        // }
 
         // Methods
         public create(data: Nullable<FloatArray> = null): void {

+ 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

+ 252 - 59
src/Particles/babylon.gpuParticleSystem.ts

@@ -30,18 +30,18 @@
         public layerMask: number = 0x0FFFFFFF; // TODO
 
         private _capacity: number;
+        private _activeCount: number;
+        private _currentActiveCount: number;
         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 _buffer0: Buffer;
+        private _buffer1: Buffer;
+        private _spriteBuffer: Buffer;
+        private _updateVAO = new Array<WebGLVertexArrayObject>();
+        private _renderVAO = new Array<WebGLVertexArrayObject>()
 
-        private _sourceVAO: WebGLVertexArrayObject;
-        private _targetVAO: WebGLVertexArrayObject;
+        private _targetIndex = 0;
         private _sourceBuffer: Buffer;
         private _targetBuffer: Buffer;
 
@@ -49,7 +49,13 @@
         private _engine: Engine;
 
         private _currentRenderId = -1;    
-        private _started = true;    
+        private _started = false;    
+
+        private _timeDelta = 0;
+
+        private _randomTexture: RawTexture;
+
+        private readonly _attributesStrideSize = 14;
 
         /**
         * An event triggered when the system is disposed.
@@ -57,6 +63,81 @@
         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;   
+        
+        /**
+         * Minimum life time of emitting particles.
+         */
+        public minLifeTime = 1;
+        /**
+         * Maximum life time of emitting particles.
+         */
+        public maxLifeTime = 1;    
+
+        /**
+         * Minimum Size of emitting particles.
+         */
+        public minSize = 1;
+        /**
+         * Maximum Size of emitting particles.
+         */
+        public maxSize = 1;        
+        
+        /**
+         * 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);
+        /**
+         * 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);  
+        
+        /**
+         * Color the particle will have at the end of its lifetime.
+         */
+        public colorDead = new Color4(0, 0, 0, 0);        
+        
+        /**
+         * The maximum number of particles to emit per frame until we reach the activeParticleCount value
+         */
+        public emitRate = 100; 
+        
+        /**
+         * You can use gravity if you want to give an orientation to your particles.
+         */
+        public gravity = Vector3.Zero();    
+
+        /**
+         * Gets the maximum number of particles supported by this system
+         */
+        public get capacity(): number {
+            return this._capacity;
+        }
+
+        /**
+         * Gets or set the number of active particles
+         */
+        public get activeParticleCount(): number {
+            return this._activeCount;
+        }
+
+        public set activeParticleCount(value: number) {
+            this._activeCount = Math.min(value, this._capacity);
+        }
+
+        /**
          * Gets Wether the system has been started.
          * @returns True if it has been started, otherwise false.
          */
@@ -90,86 +171,147 @@
             this.name = name;
             this._scene = scene || Engine.LastCreatedScene;
             this._capacity = capacity;
+            this._activeCount = capacity;
+            this._currentActiveCount = 0;
             this._engine = this._scene.getEngine();
 
             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", "size", "color", "direction"],
+                uniformsNames: ["currentCount", "timeDelta", "generalRandom", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity"],
                 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", "outSize", "outColor", "outDirection"]
             };
 
             this._updateEffect = new Effect("gpuUpdateParticles", updateEffectOptions, this._scene.getEngine());   
+
+            this._renderEffect = new Effect("gpuRenderParticles", ["position", "age", "life", "size", "color", "offset", "uv"], ["view", "projection", "colorDead"], ["textureSampler"], this._scene.getEngine());
+
+            // Random data
+            var maxTextureSize = this._engine.getCaps().maxTextureSize;
+            var d = [];
+            for (var i = 0; i < maxTextureSize; ++i) {
+                d.push(Math.random());
+                d.push(Math.random());
+                d.push(Math.random());
+            }
+            this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 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["size"] = source.createVertexBuffer("size", 6, 1);
+            updateVertexBuffers["color"] = source.createVertexBuffer("color", 7, 4);
+            updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 11, 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, this._attributesStrideSize, true);
+            renderVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1, this._attributesStrideSize, true);
+            renderVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1, this._attributesStrideSize, true);
+            renderVertexBuffers["size"] = source.createVertexBuffer("size", 6, 1, this._attributesStrideSize, true);           
+            renderVertexBuffers["color"] = source.createVertexBuffer("color", 7, 4, this._attributesStrideSize, 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
-              data.push(life + 1); // create the particle as a dead one to create a new one at start
-              data.push(life);
-          
-              // velocity
+
+              // Age and life
+              data.push(0.0); // create the particle as a dead one to create a new one at start
+              data.push(0.0);
+
+              // Seed
+              data.push(Math.random());
+
+              // Size
+              data.push(0.0);
+
+              // color
               data.push(0.0);
               data.push(0.0);
+              data.push(0.0);                     
+              data.push(0.0); 
+
+              // 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, 0]);
+
+            // Buffers
+            this._buffer0 = new Buffer(engine, data, false, this._attributesStrideSize);
+            this._buffer1 = new Buffer(engine, data, false, this._attributesStrideSize);
+            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.
@@ -181,42 +323,71 @@
 
             // Get everything ready to render
             this. _initialize();
-            
-            if (this._currentRenderId === this._scene.getRenderId()) {
-                return 0;
-            }
-
-            this._currentRenderId = this._scene.getRenderId();            
 
+            this._currentActiveCount = Math.min(this._activeCount, this._currentActiveCount + this.emitRate);
+            
             // Enable update effect
             this._engine.enableEffect(this._updateEffect);
-            this._engine.setState(false);            
+            this._engine.setState(false);    
+            
+            this._updateEffect.setFloat("currentCount", this._currentActiveCount);
+            this._updateEffect.setFloat("timeDelta", this._timeDelta);
+            this._updateEffect.setFloat("generalRandom", Math.random());
+            this._updateEffect.setTexture("randomSampler", this._randomTexture);
+            this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
+            this._updateEffect.setDirectColor4("color1", this.color1);
+            this._updateEffect.setDirectColor4("color2", this.color2);
+            this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
+            this._updateEffect.setVector3("gravity", this.gravity);
+
+            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());
             this._engine.setRasterizerState(false);
             this._engine.beginTransformFeedback();
-            this._engine.drawArraysType(Material.PointListDrawMode, 0, this._capacity);
+            this._engine.drawArraysType(Material.PointListDrawMode, 0, this._currentActiveCount);
             this._engine.endTransformFeedback();
             this._engine.setRasterizerState(true);
             this._engine.bindTransformFeedbackBuffer(null);
 
             // 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);
+            this._renderEffect.setDirectColor4("colorDead", this.colorDead);
+
+            // 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.TriangleFanDrawMode, 0, 4, this._currentActiveCount);   
+            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 +413,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);

+ 1 - 1
src/Particles/babylon.particleSystem.ts

@@ -103,7 +103,7 @@
         public emitter: Nullable<AbstractMesh | Vector3> = null;
 
         /**
-         * The density of particles, the rate of particle flow
+         * The maximum number of particles to emit per frame
          */
         public emitRate = 10;
 

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

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

+ 18 - 3
src/Shaders/gpuRenderParticles.vertex.fx

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

+ 43 - 9
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -1,34 +1,68 @@
 #version 300 es
 
+uniform float currentCount;
 uniform float timeDelta;
+uniform float generalRandom;
+uniform mat4 emitterWM;
+uniform vec2 lifeTime;
+uniform vec2 sizeRange;
+uniform vec4 color1;
+uniform vec4 color2;
+uniform vec3 gravity;
+uniform sampler2D randomSampler;
 
 // Particles state
 in vec3 position;
 in float age;
 in float life;
-in vec3 velocity;
+in float seed;
+in float size;
+in vec4 color;
+in vec3 direction;
 
 // Output
 out vec3 outPosition;
 out float outAge;
 out float outLife;
-out vec3 outVelocity;
+out float outSeed;
+out float outSize;
+out vec4 outColor;
+out vec3 outDirection;
+
+vec3 getRandomVec3(float offset) {
+  return texture(randomSampler, vec2(float(gl_VertexID) * offset / currentCount, 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;
+
+    // Let's get some random values
+    vec3 randoms = getRandomVec3(generalRandom);
 
     // Age and life
     outAge = 0.0;
-    outLife = life;
+    outLife = lifeTime.x + (lifeTime.y - lifeTime.x) * randoms.r;
+
+    // Seed
+    outSeed = seed;
+
+    // Size
+    outSize = sizeRange.x + (sizeRange.y - sizeRange.x) * randoms.g;
+
+    // Color
+    outColor = color1 + (color2 - color1) * randoms.b;
 
-    // Initial velocity
-    outVelocity = vec3(0, 1, 0);
-  } else {
-    outPosition = position + velocity * timeDelta;
+    // Direction
+    outDirection = 2.0 * (getRandomVec3(seed) - vec3(0.5, 0.5, 0.5));
+  } else {   
+    outPosition = position + (direction + gravity) * timeDelta;
     outAge = age + timeDelta;
     outLife = life;
-    outVelocity = velocity;
+    outSeed = seed;
+    outColor = color;
+    outSize = size;
+    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;