Parcourir la source

Merge https://github.com/BabylonJS/Babylon.js into developmentFrictionFixes

Trevor Baron il y a 7 ans
Parent
commit
fae63dae2e
34 fichiers modifiés avec 22470 ajouts et 22125 suppressions
  1. 4094 4067
      Playground/babylon.d.txt
  2. 0 1
      Viewer/src/configuration/configuration.ts
  3. 17766 17739
      dist/preview release/babylon.d.ts
  4. 30 30
      dist/preview release/babylon.js
  5. 80 33
      dist/preview release/babylon.max.js
  6. 80 33
      dist/preview release/babylon.no-module.max.js
  7. 30 30
      dist/preview release/babylon.worker.js
  8. 80 33
      dist/preview release/es6.js
  9. 1 1
      dist/preview release/gltf2Interface/package.json
  10. 1 1
      dist/preview release/gui/package.json
  11. 1 1
      dist/preview release/inspector/package.json
  12. 2 2
      dist/preview release/loaders/package.json
  13. 1 1
      dist/preview release/materialsLibrary/package.json
  14. 1 1
      dist/preview release/postProcessesLibrary/package.json
  15. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  16. 2 2
      dist/preview release/serializers/package.json
  17. 2 14
      dist/preview release/typedocValidationBaseline.json
  18. 3 4
      dist/preview release/viewer/babylon.viewer.d.ts
  19. 33 33
      dist/preview release/viewer/babylon.viewer.js
  20. 83 36
      dist/preview release/viewer/babylon.viewer.max.js
  21. 3 4
      dist/preview release/viewer/babylon.viewer.module.d.ts
  22. 1 1
      dist/preview release/viewer/package.json
  23. 9 7
      dist/preview release/what's new.md
  24. 1 1
      package.json
  25. 32 13
      src/Animations/babylon.animatable.ts
  26. 31 14
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  27. 1 1
      src/Engine/babylon.engine.ts
  28. 44 4
      src/Mesh/Compression/babylon.dracoCompression.ts
  29. 4 4
      src/Mesh/babylon.mesh.ts
  30. 1 1
      src/Particles/EmitterTypes/babylon.coneParticleEmitter.ts
  31. 28 7
      src/Particles/babylon.gpuParticleSystem.ts
  32. 11 1
      src/Shaders/gpuRenderParticles.vertex.fx
  33. 11 2
      src/Shaders/gpuUpdateParticles.vertex.fx
  34. 2 2
      src/Shaders/particles.vertex.fx

Fichier diff supprimé car celui-ci est trop grand
+ 4094 - 4067
Playground/babylon.d.txt


+ 0 - 1
Viewer/src/configuration/configuration.ts

@@ -157,7 +157,6 @@ export interface IDefaultRenderingPipelineConfiguration {
     bloomKernel?: number;
     hardwareScaleLevel?: number;
     bloomWeight?: number;
-    bllomThreshold?: number;
     hdr?: boolean;
     samples?: number;
     glowLayerEnabled?: boolean;

Fichier diff supprimé car celui-ci est trop grand
+ 17766 - 17739
dist/preview release/babylon.d.ts


Fichier diff supprimé car celui-ci est trop grand
+ 30 - 30
dist/preview release/babylon.js


Fichier diff supprimé car celui-ci est trop grand
+ 80 - 33
dist/preview release/babylon.max.js


Fichier diff supprimé car celui-ci est trop grand
+ 80 - 33
dist/preview release/babylon.no-module.max.js


Fichier diff supprimé car celui-ci est trop grand
+ 30 - 30
dist/preview release/babylon.worker.js


Fichier diff supprimé car celui-ci est trop grand
+ 80 - 33
dist/preview release/es6.js


+ 1 - 1
dist/preview release/gltf2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 2 - 2
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.2.0"
+        "babylonjs-gltf2interface": "3.3.0-alpha.1"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

+ 1 - 1
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 2 - 2
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.2.0"
+        "babylonjs-gltf2interface": "3.3.0-alpha.1"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

+ 2 - 14
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,7 @@
 {
-  "errors": 4309,
+  "errors": 4307,
   "babylon.typedoc.json": {
-    "errors": 4309,
+    "errors": 4307,
     "Animatable": {
       "Class": {
         "Comments": {
@@ -90,18 +90,6 @@
         }
       },
       "Method": {
-        "_animate": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "delay": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "appendAnimations": {
           "Comments": {
             "MissingText": true

+ 3 - 4
dist/preview release/viewer/babylon.viewer.d.ts

@@ -1078,7 +1078,6 @@ declare module BabylonViewer {
             bloomKernel?: number;
             hardwareScaleLevel?: number;
             bloomWeight?: number;
-            bllomThreshold?: number;
             hdr?: boolean;
             samples?: number;
             glowLayerEnabled?: boolean;
@@ -1200,9 +1199,9 @@ declare module BabylonViewer {
                     a: number;
             };
             mainColor?: {
-                    r: number;
-                    g: number;
-                    b: number;
+                    r?: number;
+                    g?: number;
+                    b?: number;
             };
             imageProcessingConfiguration?: IImageProcessingConfiguration;
             environmentTexture?: string;

Fichier diff supprimé car celui-ci est trop grand
+ 33 - 33
dist/preview release/viewer/babylon.viewer.js


Fichier diff supprimé car celui-ci est trop grand
+ 83 - 36
dist/preview release/viewer/babylon.viewer.max.js


+ 3 - 4
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -1078,7 +1078,6 @@ declare module 'babylonjs-viewer/configuration/configuration' {
             bloomKernel?: number;
             hardwareScaleLevel?: number;
             bloomWeight?: number;
-            bllomThreshold?: number;
             hdr?: boolean;
             samples?: number;
             glowLayerEnabled?: boolean;
@@ -1200,9 +1199,9 @@ declare module 'babylonjs-viewer/configuration/configuration' {
                     a: number;
             };
             mainColor?: {
-                    r: number;
-                    g: number;
-                    b: number;
+                    r?: number;
+                    g?: number;
+                    b?: number;
             };
             imageProcessingConfiguration?: IImageProcessingConfiguration;
             environmentTexture?: string;

+ 1 - 1
dist/preview release/viewer/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-viewer",
     "description": "A simple-to-use viewer based on BabylonJS to display 3D elements natively",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 9 - 7
dist/preview release/what's new.md

@@ -6,15 +6,17 @@
 
 ### Core Engine
 
-- Add the choice of [forming a closed loop](http://doc.babylonjs.com/how_to/how_to_use_curve3#catmull-rom-spline) to the catamull-rom-spline curve3 ([johnk](https://github.com/babylonjsguide))
-- Add support for specifying the center of rotation to textures ([bghgary](http://www.github.com/bghgary))
-- Add webVR support for Oculus Go ([TrevorDev](https://github.com/TrevorDev))
-- Add ability to not generate polynomials harmonics upon prefiltered texture creation ([sebavan](http://www.github.com/sebavan))
-- Add predicate function to customize the list of mesh included in the computation of bounding vectors in the ```getHierarchyBoundingVectors``` method ([sebavan](http://www.github.com/sebavan))
+- Added new `Animatable.waitAsync` function to use Promises with animations. Demo [Here](https://www.babylonjs-playground.com/#HZBCXR) ([Deltakosh](https://github.com/deltakosh)) 
+- Added the choice of [forming a closed loop](http://doc.babylonjs.com/how_to/how_to_use_curve3#catmull-rom-spline) to the catamull-rom-spline curve3 ([johnk](https://github.com/babylonjsguide))
+- Added support for specifying the center of rotation to textures ([bghgary](http://www.github.com/bghgary))
+- Added webVR support for Oculus Go ([TrevorDev](https://github.com/TrevorDev))
+- Added ability to not generate polynomials harmonics upon prefiltered texture creation ([sebavan](http://www.github.com/sebavan))
+- Added predicate function to customize the list of mesh included in the computation of bounding vectors in the ```getHierarchyBoundingVectors``` method ([sebavan](http://www.github.com/sebavan))
+- Added webVR constructor options: disable laser pointer toggle, teleportation floor meshes ([TrevorDev](https://github.com/TrevorDev))
 
 ### glTF Loader
 
-- Add support for KHR_texture_transform ([bghgary](http://www.github.com/bghgary))
+- Added support for KHR_texture_transform ([bghgary](http://www.github.com/bghgary))
 
 ### Viewer
 
@@ -26,7 +28,7 @@
 
 ### Core Engine
 
-- Fix ```shadowEnabled``` property on lights. Shadows are not visble anymore when disabled ([sebavan](http://www.github.com/sebavan))
+- Fixed ```shadowEnabled``` property on lights. Shadows are not visble anymore when disabled ([sebavan](http://www.github.com/sebavan))
 - Physics `unregisterOnPhysicsCollide` didn't remove callback correctly [#4291](https://github.com/BabylonJS/Babylon.js/issues/4291) ([RaananW](https://github.com/RaananW))
 - Added missing getter and setter for global exposure in ColorCurves ([RaananW](https://github.com/RaananW))
 - Fixed an issue with view matrix when `ArcRotateCamera` was used with collisions ([Deltakosh](https://github.com/deltakosh)) 

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "3.2.0",
+    "version": "3.3.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 32 - 13
src/Animations/babylon.animatable.ts

@@ -12,6 +12,11 @@
         public animationStarted = false;
 
         /**
+         * Observer raised when the animation ends
+         */
+        public onAnimationEndObservable = new Observable<Animatable>();
+
+        /**
          * Gets the root Animatable used to synchronize and normalize animations
          */
         public get syncRoot(): Animatable {
@@ -190,10 +195,16 @@
             this._paused = false;
         }
 
-        public stop(animationName?: string): void {
+        private _raiseOnAnimationEnd() {            
+            if (this.onAnimationEnd) {
+                this.onAnimationEnd();
+            }
 
-            if (animationName) {
+            this.onAnimationEndObservable.notifyObservers(this);
+        }
 
+        public stop(animationName?: string): void {
+            if (animationName) {
                 var idx = this._scene._activeAnimatables.indexOf(this);
 
                 if (idx > -1) {
@@ -211,10 +222,7 @@
 
                     if (runtimeAnimations.length == 0) {
                         this._scene._activeAnimatables.splice(idx, 1);
-
-                        if (this.onAnimationEnd) {
-                            this.onAnimationEnd();
-                        }
+                        this._raiseOnAnimationEnd();
                     }
                 }
 
@@ -230,14 +238,24 @@
                         runtimeAnimations[index].dispose();
                     }
 
-                    if (this.onAnimationEnd) {
-                        this.onAnimationEnd();
-                    }
+                    this._raiseOnAnimationEnd();
                 }
-
             }
         }
 
+        /**
+         * Wait asynchronously for the animation to end
+         * @returns a promise which will be fullfilled when the animation ends
+         */
+        public waitAsync(): Promise<Animatable> {
+            return new Promise((resolve, reject) => {
+                this.onAnimationEndObservable.add(() => {
+                    resolve(this);
+                }, undefined, undefined, this, true);
+            });
+        }
+
+        /** @hidden */
         public _animate(delay: number): boolean {
             if (this._paused) {
                 this.animationStarted = false;
@@ -283,9 +301,10 @@
                 }
             }
 
-            if (!running && this.onAnimationEnd) {
-                this.onAnimationEnd();
-                this.onAnimationEnd = null;
+            if (!running) {
+                this._raiseOnAnimationEnd();
+                this.onAnimationEnd = null
+                this.onAnimationEndObservable.clear();
             }
 
             return running;

+ 31 - 14
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -18,13 +18,21 @@ module BABYLON {
      */
     export interface VRExperienceHelperOptions extends WebVROptions {
         /**
-         * Create a DeviceOrientationCamera to be used as your out of vr camera.
+         * Create a DeviceOrientationCamera to be used as your out of vr camera. (default: true)
          */
         createDeviceOrientationCamera?: boolean;
         /**
-         * Create a VRDeviceOrientationFreeCamera to be used for VR when no external HMD is found.
+         * Create a VRDeviceOrientationFreeCamera to be used for VR when no external HMD is found. (default: true)
          */
         createFallbackVRDeviceOrientationFreeCamera?: boolean;
+        /**
+         * Uses the main button on the controller to toggle the laser casted. (default: true)
+         */
+        laserToggle?:boolean;
+        /**
+         * A list of meshes to be used as the teleportation floor. If specified, teleportation will be enabled (default: undefined)
+         */
+        floorMeshes?: Mesh[];
     }
 
     class VRExperienceHelperGazer implements IDisposable {
@@ -509,6 +517,9 @@ module BABYLON {
             if (webVROptions.createDeviceOrientationCamera === undefined) {
                 webVROptions.createDeviceOrientationCamera = true;
             }
+            if (webVROptions.laserToggle === undefined) {
+                webVROptions.laserToggle = true;
+            }
             if (webVROptions.defaultHeight === undefined) {
                 webVROptions.defaultHeight = 1.7;
             }
@@ -668,6 +679,10 @@ module BABYLON {
             //create easing functions
             this._circleEase = new CircleEase();
             this._circleEase.setEasingMode(EasingFunction.EASINGMODE_EASEINOUT);
+
+            if(this.webVROptions.floorMeshes){
+                this.enableTeleportation({floorMeshes: this.webVROptions.floorMeshes});
+            }
         }
 
         // Raised when one of the controller has loaded successfully its associated default mesh
@@ -1108,19 +1123,21 @@ module BABYLON {
                 
                 controller._interactionsEnabled = true;
                 controller._activatePointer();
-                controller.webVRController.onMainButtonStateChangedObservable.add((stateObject) => {
-                    // Enabling / disabling laserPointer 
-                    if (this._displayLaserPointer && stateObject.value === 1) {
-                        if(controller._activePointer){
-                            controller._deactivatePointer();
-                        }else{
-                            controller._activatePointer();
-                        }
-                        if(this.displayGaze){
-                            controller._gazeTracker.isVisible = controller._activePointer;
+                if(this.webVROptions.laserToggle){
+                    controller.webVRController.onMainButtonStateChangedObservable.add((stateObject) => {
+                        // Enabling / disabling laserPointer 
+                        if (this._displayLaserPointer && stateObject.value === 1) {
+                            if(controller._activePointer){
+                                controller._deactivatePointer();
+                            }else{
+                                controller._activatePointer();
+                            }
+                            if(this.displayGaze){
+                                controller._gazeTracker.isVisible = controller._activePointer;
+                            }
                         }
-                    }
-                });
+                    });
+                }
                 controller.webVRController.onTriggerStateChangedObservable.add((stateObject) => {
                     if (!controller._pointerDownOnMeshAsked) {
                         if (stateObject.value > this._padSensibilityUp) {

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -726,7 +726,7 @@
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "3.3.0-alpha.0";
+            return "3.3.0-alpha.1";
         }
 
         // Updatable statics so stick with vars here

+ 44 - 4
src/Mesh/Compression/babylon.dracoCompression.ts

@@ -29,12 +29,50 @@ module BABYLON {
 
     /**
      * Draco compression (https://google.github.io/draco/)
+     * 
+     * This class wraps the Draco module.
+     * 
+     * **Encoder**
+     * 
+     * The encoder is not currently implemented.
+     * 
+     * **Decoder**
+     * 
+     * By default, the configuration points to a copy of the Draco decoder files for glTF from https://preview.babylonjs.com.
+     * 
+     * To update the configuration, use the following code:
+     * ```javascript
+     *     BABYLON.DracoCompression.Configuration = {
+     *         decoder: {
+     *             wasmUrl: "<url to the WebAssembly library>",
+     *             wasmBinaryUrl: "<url to the WebAssembly binary>",
+     *             fallbackUrl: "<url to the fallback JavaScript library>",
+     *         }
+     *     };
+     * ```
+     * 
+     * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
+     * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
+     * Use `BABYLON.DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+     * 
+     * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+     * ```javascript
+     *     var dracoCompression = new BABYLON.DracoCompression();
+     *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
+     *         [BABYLON.VertexBuffer.PositionKind]: 0
+     *     });
+     * ```
+     * 
+     * @see https://www.babylonjs-playground.com/#N3EK4B#0
      */
     export class DracoCompression implements IDisposable {
         private static _DecoderModulePromise: Promise<any>;
 
         /**
-         * The configuration.
+         * The configuration. Defaults to the following urls:
+         * - wasmUrl: "https://preview.babylonjs.com/draco_wasm_wrapper_gltf.js"
+         * - wasmBinaryUrl: "https://preview.babylonjs.com/draco_decoder_gltf.wasm"
+         * - fallbackUrl: "https://preview.babylonjs.com/draco_decoder_gltf.js"
          */
         public static Configuration: IDracoCompressionConfiguration = {
             decoder: {
@@ -80,17 +118,19 @@ module BABYLON {
 
         /**
          * Decode Draco compressed mesh data to vertex data.
-         * @param data The array buffer view for the Draco compression data
+         * @param data The ArrayBuffer or ArrayBufferView for the Draco compression data
          * @param attributes A map of attributes from vertex buffer kinds to Draco unique ids
          * @returns A promise that resolves with the decoded vertex data
          */
-        public decodeMeshAsync(data: ArrayBufferView, attributes: { [kind: string]: number }): Promise<VertexData> {
+        public decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes: { [kind: string]: number }): Promise<VertexData> {
+            const dataView = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
+
             return DracoCompression._GetDecoderModule().then(wrappedModule => {
                 const module = wrappedModule.module;
                 const vertexData = new VertexData();
 
                 const buffer = new module.DecoderBuffer();
-                buffer.Init(data, data.byteLength);
+                buffer.Init(dataView, dataView.byteLength);
 
                 const decoder = new module.Decoder();
                 let geometry: any;

+ 4 - 4
src/Mesh/babylon.mesh.ts

@@ -177,9 +177,6 @@
             scene = this.getScene();
 
             if (source) {
-                // Source mesh
-                this._source = source;
-
                 // Geometry
                 if (source._geometry) {
                     source._geometry.applyToMesh(this);
@@ -188,7 +185,10 @@
                 // Deep copy
                 Tools.DeepCopy(source, this, ["name", "material", "skeleton", "instances", "parent", "uniqueId", 
                                               "source", "metadata", "hasLODLevels", "geometry", "isBlocked", "areNormalsFrozen"], 
-                                              ["_poseMatrix", "_source"]);
+                                              ["_poseMatrix"]);
+
+                // Source mesh
+                this._source = source;
 
                 // Metadata
                 if (source.metadata && source.metadata.clone) {

+ 1 - 1
src/Particles/EmitterTypes/babylon.coneParticleEmitter.ts

@@ -111,7 +111,7 @@ module BABYLON {
          */        
         public applyToShader(effect: Effect): void {
             effect.setFloat("radius", this.radius);
-            effect.setFloat("angle", this.angle);
+            effect.setFloat("coneAngle", this.angle);
             effect.setFloat("height", this._height);
             effect.setFloat("directionRandomizer", this.directionRandomizer);
         }

+ 28 - 7
src/Particles/babylon.gpuParticleSystem.ts

@@ -57,7 +57,7 @@
 
         private _randomTexture: RawTexture;
 
-        private readonly _attributesStrideSize = 14;
+        private readonly _attributesStrideSize = 16;
         private _updateEffectOptions: EffectCreationOptions;
 
         private _randomTextureSize: number;
@@ -155,6 +155,15 @@
         public maxEmitPower = 1;        
 
         /**
+         * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
+         */
+        public minAngularSpeed = 0;
+        /**
+         * Maximum angular speed of emitting particles (Z-axis rotation for each particle).
+         */
+        public maxAngularSpeed = 0;
+
+        /**
          * The particle emitter type defines the emitter used by the particle system.
          * It can be for example box, sphere, or cone...
          */
@@ -346,9 +355,10 @@
             this._scene.particleSystems.push(this);
 
             this._updateEffectOptions = {
-                attributes: ["position", "age", "life", "seed", "size", "color", "direction"],
+                attributes: ["position", "age", "life", "seed", "size", "color", "direction", "angle"],
                 uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity", "emitPower",
-                                "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "angle", "stopFactor"],
+                                "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
+                                "angleRange"],
                 uniformBuffersNames: [],
                 samplers:["randomSampler"],
                 defines: "",
@@ -357,7 +367,7 @@
                 onError: null,
                 indexParameters: null,
                 maxSimultaneousLights: 0,                                                      
-                transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outSeed", "outSize", "outColor", "outDirection"]
+                transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outSeed", "outSize", "outColor", "outDirection", "outAngle"]
             };
 
             // Random data
@@ -386,6 +396,7 @@
             updateVertexBuffers["size"] = source.createVertexBuffer("size", 6, 1);
             updateVertexBuffers["color"] = source.createVertexBuffer("color", 7, 4);
             updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 11, 3);
+            updateVertexBuffers["angle"] = source.createVertexBuffer("angle", 14, 2);
            
             let vao = this._engine.recordVertexArrayObject(updateVertexBuffers, null, this._updateEffect);
             this._engine.bindArrayBuffer(null);
@@ -400,6 +411,7 @@
             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["angle"] = source.createVertexBuffer("angle", 14, 2, this._attributesStrideSize, true);
 
             renderVertexBuffers["offset"] = spriteSource.createVertexBuffer("offset", 0, 2);
             renderVertexBuffers["uv"] = spriteSource.createVertexBuffer("uv", 2, 2);
@@ -442,7 +454,11 @@
               // direction
               data.push(0.0);
               data.push(0.0);
-              data.push(0.0);              
+              data.push(0.0);     
+              
+              // angle
+              data.push(0.0);  
+              data.push(0.0); 
             }
 
             // Sprite data
@@ -494,7 +510,7 @@
             }
 
             this._renderEffect = new Effect("gpuRenderParticles", 
-                                            ["position", "age", "life", "size", "color", "offset", "uv"], 
+                                            ["position", "age", "life", "size", "color", "offset", "uv", "angle"], 
                                             ["view", "projection", "colorDead", "invView", "vClipPlane"], 
                                             ["textureSampler"], this._scene.getEngine(), defines);
         }        
@@ -555,6 +571,7 @@
             this._updateEffect.setDirectColor4("color1", this.color1);
             this._updateEffect.setDirectColor4("color2", this.color2);
             this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
+            this._updateEffect.setFloat2("angleRange", this.minAngularSpeed, this.maxAngularSpeed);
             this._updateEffect.setVector3("gravity", this.gravity);
 
             if (this.particleEmitterType) {
@@ -757,7 +774,9 @@
             serializationObject.minEmitPower = this.minEmitPower;
             serializationObject.maxEmitPower = this.maxEmitPower;
             serializationObject.minLifeTime = this.minLifeTime;
-            serializationObject.maxLifeTime = this.maxLifeTime;
+            serializationObject.maxLifeTime = this.maxLifeTime;            
+            serializationObject.minAngularSpeed = this.minAngularSpeed;
+            serializationObject.maxAngularSpeed = this.maxAngularSpeed;
             serializationObject.emitRate = this.emitRate;
             serializationObject.gravity = this.gravity.asArray();
             serializationObject.color1 = this.color1.asArray();
@@ -817,6 +836,8 @@
             particleSystem.maxSize = parsedParticleSystem.maxSize;
             particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
             particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
+            particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
+            particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
             particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
             particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
             particleSystem.emitRate = parsedParticleSystem.emitRate;

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

@@ -12,6 +12,7 @@ in float size;
 in vec4 color;
 in vec2 offset;
 in vec2 uv;
+in vec2 angle;
 
 out vec2 vUV;
 out vec4 vColor;
@@ -28,9 +29,18 @@ void main() {
   float ratio = age / life;
   vColor = color * vec4(1.0 - ratio) + colorDead * vec4(ratio);
 
+  vec2 cornerPos = offset * size;
+
+  // Rotate
+	vec4 rotatedCorner;
+	rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
+	rotatedCorner.y = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+	rotatedCorner.z = 0.;
+  rotatedCorner.w = 0.;
+
   // Expand position
   vec4 viewPosition = view * vec4(position, 1.0);
-  gl_Position = projection * (viewPosition + vec4(offset * size, 0., 0.));
+  gl_Position = projection * (viewPosition + rotatedCorner);
 
 	// Clip plane
 #ifdef CLIPPLANE

+ 11 - 2
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -14,6 +14,7 @@ uniform vec4 color1;
 uniform vec4 color2;
 uniform vec3 gravity;
 uniform sampler2D randomSampler;
+uniform vec2 angleRange;
 
 #ifdef BOXEMITTER
 uniform vec3 direction1;
@@ -34,7 +35,7 @@ uniform float radius;
 
 #ifdef CONEEMITTER
 uniform float radius;
-uniform float angle;
+uniform float coneAngle;
 uniform float height;
 uniform float directionRandomizer;
 #endif
@@ -47,6 +48,7 @@ in float seed;
 in float size;
 in vec4 color;
 in vec3 direction;
+in vec2 angle;
 
 // Output
 out vec3 outPosition;
@@ -56,6 +58,7 @@ out float outSeed;
 out float outSize;
 out vec4 outColor;
 out vec3 outDirection;
+out vec2 outAngle;
 
 vec3 getRandomVec3(float offset) {
   return texture(randomSampler, vec2(float(gl_VertexID) * offset / currentCount, 0)).rgb;
@@ -76,6 +79,7 @@ void main() {
       outColor = vec4(0.,0.,0.,0.);
       outSize = 0.;
       outDirection = direction;
+      outAngle = angle;
       return;
     }
     vec3 position;
@@ -97,6 +101,10 @@ void main() {
     // Color
     outColor = color1 + (color2 - color1) * randoms.b;
 
+    // Angular speed
+    outAngle.y = angleRange.x + (angleRange.y - angleRange.x) * randoms.a;
+    outAngle.x = 0.;
+
     // Position / Direction (based on emitter type)
 #ifdef BOXEMITTER
     vec3 randoms2 = getRandomVec3(generalRandoms.y);
@@ -142,7 +150,7 @@ void main() {
     position = vec3(randX, randY, randZ); 
 
     // Direction
-    if (angle == 0.) {
+    if (coneAngle == 0.) {
         direction = vec3(0., 1.0, 0.);
     } else {
         vec3 randoms3 = getRandomVec3(generalRandoms.z);
@@ -169,5 +177,6 @@ void main() {
     outColor = color;
     outSize = size;
     outDirection = direction + gravity * timeDelta;
+    outAngle = vec2(angle.x + angle.y * timeDelta, angle.y);
   }
 }

+ 2 - 2
src/Shaders/particles.vertex.fx

@@ -21,12 +21,12 @@ varying float fClipDistance;
 
 void main(void) {	
 	vec3 viewPos = (view * vec4(position, 1.0)).xyz; 
-	vec3 cornerPos;
+	vec2 cornerPos;
 	float size = options.y;
 	float angle = options.x;
 	vec2 offset = options.zw;
 	
-	cornerPos = vec3(offset.x - 0.5, offset.y  - 0.5, 0.) * size;
+	cornerPos = vec2(offset.x - 0.5, offset.y  - 0.5) * size;
 
 	// Rotate
 	vec3 rotatedCorner;