sebastien 6 年之前
父節點
當前提交
647775717b
共有 71 個文件被更改,包括 16778 次插入14160 次删除
  1. 6764 6481
      Playground/babylon.d.txt
  2. 3 1
      Tools/Gulp/config.json
  3. 6713 6493
      dist/preview release/babylon.d.ts
  4. 1 1
      dist/preview release/babylon.js
  5. 690 219
      dist/preview release/babylon.max.js
  6. 690 219
      dist/preview release/babylon.no-module.max.js
  7. 1 1
      dist/preview release/babylon.worker.js
  8. 692 221
      dist/preview release/es6.js
  9. 1 4
      dist/preview release/glTF2Interface/package.json
  10. 18 2
      dist/preview release/gui/babylon.gui.d.ts
  11. 47 5
      dist/preview release/gui/babylon.gui.js
  12. 1 1
      dist/preview release/gui/babylon.gui.js.map
  13. 1 1
      dist/preview release/gui/babylon.gui.min.js
  14. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  15. 39 5
      dist/preview release/gui/babylon.gui.module.d.ts
  16. 2 2
      dist/preview release/gui/package.json
  17. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  18. 5 5
      dist/preview release/inspector/package.json
  19. 3 5
      dist/preview release/loaders/package.json
  20. 2 2
      dist/preview release/materialsLibrary/package.json
  21. 3 9
      dist/preview release/postProcessesLibrary/package.json
  22. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  23. 3 3
      dist/preview release/serializers/package.json
  24. 14 14
      dist/preview release/viewer/babylon.viewer.js
  25. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  26. 26 5
      dist/preview release/what's new.md
  27. 31 1
      gui/src/2D/controls/button.ts
  28. 3 1
      gui/src/2D/controls/container.ts
  29. 7 2
      gui/src/2D/controls/control.ts
  30. 8 2
      gui/src/2D/controls/stackPanel.ts
  31. 1 1
      inspector/src/tools/FullscreenTool.ts
  32. 1 1
      package.json
  33. 4 3
      src/Cameras/Inputs/babylon.arcRotateCameraMouseWheelInput.ts
  34. 1 1
      src/Cameras/Inputs/babylon.freeCameraDeviceOrientationInput.ts
  35. 4 4
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  36. 100 0
      src/Cameras/XR/babylon.webXRCamera.ts
  37. 143 0
      src/Cameras/XR/babylon.webXRSessionManager.ts
  38. 12 3
      src/Cameras/babylon.camera.ts
  39. 4 3
      src/Culling/babylon.boundingBox.ts
  40. 14 20
      src/Culling/babylon.boundingSphere.ts
  41. 29 8
      src/Engine/babylon.engine.ts
  42. 1 1
      src/Engine/babylon.nullEngine.ts
  43. 18 11
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  44. 5 2
      src/Materials/Textures/babylon.internalTexture.ts
  45. 11 9
      src/Materials/Textures/babylon.renderTargetTexture.ts
  46. 71 45
      src/Materials/babylon.material.ts
  47. 2 2
      src/Materials/babylon.multiMaterial.ts
  48. 14 7
      src/Materials/babylon.pushMaterial.ts
  49. 3 2
      src/Materials/babylon.shaderMaterial.ts
  50. 15 4
      src/Materials/babylon.standardMaterial.ts
  51. 4 2
      src/Materials/babylon.uniformBuffer.ts
  52. 21 4
      src/Math/babylon.math.ts
  53. 13 4
      src/Mesh/babylon.abstractMesh.ts
  54. 5 5
      src/Mesh/babylon.instancedMesh.ts
  55. 1 2
      src/Mesh/babylon.linesMesh.ts
  56. 61 11
      src/Mesh/babylon.mesh.ts
  57. 58 65
      src/Mesh/babylon.transformNode.ts
  58. 19 20
      src/Particles/babylon.gpuParticleSystem.ts
  59. 149 149
      src/Particles/babylon.particleSystem.ts
  60. 14 4
      src/Particles/babylon.solidParticleSystem.ts
  61. 2 2
      src/PostProcess/babylon.motionBlurPostProcess.ts
  62. 20 0
      src/Tools/babylon.tools.ts
  63. 6 0
      src/babylon.abstractScene.ts
  64. 7 2
      src/babylon.assetContainer.ts
  65. 41 10
      src/babylon.mixins.ts
  66. 119 37
      src/babylon.scene.ts
  67. 1 1
      src/babylon.sceneComponent.ts
  68. 二進制
      tests/validation/ReferenceImages/cameraRig.png
  69. 二進制
      tests/validation/ReferenceImages/sps.png
  70. 7 1
      tests/validation/config.json
  71. 3 3
      tests/validation/validation.js

文件差異過大導致無法顯示
+ 6764 - 6481
Playground/babylon.d.txt


+ 3 - 1
Tools/Gulp/config.json

@@ -1300,7 +1300,9 @@
                 "../../src/Cameras/VR/babylon.vrDeviceOrientationFreeCamera.js",
                 "../../src/Cameras/VR/babylon.vrDeviceOrientationFreeCamera.js",
                 "../../src/Cameras/VR/babylon.vrDeviceOrientationArcRotateCamera.js",
                 "../../src/Cameras/VR/babylon.vrDeviceOrientationArcRotateCamera.js",
                 "../../src/Cameras/VR/babylon.vrDeviceOrientationGamepadCamera.js",
                 "../../src/Cameras/VR/babylon.vrDeviceOrientationGamepadCamera.js",
-                "../../src/Cameras/VR/babylon.vrExperienceHelper.js"
+                "../../src/Cameras/VR/babylon.vrExperienceHelper.js",
+                "../../src/Cameras/XR/babylon.webXRCamera.js",
+                "../../src/Cameras/XR/babylon.webXRSessionManager.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
                 "core",
                 "core",

文件差異過大導致無法顯示
+ 6713 - 6493
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/babylon.js


文件差異過大導致無法顯示
+ 690 - 219
dist/preview release/babylon.max.js


文件差異過大導致無法顯示
+ 690 - 219
dist/preview release/babylon.no-module.max.js


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/babylon.worker.js


文件差異過大導致無法顯示
+ 692 - 221
dist/preview release/es6.js


+ 1 - 4
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
 {
     "name": "babylonjs-gltf2interface",
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "3.3.0",
+    "version": "4.0.0-alpha.2",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -19,9 +19,6 @@
         "gltf2"
         "gltf2"
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
-    "peerDependencies": {
-        "babylonjs": ">=3.3.0-rc.4"
-    },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"
     }
     }

+ 18 - 2
dist/preview release/gui/babylon.gui.d.ts

@@ -582,6 +582,14 @@ declare module BABYLON.GUI {
                 */
                 */
             pointerUpAnimation: () => void;
             pointerUpAnimation: () => void;
             /**
             /**
+                * Returns the image part of the button (if any)
+                */
+            readonly image: BABYLON.Nullable<Image>;
+            /**
+                * Returns the image part of the button (if any)
+                */
+            readonly textBlock: BABYLON.Nullable<TextBlock>;
+            /**
                 * Creates a new Button
                 * Creates a new Button
                 * @param name defines the name of the button
                 * @param name defines the name of the button
                 */
                 */
@@ -837,6 +845,8 @@ declare module BABYLON.GUI {
             isPointerBlocker: boolean;
             isPointerBlocker: boolean;
             /** Gets or sets a boolean indicating if the control can be focusable */
             /** Gets or sets a boolean indicating if the control can be focusable */
             isFocusInvisible: boolean;
             isFocusInvisible: boolean;
+            /** Gets or sets a boolean indicating if the children are clipped to the current control bounds */
+            clipChildren: boolean;
             /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
             /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
             shadowOffsetX: number;
             shadowOffsetX: number;
             /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
             /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
@@ -1603,9 +1613,15 @@ declare module BABYLON.GUI {
             name?: string | undefined;
             name?: string | undefined;
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             isVertical: boolean;
             isVertical: boolean;
-            /** Gets or sets panel width */
+            /**
+                * Gets or sets panel width.
+                * This value should not be set when in horizontal mode as it will be computed automatically
+                */
             width: string | number;
             width: string | number;
-            /** Gets or sets panel height */
+            /**
+                * Gets or sets panel height.
+                * This value should not be set when in vertical mode as it will be computed automatically
+                */
             height: string | number;
             height: string | number;
             /**
             /**
                 * Creates a new StackPanel
                 * Creates a new StackPanel

+ 47 - 5
dist/preview release/gui/babylon.gui.js

@@ -902,6 +902,26 @@ var Button = /** @class */ (function (_super) {
         };
         };
         return _this;
         return _this;
     }
     }
+    Object.defineProperty(Button.prototype, "image", {
+        /**
+         * Returns the image part of the button (if any)
+         */
+        get: function () {
+            return this._image;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(Button.prototype, "textBlock", {
+        /**
+         * Returns the image part of the button (if any)
+         */
+        get: function () {
+            return this._textBlock;
+        },
+        enumerable: true,
+        configurable: true
+    });
     Button.prototype._getTypeName = function () {
     Button.prototype._getTypeName = function () {
         return "Button";
         return "Button";
     };
     };
@@ -973,6 +993,9 @@ var Button = /** @class */ (function (_super) {
         iconImage.stretch = image_1.Image.STRETCH_UNIFORM;
         iconImage.stretch = image_1.Image.STRETCH_UNIFORM;
         iconImage.horizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_LEFT;
         iconImage.horizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_LEFT;
         result.addControl(iconImage);
         result.addControl(iconImage);
+        // Store
+        result._image = iconImage;
+        result._textBlock = textBlock;
         return result;
         return result;
     };
     };
     /**
     /**
@@ -988,6 +1011,8 @@ var Button = /** @class */ (function (_super) {
         iconImage.stretch = image_1.Image.STRETCH_FILL;
         iconImage.stretch = image_1.Image.STRETCH_FILL;
         iconImage.horizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_LEFT;
         iconImage.horizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_LEFT;
         result.addControl(iconImage);
         result.addControl(iconImage);
+        // Store
+        result._image = iconImage;
         return result;
         return result;
     };
     };
     /**
     /**
@@ -1003,6 +1028,8 @@ var Button = /** @class */ (function (_super) {
         textBlock.textWrapping = true;
         textBlock.textWrapping = true;
         textBlock.textHorizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_CENTER;
         textBlock.textHorizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_CENTER;
         result.addControl(textBlock);
         result.addControl(textBlock);
+        // Store
+        result._textBlock = textBlock;
         return result;
         return result;
     };
     };
     /**
     /**
@@ -1023,6 +1050,9 @@ var Button = /** @class */ (function (_super) {
         textBlock.textWrapping = true;
         textBlock.textWrapping = true;
         textBlock.textHorizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_CENTER;
         textBlock.textHorizontalAlignment = control_1.Control.HORIZONTAL_ALIGNMENT_CENTER;
         result.addControl(textBlock);
         result.addControl(textBlock);
+        // Store
+        result._image = iconImage;
+        result._textBlock = textBlock;
         return result;
         return result;
     };
     };
     return Button;
     return Button;
@@ -1869,7 +1899,9 @@ var Container = /** @class */ (function (_super) {
         this._applyStates(context);
         this._applyStates(context);
         if (this._processMeasures(parentMeasure, context)) {
         if (this._processMeasures(parentMeasure, context)) {
             this._localDraw(context);
             this._localDraw(context);
-            this._clipForChildren(context);
+            if (this.clipChildren) {
+                this._clipForChildren(context);
+            }
             var computedWidth = -1;
             var computedWidth = -1;
             var computedHeight = -1;
             var computedHeight = -1;
             for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
             for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
@@ -2034,6 +2066,8 @@ var Control = /** @class */ (function () {
         this.isPointerBlocker = false;
         this.isPointerBlocker = false;
         /** Gets or sets a boolean indicating if the control can be focusable */
         /** Gets or sets a boolean indicating if the control can be focusable */
         this.isFocusInvisible = false;
         this.isFocusInvisible = false;
+        /** Gets or sets a boolean indicating if the children are clipped to the current control bounds */
+        this.clipChildren = true;
         /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
         /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
         this.shadowOffsetX = 0;
         this.shadowOffsetX = 0;
         /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
         /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
@@ -2995,8 +3029,10 @@ var Control = /** @class */ (function () {
             return false; // We do not want rendering for this frame as they are measure dependant information that need to be gathered
             return false; // We do not want rendering for this frame as they are measure dependant information that need to be gathered
         }
         }
         // Clip
         // Clip
-        this._clip(context);
-        context.clip();
+        if (this.clipChildren) {
+            this._clip(context);
+            context.clip();
+        }
         return true;
         return true;
     };
     };
     /** @hidden */
     /** @hidden */
@@ -7187,7 +7223,10 @@ var StackPanel = /** @class */ (function (_super) {
         get: function () {
         get: function () {
             return this._width.toString(this._host);
             return this._width.toString(this._host);
         },
         },
-        /** Gets or sets panel width */
+        /**
+         * Gets or sets panel width.
+         * This value should not be set when in horizontal mode as it will be computed automatically
+         */
         set: function (value) {
         set: function (value) {
             if (!this._doNotTrackManualChanges) {
             if (!this._doNotTrackManualChanges) {
                 this._manualWidth = true;
                 this._manualWidth = true;
@@ -7206,7 +7245,10 @@ var StackPanel = /** @class */ (function (_super) {
         get: function () {
         get: function () {
             return this._height.toString(this._host);
             return this._height.toString(this._host);
         },
         },
-        /** Gets or sets panel height */
+        /**
+         * Gets or sets panel height.
+         * This value should not be set when in vertical mode as it will be computed automatically
+         */
         set: function (value) {
         set: function (value) {
             if (!this._doNotTrackManualChanges) {
             if (!this._doNotTrackManualChanges) {
                 this._manualHeight = true;
                 this._manualHeight = true;

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js.map


+ 39 - 5
dist/preview release/gui/babylon.gui.module.d.ts

@@ -652,7 +652,9 @@ declare module 'babylonjs-gui/3D/vector3WithInfo' {
 declare module 'babylonjs-gui/2D/controls/button' {
 declare module 'babylonjs-gui/2D/controls/button' {
     import { Rectangle } from "babylonjs-gui/2D/controls/rectangle";
     import { Rectangle } from "babylonjs-gui/2D/controls/rectangle";
     import { Control } from "babylonjs-gui/2D/controls/control";
     import { Control } from "babylonjs-gui/2D/controls/control";
-    import { Vector2 } from "babylonjs";
+    import { TextBlock } from "babylonjs-gui/2D/controls/textBlock";
+    import { Image } from "babylonjs-gui/2D/controls/image";
+    import { Vector2, Nullable } from "babylonjs";
     /**
     /**
         * Class used to create 2D buttons
         * Class used to create 2D buttons
         */
         */
@@ -675,6 +677,14 @@ declare module 'babylonjs-gui/2D/controls/button' {
                 */
                 */
             pointerUpAnimation: () => void;
             pointerUpAnimation: () => void;
             /**
             /**
+                * Returns the image part of the button (if any)
+                */
+            readonly image: Nullable<Image>;
+            /**
+                * Returns the image part of the button (if any)
+                */
+            readonly textBlock: Nullable<TextBlock>;
+            /**
                 * Creates a new Button
                 * Creates a new Button
                 * @param name defines the name of the button
                 * @param name defines the name of the button
                 */
                 */
@@ -952,6 +962,8 @@ declare module 'babylonjs-gui/2D/controls/control' {
             isPointerBlocker: boolean;
             isPointerBlocker: boolean;
             /** Gets or sets a boolean indicating if the control can be focusable */
             /** Gets or sets a boolean indicating if the control can be focusable */
             isFocusInvisible: boolean;
             isFocusInvisible: boolean;
+            /** Gets or sets a boolean indicating if the children are clipped to the current control bounds */
+            clipChildren: boolean;
             /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
             /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
             shadowOffsetX: number;
             shadowOffsetX: number;
             /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
             /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
@@ -1754,9 +1766,15 @@ declare module 'babylonjs-gui/2D/controls/stackPanel' {
             name?: string | undefined;
             name?: string | undefined;
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             isVertical: boolean;
             isVertical: boolean;
-            /** Gets or sets panel width */
+            /**
+                * Gets or sets panel width.
+                * This value should not be set when in horizontal mode as it will be computed automatically
+                */
             width: string | number;
             width: string | number;
-            /** Gets or sets panel height */
+            /**
+                * Gets or sets panel height.
+                * This value should not be set when in vertical mode as it will be computed automatically
+                */
             height: string | number;
             height: string | number;
             /**
             /**
                 * Creates a new StackPanel
                 * Creates a new StackPanel
@@ -3409,6 +3427,14 @@ declare module BABYLON.GUI {
                 */
                 */
             pointerUpAnimation: () => void;
             pointerUpAnimation: () => void;
             /**
             /**
+                * Returns the image part of the button (if any)
+                */
+            readonly image: BABYLON.Nullable<Image>;
+            /**
+                * Returns the image part of the button (if any)
+                */
+            readonly textBlock: BABYLON.Nullable<TextBlock>;
+            /**
                 * Creates a new Button
                 * Creates a new Button
                 * @param name defines the name of the button
                 * @param name defines the name of the button
                 */
                 */
@@ -3664,6 +3690,8 @@ declare module BABYLON.GUI {
             isPointerBlocker: boolean;
             isPointerBlocker: boolean;
             /** Gets or sets a boolean indicating if the control can be focusable */
             /** Gets or sets a boolean indicating if the control can be focusable */
             isFocusInvisible: boolean;
             isFocusInvisible: boolean;
+            /** Gets or sets a boolean indicating if the children are clipped to the current control bounds */
+            clipChildren: boolean;
             /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
             /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
             shadowOffsetX: number;
             shadowOffsetX: number;
             /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
             /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
@@ -4430,9 +4458,15 @@ declare module BABYLON.GUI {
             name?: string | undefined;
             name?: string | undefined;
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             isVertical: boolean;
             isVertical: boolean;
-            /** Gets or sets panel width */
+            /**
+                * Gets or sets panel width.
+                * This value should not be set when in horizontal mode as it will be computed automatically
+                */
             width: string | number;
             width: string | number;
-            /** Gets or sets panel height */
+            /**
+                * Gets or sets panel height.
+                * This value should not be set when in vertical mode as it will be computed automatically
+                */
             height: string | number;
             height: string | number;
             /**
             /**
                 * Creates a new StackPanel
                 * Creates a new StackPanel

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-gui",
     "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.",
     "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.3.0",
+    "version": "4.0.0-alpha.2",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "3.3.0"
+        "babylonjs": "4.0.0-alpha.2"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js.map


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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-inspector",
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
     "description": "The Babylon.js inspector.",
-    "version": "3.3.0",
+    "version": "4.0.0-alpha.2",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,10 +28,10 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "3.3.0",
-        "babylonjs-gui": "3.3.0",
-        "babylonjs-loaders": "3.3.0",
-        "babylonjs-serializers": "3.3.0"
+        "babylonjs": "4.0.0-alpha.2",
+        "babylonjs-gui": "4.0.0-alpha.2",
+        "babylonjs-loaders": "4.0.0-alpha.2",
+        "babylonjs-serializers": "4.0.0-alpha.2"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 3 - 5
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-loaders",
     "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.",
     "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.3.0",
+    "version": "4.0.0-alpha.2",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,10 +27,8 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs-gltf2interface": "3.3.0"
-    },
-    "peerDependencies": {
-        "babylonjs": ">=3.3.0-rc.4"
+        "babylonjs-gltf2interface": "4.0.0-alpha.2",
+        "babylonjs": "4.0.0-alpha.2"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-materials",
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0",
+    "version": "4.0.0-alpha.2",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "3.3.0"
+        "babylonjs": "4.0.0-alpha.2"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 3 - 9
dist/preview release/postProcessesLibrary/package.json

@@ -2,15 +2,9 @@
     "author": {
     "author": {
         "name": "David CATUHE"
         "name": "David CATUHE"
     },
     },
-<<<<<<< HEAD
-    "name": "babylonjs-post-processes",
-    "description": "The Babylon.js post process library is a collection of advanced post process to be used in a Babylon.js scene.",
-    "version": "3.3.0-rc.5",
-=======
     "name": "babylonjs-post-process",
     "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.3.0",
->>>>>>> upstream/master
+    "description": "The Babylon.js post process library is a collection of advanced post process to be used in a Babylon.js scene.",
+    "version": "4.0.0-alpha.2",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -33,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "3.3.0"
+        "babylonjs": "4.0.0-alpha.2"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-procedural-textures",
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0",
+    "version": "4.0.0-alpha.2",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "3.3.0"
+        "babylonjs": "4.0.0-alpha.2"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

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

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

文件差異過大導致無法顯示
+ 14 - 14
dist/preview release/viewer/babylon.viewer.js


文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/viewer/babylon.viewer.max.js


+ 26 - 5
dist/preview release/what's new.md

@@ -3,18 +3,35 @@
 ## Major updates
 ## Major updates
 
 
 - Added support for [parallel shader compilation](https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/) ([Deltakosh](https://github.com/deltakosh))
 - Added support for [parallel shader compilation](https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/) ([Deltakosh](https://github.com/deltakosh))
-- Added FlyCamera for free navigation in 3D space, with a limited set of settings ([Phuein](https://github.com/phuein))
-- Added Object Based Motion Blur post-process ([julien-moreau](https://github.com/julien-moreau))
+@NEED DEMO- Added FlyCamera for free navigation in 3D space, with a limited set of settings ([Phuein](https://github.com/phuein))
+@NEED DEMO- Added Object Based Motion Blur post-process ([julien-moreau](https://github.com/julien-moreau))
+@NEED DEMO- WebXR ([TrevorDev](https://github.com/TrevorDev))
+  - Add customAnimationFrameRequester to allow sessions to hook into engine's render loop ([TrevorDev](https://github.com/TrevorDev))
+  - camera customDefaultRenderTarget to allow cameras to render to a custom render target (eg. xr framebuffer) instead of the canvas ([TrevorDev](https://github.com/TrevorDev))
+  - webXR camera which can be updated by a webXRSession ([TrevorDev](https://github.com/TrevorDev))
+  - webXRSessionManager to bridge xrSession to babylon's engine/camera ([TrevorDev](https://github.com/TrevorDev))
 
 
 ## Updates
 ## Updates
 
 
 ### GUI
 ### GUI
 
 
+- Added `button.image` and `button.textBlock` to simplify access to button internal parts ([Deltakosh](https://github.com/deltakosh))
+
 ### Core Engine
 ### Core Engine
 
 
-- Refactor of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
+- Refactored of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
 - Added utility function `Tools.BuildArray` for array initialisation ([barroij](https://github.com/barroij))
 - Added utility function `Tools.BuildArray` for array initialisation ([barroij](https://github.com/barroij))
 - Introduced a new `IOfflineSupport` interface to hide IndexedDB ([Deltakosh](https://github.com/deltakosh))
 - Introduced a new `IOfflineSupport` interface to hide IndexedDB ([Deltakosh](https://github.com/deltakosh))
+- `PBRMaterial` and `StandardMaterial` now use hot swapping feature for shaders. This means they can keep using a previous shader while a new one is being compiled ([Deltakosh](https://github.com/deltakosh))
+- Performance oriented changes ([barroij](https://github.com/barroij))
+  - Prevented avoidable matrix inversion or square root computation
+  - Enabled a removal in O(1) from the `transformNodes` array and `materials` array of the `Scene`. As a consequence, the order of the element within these arrays might change during a removal
+  - Enabled a removal in O(1) from the `instances` array of a `Mesh`. As a consequence, the order of the element within this array might change during a removal
+  - Stopped calling `Array.splice` on the `scene.meshes` array and on the `engine._uniformBuffer` when removing an element. As a consequence, the order of the element within these arrays might change during a removal
+  - Added an option `useMaterialMeshMap` in the `Scene` constructor options. When set to true, each `Material` isntance will have and will keep up-to-date a map of its bound meshes. This is to avoid browsing all the meshes of the scene to retrieve the ones bound to the current material when disposing the Material. Disabled by default
+  - Added an option `useClonedMeshhMap` in the `Scene` constructor options. When set to true, each `Mesh` will have and will keep up-to-date a map of cloned meshes. This is to avoid browsing all the meshes of the scene to retrieve the ones that have the current mesh as source mesh. Disabled by default
+  - Added `blockfreeActiveMeshesAndRenderingGroups` property in the `Scene`, following the same model as `blockMaterialDirtyMechanism`. This is to avoid calling `Scene.freeActiveMeshes` and `Scene.freeRenderingGroups` for each disposed mesh when we dispose several meshes in a row. One have to set `blockfreeActiveMeshesAndRenderingGroups` to `true` just before disposing the meshes, and set it back to `false` just after
+  - Prevented code from doing useless and possible time consuming computation when disposing the `ShaderMaterial` of a `LinesMesh`
 
 
 ### glTF Loader
 ### glTF Loader
 
 
@@ -25,9 +42,12 @@
 ### Materials Library
 ### Materials Library
 
 
 ## Bug fixes
 ## Bug fixes
+- Add missing effect layer to asset container ([TrevorDev](https://github.com/TrevorDev))
 
 
 ### Core Engine
 ### Core Engine
-
+- Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
+- Fixed a bug with pointer up being fire twice ([Deltakosh](https://github.com/deltakosh))
+- Fixed a bug with particle systems being update once per camera instead of once per frame ([Deltakosh](https://github.com/deltakosh))
 
 
 
 
 ### Viewer
 ### Viewer
@@ -37,4 +57,5 @@
 ## Breaking changes
 ## Breaking changes
 
 
 - `Database.IDBStorageEnabled` is now false by default ([Deltakosh](https://github.com/deltakosh))
 - `Database.IDBStorageEnabled` is now false by default ([Deltakosh](https://github.com/deltakosh))
-- `Database.openAsync` was renamed by `Database.open`
+- `Database.openAsync` was renamed by `Database.open` ([Deltakosh](https://github.com/deltakosh))
+- `scene.database` was renamed to `scene.offlineProvider` ([Deltakosh](https://github.com/deltakosh))

+ 31 - 1
gui/src/2D/controls/button.ts

@@ -2,7 +2,7 @@ import { Rectangle } from "./rectangle";
 import { Control } from "./control";
 import { Control } from "./control";
 import { TextBlock } from "./textBlock";
 import { TextBlock } from "./textBlock";
 import { Image } from "./image";
 import { Image } from "./image";
-import { Vector2 } from "babylonjs";
+import { Vector2, Nullable } from "babylonjs";
 
 
 /**
 /**
  * Class used to create 2D buttons
  * Class used to create 2D buttons
@@ -25,6 +25,22 @@ export class Button extends Rectangle {
      */
      */
     public pointerUpAnimation: () => void;
     public pointerUpAnimation: () => void;
 
 
+    private _image: Nullable<Image>;
+    /**
+     * Returns the image part of the button (if any)
+     */
+    public get image(): Nullable<Image> {
+        return this._image;
+    }
+
+    private _textBlock: Nullable<TextBlock>;
+    /**
+     * Returns the image part of the button (if any)
+     */
+    public get textBlock(): Nullable<TextBlock> {
+        return this._textBlock;
+    }
+
     /**
     /**
      * Creates a new Button
      * Creates a new Button
      * @param name defines the name of the button
      * @param name defines the name of the button
@@ -143,6 +159,10 @@ export class Button extends Rectangle {
         iconImage.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         iconImage.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         result.addControl(iconImage);
         result.addControl(iconImage);
 
 
+        // Store
+        result._image = iconImage;
+        result._textBlock = textBlock;
+
         return result;
         return result;
     }
     }
 
 
@@ -161,6 +181,9 @@ export class Button extends Rectangle {
         iconImage.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         iconImage.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         result.addControl(iconImage);
         result.addControl(iconImage);
 
 
+        // Store
+        result._image = iconImage;
+
         return result;
         return result;
     }
     }
 
 
@@ -179,6 +202,9 @@ export class Button extends Rectangle {
         textBlock.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         textBlock.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         result.addControl(textBlock);
         result.addControl(textBlock);
 
 
+        // Store
+        result._textBlock = textBlock;
+
         return result;
         return result;
     }
     }
 
 
@@ -203,6 +229,10 @@ export class Button extends Rectangle {
         textBlock.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         textBlock.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         result.addControl(textBlock);
         result.addControl(textBlock);
 
 
+        // Store
+        result._image = iconImage;
+        result._textBlock = textBlock;
+
         return result;
         return result;
     }
     }
 }
 }

+ 3 - 1
gui/src/2D/controls/container.ts

@@ -266,7 +266,9 @@ export class Container extends Control {
         if (this._processMeasures(parentMeasure, context)) {
         if (this._processMeasures(parentMeasure, context)) {
             this._localDraw(context);
             this._localDraw(context);
 
 
-            this._clipForChildren(context);
+            if (this.clipChildren) {
+                this._clipForChildren(context);
+            }
 
 
             let computedWidth = -1;
             let computedWidth = -1;
             let computedHeight = -1;
             let computedHeight = -1;

+ 7 - 2
gui/src/2D/controls/control.ts

@@ -88,6 +88,9 @@ export class Control {
     /** Gets or sets a boolean indicating if the control can be focusable */
     /** Gets or sets a boolean indicating if the control can be focusable */
     public isFocusInvisible = false;
     public isFocusInvisible = false;
 
 
+    /** Gets or sets a boolean indicating if the children are clipped to the current control bounds */
+    public clipChildren = true;
+
     /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
     /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
     public shadowOffsetX = 0;
     public shadowOffsetX = 0;
     /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
     /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
@@ -1040,8 +1043,10 @@ export class Control {
         }
         }
 
 
         // Clip
         // Clip
-        this._clip(context);
-        context.clip();
+        if (this.clipChildren) {
+            this._clip(context);
+            context.clip();
+        }
 
 
         return true;
         return true;
     }
     }

+ 8 - 2
gui/src/2D/controls/stackPanel.ts

@@ -26,7 +26,10 @@ export class StackPanel extends Container {
         this._markAsDirty();
         this._markAsDirty();
     }
     }
 
 
-    /** Gets or sets panel width */
+    /**
+     * Gets or sets panel width.
+     * This value should not be set when in horizontal mode as it will be computed automatically
+     */
     public set width(value: string | number) {
     public set width(value: string | number) {
         if (!this._doNotTrackManualChanges) {
         if (!this._doNotTrackManualChanges) {
             this._manualWidth = true;
             this._manualWidth = true;
@@ -45,7 +48,10 @@ export class StackPanel extends Container {
         return this._width.toString(this._host);
         return this._width.toString(this._host);
     }
     }
 
 
-    /** Gets or sets panel height */
+    /**
+     * Gets or sets panel height.
+     * This value should not be set when in vertical mode as it will be computed automatically
+     */
     public set height(value: string | number) {
     public set height(value: string | number) {
         if (!this._doNotTrackManualChanges) {
         if (!this._doNotTrackManualChanges) {
             this._manualHeight = true;
             this._manualHeight = true;

+ 1 - 1
inspector/src/tools/FullscreenTool.ts

@@ -14,7 +14,7 @@ export class FullscreenTool extends AbstractTool {
 
 
         function requestFullScreen(element: HTMLElement) {
         function requestFullScreen(element: HTMLElement) {
             // Supports most browsers and their versions.
             // Supports most browsers and their versions.
-            var requestMethod = element.requestFullscreen || element.webkitRequestFullScreen;
+            var requestMethod = element.requestFullscreen || (<any>element).webkitRequestFullScreen;
             requestMethod.call(element);
             requestMethod.call(element);
         }
         }
 
 

+ 1 - 1
package.json

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

+ 4 - 3
src/Cameras/Inputs/babylon.arcRotateCameraMouseWheelInput.ts

@@ -48,12 +48,13 @@ module BABYLON {
                     } else {
                     } else {
                         delta = event.wheelDelta / (this.wheelPrecision * 40);
                         delta = event.wheelDelta / (this.wheelPrecision * 40);
                     }
                     }
-                } else if (event.detail) {
-                    delta = -event.detail / this.wheelPrecision;
+                } else {
+                    let deltaValue = event.deltaY || event.detail;
+                    delta = -deltaValue / this.wheelPrecision;
                 }
                 }
 
 
                 if (delta) {
                 if (delta) {
-                    this.camera.inertialRadiusOffset += delta;
+                    this.camera.inertialRadiusOffset += delta;
                 }
                 }
 
 
                 if (event.preventDefault) {
                 if (event.preventDefault) {

+ 1 - 1
src/Cameras/Inputs/babylon.freeCameraDeviceOrientationInput.ts

@@ -53,7 +53,7 @@ module BABYLON {
         }
         }
 
 
         private _orientationChanged = () => {
         private _orientationChanged = () => {
-            this._screenOrientationAngle = (window.orientation !== undefined ? +window.orientation : (window.screen.orientation && (<any>window.screen.orientation)['angle'] ? (<any>window.screen.orientation).angle : 0));
+            this._screenOrientationAngle = (<any>window.orientation !== undefined ? +<any>window.orientation : ((<any>window.screen).orientation && ((<any>window.screen).orientation)['angle'] ? ((<any>window.screen).orientation).angle : 0));
             this._screenOrientationAngle = -Tools.ToRadians(this._screenOrientationAngle / 2);
             this._screenOrientationAngle = -Tools.ToRadians(this._screenOrientationAngle / 2);
             this._screenQuaternion.copyFromFloats(0, Math.sin(this._screenOrientationAngle), 0, Math.cos(this._screenOrientationAngle));
             this._screenQuaternion.copyFromFloats(0, Math.sin(this._screenOrientationAngle), 0, Math.cos(this._screenOrientationAngle));
         }
         }

+ 4 - 4
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -780,8 +780,8 @@ module BABYLON {
         }
         }
 
 
         private _onFullscreenChange = () => {
         private _onFullscreenChange = () => {
-            if (document.fullscreen !== undefined) {
-                this._fullscreenVRpresenting = document.fullscreen;
+            if ((<any>document).fullscreen !== undefined) {
+                this._fullscreenVRpresenting = (<any>document).fullscreen;
             } else if (document.mozFullScreen !== undefined) {
             } else if (document.mozFullScreen !== undefined) {
                 this._fullscreenVRpresenting = document.mozFullScreen;
                 this._fullscreenVRpresenting = document.mozFullScreen;
             } else if (document.webkitIsFullScreen !== undefined) {
             } else if (document.webkitIsFullScreen !== undefined) {
@@ -814,7 +814,7 @@ module BABYLON {
                 this._webVRpresenting = vrDisplay.isPresenting;
                 this._webVRpresenting = vrDisplay.isPresenting;
 
 
                 if (wasPresenting && !this._webVRpresenting) {
                 if (wasPresenting && !this._webVRpresenting) {
-                    this.exitVR();
+                    this.exitVR();
                 }
                 }
             } else {
             } else {
                 Tools.Warn('Detected VRDisplayPresentChange on an unknown VRDisplay. Did you can enterVR on the vrExperienceHelper?');
                 Tools.Warn('Detected VRDisplayPresentChange on an unknown VRDisplay. Did you can enterVR on the vrExperienceHelper?');
@@ -880,7 +880,7 @@ module BABYLON {
             }
             }
 
 
             if (this._webVRrequesting) {
             if (this._webVRrequesting) {
-                return;
+                return;
             }
             }
 
 
             // If WebVR is supported and a headset is connected
             // If WebVR is supported and a headset is connected

+ 100 - 0
src/Cameras/XR/babylon.webXRCamera.ts

@@ -0,0 +1,100 @@
+module BABYLON {
+    /**
+     * WebXR Camera which holds the views for the xrSession
+     * @see https://doc.babylonjs.com/how_to/webxr
+     */
+    export class WebXRCamera extends FreeCamera {
+        private static _TmpMatrix = new BABYLON.Matrix();
+
+        /**
+         * Creates a new webXRCamera, this should only be set at the camera after it has been updated by the xrSessionManager
+         * @param name the name of the camera
+         * @param scene the scene to add the camera to
+         */
+        constructor(name: string, scene: BABYLON.Scene) {
+            super(name, BABYLON.Vector3.Zero(), scene);
+
+            // Initial camera configuration
+            this.minZ = 0;
+            this.rotationQuaternion = new BABYLON.Quaternion();
+            this.cameraRigMode = BABYLON.Camera.RIG_MODE_CUSTOM;
+            this._updateNumberOfRigCameras(1);
+        }
+
+        private _updateNumberOfRigCameras(viewCount = 1) {
+            while (this.rigCameras.length < viewCount) {
+                var newCamera = new BABYLON.TargetCamera("view: " + this.rigCameras.length, BABYLON.Vector3.Zero(), this.getScene());
+                newCamera.minZ = 0;
+                newCamera.parent = this;
+                this.rigCameras.push(newCamera);
+            }
+            while (this.rigCameras.length > viewCount) {
+                var removedCamera = this.rigCameras.pop();
+                if (removedCamera) {
+                    removedCamera.dispose();
+                }
+            }
+        }
+
+        /** @hidden */
+        public _updateForDualEyeDebugging(pupilDistance = 0.01) {
+            // Create initial camera rigs
+            this._updateNumberOfRigCameras(2);
+            this.rigCameras[0].viewport = new BABYLON.Viewport(0, 0, 0.5, 1.0);
+            this.rigCameras[0].position.x = -pupilDistance / 2;
+            this.rigCameras[0].outputRenderTarget = null;
+            this.rigCameras[1].viewport = new BABYLON.Viewport(0.5, 0, 0.5, 1.0);
+            this.rigCameras[1].position.x = pupilDistance / 2;
+            this.rigCameras[1].outputRenderTarget = null;
+        }
+
+        /**
+         * Updates the cameras position from the current pose information of the  XR session
+         * @param xrSessionManager the session containing pose information
+         */
+        public updateFromXRSessionManager(xrSessionManager: WebXRSessionManager) {
+            // Ensure all frame data is available
+            if (!xrSessionManager._currentXRFrame || !xrSessionManager._currentXRFrame.getDevicePose) {
+                return;
+            }
+            var pose = xrSessionManager._currentXRFrame.getDevicePose(xrSessionManager._frameOfReference);
+            if (!pose || !pose.poseModelMatrix) {
+                return;
+            }
+
+            // Update the parent cameras matrix
+            BABYLON.Matrix.FromFloat32ArrayToRefScaled(pose.poseModelMatrix, 0, 1, WebXRCamera._TmpMatrix);
+            if (!this._scene.useRightHandedSystem) {
+                WebXRCamera._TmpMatrix.toggleModelMatrixHandInPlace();
+            }
+            WebXRCamera._TmpMatrix.getTranslationToRef(this.position);
+            WebXRCamera._TmpMatrix.getRotationMatrixToRef(WebXRCamera._TmpMatrix);
+            BABYLON.Quaternion.FromRotationMatrixToRef(WebXRCamera._TmpMatrix, this.rotationQuaternion);
+            this.computeWorldMatrix();
+
+            // Update camera rigs
+            this._updateNumberOfRigCameras(xrSessionManager._currentXRFrame.views.length);
+            xrSessionManager._currentXRFrame.views.forEach((view, i) => {
+                // Update view/projection matrix
+                BABYLON.Matrix.FromFloat32ArrayToRefScaled(pose.getViewMatrix(view), 0, 1, this.rigCameras[i]._computedViewMatrix);
+                BABYLON.Matrix.FromFloat32ArrayToRefScaled(view.projectionMatrix, 0, 1, this.rigCameras[i]._projectionMatrix);
+                if (!this._scene.useRightHandedSystem) {
+                    this.rigCameras[i]._computedViewMatrix.toggleModelMatrixHandInPlace();
+                    this.rigCameras[i]._projectionMatrix.toggleProjectionMatrixHandInPlace();
+                }
+
+                // Update viewport
+                var viewport  = xrSessionManager._xrSession.baseLayer.getViewport(view);
+                var width = xrSessionManager._xrSession.baseLayer.framebufferWidth;
+                var height = xrSessionManager._xrSession.baseLayer.framebufferHeight;
+                this.rigCameras[i].viewport.width = viewport.width / width;
+                this.rigCameras[i].viewport.height = viewport.height / height;
+                this.rigCameras[i].viewport.x = viewport.x / width;
+                this.rigCameras[i].viewport.y = viewport.y / height;
+
+                // Set cameras to render to the session's render target
+                this.rigCameras[i].outputRenderTarget = xrSessionManager._sessionRenderTargetTexture;
+            });
+        }
+    }
+}

+ 143 - 0
src/Cameras/XR/babylon.webXRSessionManager.ts

@@ -0,0 +1,143 @@
+module BABYLON {
+    /**
+     * Manages an XRSession
+     * @see https://doc.babylonjs.com/how_to/webxr
+     */
+    export class WebXRSessionManager {
+        private _xrNavigator: any;
+        private _xrDevice: XRDevice;
+        private _tmpMatrix = new BABYLON.Matrix();
+        /** @hidden */
+        public _xrSession: XRSession;
+        /** @hidden */
+        public _frameOfReference: XRFrameOfReference;
+        /** @hidden */
+        public _sessionRenderTargetTexture: RenderTargetTexture;
+        /** @hidden */
+        public _currentXRFrame: XRFrame;
+
+        /**
+         * Constructs a WebXRSessionManager, this must be initialized within a user action before usage
+         * @param scene The scene which the session should be created for
+         */
+        constructor(private scene: BABYLON.Scene) {
+
+        }
+
+        /**
+         * Initializes the manager, this must be done with a user action (eg. button click event)
+         * After initialization enterXR can be called to start an XR session
+         * @returns Promise which resolves after it is initialized
+         */
+        public initialize(): Promise<void> {
+             // Check if the browser supports webXR
+            this._xrNavigator = navigator;
+            if (!this._xrNavigator.xr) {
+                return Promise.reject("webXR not supported by this browser");
+            }
+             // Request the webXR device
+            return this._xrNavigator.xr.requestDevice().then((device: XRDevice) => {
+                this._xrDevice = device;
+                return (<any>this.scene.getEngine()._gl).setCompatibleXRDevice(this._xrDevice);
+            });
+        }
+
+        /**
+         * Enters XR with the desired XR session options
+         * @param sessionCreationOptions xr options to create the session with
+         * @param frameOfReferenceType option to configure how the xr pose is expressed
+         * @returns Promise which resolves after it enters XR
+         */
+        public enterXR(sessionCreationOptions: XRSessionCreationOptions, frameOfReferenceType: XRFrameOfReferenceType): Promise<void> {
+            // initialize session
+            return this._xrDevice.requestSession(sessionCreationOptions).then((session: XRSession) => {
+                this._xrSession = session;
+                this._xrSession.baseLayer = new XRWebGLLayer(this._xrSession, this.scene.getEngine()._gl);
+                return this._xrSession.requestFrameOfReference(frameOfReferenceType);
+            }).then((frameOfRef: any) => {
+                this._frameOfReference = frameOfRef;
+                // Tell the engine's render loop to be driven by the xr session's refresh rate and provide xr pose information
+                this.scene.getEngine().customAnimationFrameRequester = {
+                    requestAnimationFrame: this._xrSession.requestAnimationFrame.bind(this._xrSession),
+                    renderFunction: (timestamp: number, xrFrame: XRFrame) => {
+                        // Store the XR frame in the manager to be consumed by the XR camera to update pose
+                        this._currentXRFrame = xrFrame;
+                        this.scene.getEngine()._renderLoop();
+                    }
+                };
+                // Create render target texture from xr's webgl render target
+                this._sessionRenderTargetTexture = WebXRSessionManager._CreateRenderTargetTextureFromSession(this._xrSession, this.scene);
+            });
+        }
+
+        /**
+         * Stops the xrSession and restores the renderloop
+         * @returns Promise which resolves after it exits XR
+         */
+        public exitXR() {
+            return new Promise((res) => {
+                this.scene.getEngine().customAnimationFrameRequester = null;
+                this._xrSession.end();
+                // Restore frame buffer to avoid clear on xr framebuffer after session end
+                this.scene.getEngine().restoreDefaultFramebuffer();
+                // Need to restart render loop as after calling session.end the last request for new frame will never call callback
+                this.scene.getEngine()._renderLoop();
+                res();
+            });
+        }
+
+        /**
+         * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
+         * @param ray ray to cast into the environment
+         * @returns Promise which resolves with a collision point in the environment if it exists
+         */
+        public environmentPointHitTest(ray: BABYLON.Ray): Promise<Nullable<Vector3>> {
+            return new Promise((res, rej) => {
+                // Compute left handed inputs to request hit test
+                var origin = new Float32Array([ray.origin.x, ray.origin.y, ray.origin.z]);
+                var direction = new Float32Array([ray.direction.x, ray.direction.y, ray.direction.z]);
+                if (!this.scene.useRightHandedSystem) {
+                    origin[2] *= -1;
+                    direction[2] *= -1;
+                }
+
+                // Fire hittest
+                this._xrSession.requestHitTest(origin, direction, this._frameOfReference)
+                .then((hits: any) => {
+                    if (hits.length > 0) {
+                        BABYLON.Matrix.FromFloat32ArrayToRefScaled(hits[0].hitMatrix, 0, 1.0, this._tmpMatrix);
+                        var hitPoint = this._tmpMatrix.getTranslation();
+                        if (!this.scene.useRightHandedSystem) {
+                            hitPoint.z *= -1;
+                        }
+                        res(hitPoint);
+                    }else {
+                        res(null);
+                    }
+                }).catch((e: Error) => {
+                    res(null);
+                });
+            });
+        }
+
+        /**
+         * @hidden
+         * Converts the render layer of xrSession to a render target
+         * @param session session to create render target for
+         * @param scene scene the new render target should be created for
+         */
+        public static _CreateRenderTargetTextureFromSession(session: XRSession, scene: BABYLON.Scene) {
+            // Create internal texture
+            var internalTexture = new BABYLON.InternalTexture(scene.getEngine(), BABYLON.InternalTexture.DATASOURCE_UNKNOWN, true);
+            internalTexture.width = session.baseLayer.framebufferWidth;
+            internalTexture.height = session.baseLayer.framebufferHeight;
+            internalTexture._framebuffer = session.baseLayer.framebuffer;
+
+             // Create render target texture from the internal texture
+            var renderTargetTexture = new BABYLON.RenderTargetTexture("XR renderTargetTexture", {width: internalTexture.width, height: internalTexture.height}, scene, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
+            renderTargetTexture._texture = internalTexture;
+
+             return renderTargetTexture;
+        }
+    }
+}

+ 12 - 3
src/Cameras/babylon.camera.ts

@@ -56,6 +56,10 @@ module BABYLON {
          * Defines that both eyes of the camera should be renderered in a VR mode (webVR).
          * Defines that both eyes of the camera should be renderered in a VR mode (webVR).
          */
          */
         public static readonly RIG_MODE_WEBVR = 21;
         public static readonly RIG_MODE_WEBVR = 21;
+        /**
+         * Custom rig mode allowing rig cameras to be populated manually with any number of cameras
+         */
+        public static readonly RIG_MODE_CUSTOM = 22;
 
 
         /**
         /**
          * Defines if by default attaching controls should prevent the default javascript event to continue.
          * Defines if by default attaching controls should prevent the default javascript event to continue.
@@ -195,11 +199,15 @@ module BABYLON {
         public isStereoscopicSideBySide: boolean;
         public isStereoscopicSideBySide: boolean;
 
 
         /**
         /**
-         * Defines the list of custom render target the camera should render to.
+         * Defines the list of custom render target which are rendered to and then used as the input to this camera's render. Eg. display another camera view on a TV in the main scene
          * This is pretty helpfull if you wish to make a camera render to a texture you could reuse somewhere
          * This is pretty helpfull if you wish to make a camera render to a texture you could reuse somewhere
          * else in the scene.
          * else in the scene.
          */
          */
         public customRenderTargets = new Array<RenderTargetTexture>();
         public customRenderTargets = new Array<RenderTargetTexture>();
+        /**
+         * When set, the camera will render to this render target instead of the default canvas
+         */
+        public outputRenderTarget: Nullable<RenderTargetTexture> = null;
 
 
         /**
         /**
          * Observable triggered when the camera view matrix has changed.
          * Observable triggered when the camera view matrix has changed.
@@ -242,7 +250,8 @@ module BABYLON {
 
 
         protected _globalPosition = Vector3.Zero();
         protected _globalPosition = Vector3.Zero();
 
 
-        private _computedViewMatrix = Matrix.Identity();
+        /** hidden */
+        public _computedViewMatrix = Matrix.Identity();
         private _doNotComputeProjectionMatrix = false;
         private _doNotComputeProjectionMatrix = false;
         private _transformMatrix = Matrix.Zero();
         private _transformMatrix = Matrix.Zero();
         private _frustumPlanes: Plane[];
         private _frustumPlanes: Plane[];
@@ -415,7 +424,7 @@ module BABYLON {
         /** @hidden */
         /** @hidden */
         public _isSynchronizedViewMatrix(): boolean {
         public _isSynchronizedViewMatrix(): boolean {
             if (!super._isSynchronized()) {
             if (!super._isSynchronized()) {
-                return false;
+                return false;
             }
             }
 
 
             return this._cache.position.equals(this.position)
             return this._cache.position.equals(this.position)

+ 4 - 3
src/Culling/babylon.boundingBox.ts

@@ -49,6 +49,7 @@
         public maximum: Vector3 = Vector3.Zero();
         public maximum: Vector3 = Vector3.Zero();
 
 
         private _worldMatrix: Matrix;
         private _worldMatrix: Matrix;
+        private static TmpVector3 = Tools.BuildArray(3, Vector3.Zero);
 
 
         /**
         /**
          * @hidden
          * @hidden
@@ -89,8 +90,8 @@
             vectors[7].copyFromFloats(maxX, minY, maxZ);
             vectors[7].copyFromFloats(maxX, minY, maxZ);
 
 
             // OBB
             // OBB
-            this.maximum.addToRef(min, this.center).scaleInPlace(0.5);
-            this.maximum.subtractToRef(max, this.extendSize).scaleInPlace(0.5);
+            max.addToRef(min, this.center).scaleInPlace(0.5);
+            max.subtractToRef(min, this.extendSize).scaleInPlace(0.5);
 
 
             this._update(worldMatrix || this._worldMatrix || Matrix.Identity());
             this._update(worldMatrix || this._worldMatrix || Matrix.Identity());
         }
         }
@@ -101,7 +102,7 @@
          * @returns the current bounding box
          * @returns the current bounding box
          */
          */
         public scale(factor: number): BoundingBox {
         public scale(factor: number): BoundingBox {
-            const tmpVectors = Tmp.Vector3;
+            const tmpVectors = BoundingBox.TmpVector3;
             const diff = this.maximum.subtractToRef(this.minimum, tmpVectors[0]);
             const diff = this.maximum.subtractToRef(this.minimum, tmpVectors[0]);
             const len = diff.length();
             const len = diff.length();
             diff.normalizeFromLength(len);
             diff.normalizeFromLength(len);

+ 14 - 20
src/Culling/babylon.boundingSphere.ts

@@ -31,6 +31,8 @@ module BABYLON {
          */
          */
         public maximum = Vector3.Zero();
         public maximum = Vector3.Zero();
 
 
+        private static TmpVector3 = Tools.BuildArray(3, Vector3.Zero);
+
         /**
         /**
          * Creates a new bounding sphere
          * Creates a new bounding sphere
          * @param min defines the minimum vector (in local space)
          * @param min defines the minimum vector (in local space)
@@ -53,7 +55,7 @@ module BABYLON {
 
 
             var distance = Vector3.Distance(min, max);
             var distance = Vector3.Distance(min, max);
 
 
-            Vector3.LerpToRef(min, max, 0.5, this.center);
+            max.addToRef(min, this.center).scaleInPlace(0.5);
             this.radius = distance * 0.5;
             this.radius = distance * 0.5;
 
 
             this._update(worldMatrix || _identityMatrix);
             this._update(worldMatrix || _identityMatrix);
@@ -65,11 +67,11 @@ module BABYLON {
          * @returns the current bounding box
          * @returns the current bounding box
          */
          */
         public scale(factor: number): BoundingSphere {
         public scale(factor: number): BoundingSphere {
-            let newRadius = this.radius * factor;
-            const tmpVectors = Tmp.Vector3;
-            const tempRadiusVector = tmpVectors[0].copyFromFloats(newRadius, newRadius, newRadius);
-            let min = this.center.subtractToRef(tempRadiusVector, tmpVectors[1]);
-            let max = this.center.addToRef(tempRadiusVector, tmpVectors[2]);
+            const newRadius = this.radius * factor;
+            const tmpVectors = BoundingSphere.TmpVector3;
+            const tempRadiusVector = tmpVectors[0].setAll(newRadius);
+            const min = this.center.subtractToRef(tempRadiusVector, tmpVectors[1]);
+            const max = this.center.addToRef(tempRadiusVector, tmpVectors[2]);
 
 
             this.reConstruct(min, max);
             this.reConstruct(min, max);
 
 
@@ -80,7 +82,7 @@ module BABYLON {
         /** @hidden */
         /** @hidden */
         public _update(world: Matrix): void {
         public _update(world: Matrix): void {
             Vector3.TransformCoordinatesToRef(this.center, world, this.centerWorld);
             Vector3.TransformCoordinatesToRef(this.center, world, this.centerWorld);
-            const tempVector = Tmp.Vector3[0];
+            const tempVector = BoundingSphere.TmpVector3[0];
             Vector3.TransformNormalFromFloatsToRef(1.0, 1.0, 1.0, world, tempVector);
             Vector3.TransformNormalFromFloatsToRef(1.0, 1.0, 1.0, world, tempVector);
             this.radiusWorld = Math.max(Math.abs(tempVector.x), Math.abs(tempVector.y), Math.abs(tempVector.z)) * this.radius;
             this.radiusWorld = Math.max(Math.abs(tempVector.x), Math.abs(tempVector.y), Math.abs(tempVector.z)) * this.radius;
         }
         }
@@ -106,13 +108,8 @@ module BABYLON {
          * @returns true if the point is inside the bounding sphere
          * @returns true if the point is inside the bounding sphere
          */
          */
         public intersectsPoint(point: Vector3): boolean {
         public intersectsPoint(point: Vector3): boolean {
-            var x = this.centerWorld.x - point.x;
-            var y = this.centerWorld.y - point.y;
-            var z = this.centerWorld.z - point.z;
-
-            var distance = Math.sqrt((x * x) + (y * y) + (z * z));
-
-            if (this.radiusWorld < distance) {
+            const squareDistance = Vector3.DistanceSquared(this.centerWorld, point);
+            if (this.radiusWorld * this.radiusWorld < squareDistance) {
                 return false;
                 return false;
             }
             }
 
 
@@ -127,13 +124,10 @@ module BABYLON {
          * @returns true if the speres intersect
          * @returns true if the speres intersect
          */
          */
         public static Intersects(sphere0: BoundingSphere, sphere1: BoundingSphere): boolean {
         public static Intersects(sphere0: BoundingSphere, sphere1: BoundingSphere): boolean {
-            var x = sphere0.centerWorld.x - sphere1.centerWorld.x;
-            var y = sphere0.centerWorld.y - sphere1.centerWorld.y;
-            var z = sphere0.centerWorld.z - sphere1.centerWorld.z;
-
-            var distance = Math.sqrt((x * x) + (y * y) + (z * z));
+            const squareDistance = Vector3.DistanceSquared(sphere0.centerWorld, sphere1.centerWorld);
+            const radiusSum = sphere0.radiusWorld + sphere1.radiusWorld;
 
 
-            if (sphere0.radiusWorld + sphere1.radiusWorld < distance) {
+            if (radiusSum * radiusSum < squareDistance) {
                 return false;
                 return false;
             }
             }
 
 

+ 29 - 8
src/Engine/babylon.engine.ts

@@ -481,7 +481,20 @@ module BABYLON {
          * Returns the current version of the framework
          * Returns the current version of the framework
          */
          */
         public static get Version(): string {
         public static get Version(): string {
-            return "4.0.0-alpha.0";
+            return "4.0.0-alpha.2";
+        }
+
+        /**
+         * Returns a string describing the current engine
+         */
+        public get description(): string {
+            let description = "WebGL" + this.webGLVersion;
+
+            if (this._caps.parallelShaderCompile) {
+                description += " - Parallel shader compilation";
+            }
+
+            return description;
         }
         }
 
 
         // Updatable statics so stick with vars here
         // Updatable statics so stick with vars here
@@ -625,6 +638,11 @@ module BABYLON {
         public onBeginFrameObservable = new Observable<Engine>();
         public onBeginFrameObservable = new Observable<Engine>();
 
 
         /**
         /**
+         * If set, will be used to request the next animation frame for the render loop
+         */
+        public customAnimationFrameRequester: Nullable<ICustomAnimationFrameRequester> = null;
+
+        /**
          * Observable raised when the engine ends the current frame
          * Observable raised when the engine ends the current frame
          */
          */
         public onEndFrameObservable = new Observable<Engine>();
         public onEndFrameObservable = new Observable<Engine>();
@@ -1146,8 +1164,8 @@ module BABYLON {
             if (canvas) {
             if (canvas) {
                 // Fullscreen
                 // Fullscreen
                 this._onFullscreenChange = () => {
                 this._onFullscreenChange = () => {
-                    if (document.fullscreen !== undefined) {
-                        this.isFullscreen = document.fullscreen;
+                    if ((<any>document).fullscreen !== undefined) {
+                        this.isFullscreen = (<any>document).fullscreen;
                     } else if (document.mozFullScreen !== undefined) {
                     } else if (document.mozFullScreen !== undefined) {
                         this.isFullscreen = document.mozFullScreen;
                         this.isFullscreen = document.mozFullScreen;
                     } else if (document.webkitIsFullScreen !== undefined) {
                     } else if (document.webkitIsFullScreen !== undefined) {
@@ -1225,7 +1243,7 @@ module BABYLON {
             // Detect if we are running on a faulty buggy desktop OS.
             // Detect if we are running on a faulty buggy desktop OS.
             this._badDesktopOS = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
             this._badDesktopOS = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
 
 
-            console.log("Babylon.js engine (v" + Engine.Version + ") launched");
+            console.log(`Babylon.js v${Engine.Version} - ${this.description}`);
 
 
             this.enableOfflineSupport = Engine.OfflineProviderFactory !== undefined;
             this.enableOfflineSupport = Engine.OfflineProviderFactory !== undefined;
         }
         }
@@ -1894,11 +1912,14 @@ module BABYLON {
 
 
             if (this._activeRenderLoops.length > 0) {
             if (this._activeRenderLoops.length > 0) {
                 // Register new frame
                 // Register new frame
-                var requester = null;
-                if (this._vrDisplay && this._vrDisplay.isPresenting) {
-                    requester = this._vrDisplay;
+                if (this.customAnimationFrameRequester) {
+                    this.customAnimationFrameRequester.requestID = Tools.QueueNewFrame(this.customAnimationFrameRequester.renderFunction || this._bindedRenderFunction, this.customAnimationFrameRequester);
+                    this._frameHandler = this.customAnimationFrameRequester.requestID;
+                } else if (this._vrDisplay && this._vrDisplay.isPresenting) {
+                    this._frameHandler = Tools.QueueNewFrame(this._bindedRenderFunction, this._vrDisplay);
+                } else {
+                    this._frameHandler = Tools.QueueNewFrame(this._bindedRenderFunction);
                 }
                 }
-                this._frameHandler = Tools.QueueNewFrame(this._bindedRenderFunction, requester);
             } else {
             } else {
                 this._renderingQueueLaunched = false;
                 this._renderingQueueLaunched = false;
             }
             }

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

@@ -116,7 +116,7 @@ module BABYLON {
             this._caps.vertexArrayObject = false;
             this._caps.vertexArrayObject = false;
             this._caps.instancedArrays = false;
             this._caps.instancedArrays = false;
 
 
-            Tools.Log("Babylon.js null engine (v" + Engine.Version + ") launched");
+            Tools.Log(`Babylon.js v${Engine.Version} - Null engine`);
 
 
             // Wrappers
             // Wrappers
             if (typeof URL === "undefined") {
             if (typeof URL === "undefined") {

+ 18 - 11
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -861,13 +861,21 @@ module BABYLON {
             if (!engine.getCaps().standardDerivatives && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
             if (!engine.getCaps().standardDerivatives && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                 mesh.createNormals(true);
                 mesh.createNormals(true);
                 Tools.Warn("PBRMaterial: Normals have been created for the mesh: " + mesh.name);
                 Tools.Warn("PBRMaterial: Normals have been created for the mesh: " + mesh.name);
-                }
+            }
+
+            let previousEffect = subMesh.effect;
+            let effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances);
 
 
-            const effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances);
             if (effect) {
             if (effect) {
-                scene.resetCachedMaterial();
-                subMesh.setEffect(effect, defines);
-                this.buildUniformLayout();
+                // Use previous effect while new one is compiling
+                if (this.allowShaderHotSwapping && previousEffect && !effect.isReady()) {
+                    effect = previousEffect;
+                    defines.markAsUnprocessed();
+                } else {
+                    scene.resetCachedMaterial();
+                    subMesh.setEffect(effect, defines);
+                    this.buildUniformLayout();
+                }
             }
             }
 
 
             if (!subMesh.effect || !subMesh.effect.isReady()) {
             if (!subMesh.effect || !subMesh.effect.isReady()) {
@@ -1134,10 +1142,10 @@ module BABYLON {
                             case Texture.CUBIC_MODE:
                             case Texture.CUBIC_MODE:
                             case Texture.INVCUBIC_MODE:
                             case Texture.INVCUBIC_MODE:
                             default:
                             default:
-                                    defines.REFLECTIONMAP_CUBIC = true;
-                                    defines.USE_LOCAL_REFLECTIONMAP_CUBIC = (<any>reflectionTexture).boundingBoxSize ? true : false;
-                                    break;
-                            }
+                                defines.REFLECTIONMAP_CUBIC = true;
+                                defines.USE_LOCAL_REFLECTIONMAP_CUBIC = (<any>reflectionTexture).boundingBoxSize ? true : false;
+                                break;
+                        }
 
 
                         if (reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) {
                         if (reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) {
                             if (reflectionTexture.sphericalPolynomial) {
                             if (reflectionTexture.sphericalPolynomial) {
@@ -1424,8 +1432,7 @@ module BABYLON {
             this.bindOnlyWorldMatrix(world);
             this.bindOnlyWorldMatrix(world);
 
 
             // Normal Matrix
             // Normal Matrix
-            if (defines.OBJECTSPACE_NORMALMAP)
-            {
+            if (defines.OBJECTSPACE_NORMALMAP) {
                 world.toNormalMatrix(this._normalMatrix);
                 world.toNormalMatrix(this._normalMatrix);
                 this.bindOnlyNormalMatrix(this._normalMatrix);
                 this.bindOnlyNormalMatrix(this._normalMatrix);
             }
             }

+ 5 - 2
src/Materials/Textures/babylon.internalTexture.ts

@@ -236,12 +236,15 @@ module BABYLON {
          * Creates a new InternalTexture
          * Creates a new InternalTexture
          * @param engine defines the engine to use
          * @param engine defines the engine to use
          * @param dataSource defines the type of data that will be used
          * @param dataSource defines the type of data that will be used
+         * @param delayAllocation if the texture allocation should be delayed (default: false)
          */
          */
-        constructor(engine: Engine, dataSource: number) {
+        constructor(engine: Engine, dataSource: number, delayAllocation = false) {
             this._engine = engine;
             this._engine = engine;
             this._dataSource = dataSource;
             this._dataSource = dataSource;
 
 
-            this._webGLTexture = engine._createTexture();
+            if (!delayAllocation) {
+                this._webGLTexture = engine._createTexture();
+            }
         }
         }
 
 
         /**
         /**

+ 11 - 9
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -260,8 +260,9 @@ module BABYLON {
          * @param generateStencilBuffer True to generate a stencil buffer
          * @param generateStencilBuffer True to generate a stencil buffer
          * @param isMulti True if multiple textures need to be created (Draw Buffers)
          * @param isMulti True if multiple textures need to be created (Draw Buffers)
          * @param format The internal format of the buffer in the RTT (RED, RG, RGB, RGBA, ALPHA...)
          * @param format The internal format of the buffer in the RTT (RED, RG, RGB, RGBA, ALPHA...)
+         * @param delayAllocation if the texture allocation should be delayed (default: false)
          */
          */
-        constructor(name: string, size: number | { width: number, height: number } | { ratio: number }, scene: Nullable<Scene>, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Engine.TEXTURETYPE_UNSIGNED_INT, public isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, generateDepthBuffer = true, generateStencilBuffer = false, isMulti = false, format = Engine.TEXTUREFORMAT_RGBA) {
+        constructor(name: string, size: number | { width: number, height: number } | { ratio: number }, scene: Nullable<Scene>, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Engine.TEXTURETYPE_UNSIGNED_INT, public isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, generateDepthBuffer = true, generateStencilBuffer = false, isMulti = false, format = Engine.TEXTUREFORMAT_RGBA, delayAllocation = false) {
             super(null, scene, !generateMipMaps);
             super(null, scene, !generateMipMaps);
             scene = this.getScene();
             scene = this.getScene();
 
 
@@ -305,14 +306,15 @@ module BABYLON {
                 this.wrapV = Texture.CLAMP_ADDRESSMODE;
                 this.wrapV = Texture.CLAMP_ADDRESSMODE;
             }
             }
 
 
-            if (isCube) {
-                this._texture = scene.getEngine().createRenderTargetCubeTexture(this.getRenderSize(), this._renderTargetOptions);
-                this.coordinatesMode = Texture.INVCUBIC_MODE;
-                this._textureMatrix = Matrix.Identity();
-            } else {
-                this._texture = scene.getEngine().createRenderTargetTexture(this._size, this._renderTargetOptions);
+            if (!delayAllocation) {
+                if (isCube) {
+                    this._texture = scene.getEngine().createRenderTargetCubeTexture(this.getRenderSize(), this._renderTargetOptions);
+                    this.coordinatesMode = Texture.INVCUBIC_MODE;
+                    this._textureMatrix = Matrix.Identity();
+                } else {
+                    this._texture = scene.getEngine().createRenderTargetTexture(this._size, this._renderTargetOptions);
+                }
             }
             }
-
         }
         }
 
 
         /**
         /**
@@ -586,7 +588,7 @@ module BABYLON {
             // Is predicate defined?
             // Is predicate defined?
             if (this.renderListPredicate) {
             if (this.renderListPredicate) {
                 if (this.renderList) {
                 if (this.renderList) {
-                    this.renderList.splice(0); // Clear previous renderList
+                    this.renderList.length = 0; // Clear previous renderList
                 } else {
                 } else {
                     this.renderList = [];
                     this.renderList = [];
                 }
                 }

+ 71 - 45
src/Materials/babylon.material.ts

@@ -467,7 +467,7 @@ module BABYLON {
          * Gets a boolean indicating that current material needs to register RTT
          * Gets a boolean indicating that current material needs to register RTT
          */
          */
         public get hasRenderTargetTextures(): boolean {
         public get hasRenderTargetTextures(): boolean {
-          return false;
+            return false;
         }
         }
 
 
         /**
         /**
@@ -476,9 +476,9 @@ module BABYLON {
         public doNotSerialize = false;
         public doNotSerialize = false;
 
 
         /**
         /**
-         * Specifies if the effect should be stored on sub meshes
+         * @hidden
          */
          */
-        public storeEffectOnSubMeshes = false;
+        public _storeEffectOnSubMeshes = false;
 
 
         /**
         /**
          * Stores the animations for the material
          * Stores the animations for the material
@@ -765,6 +765,12 @@ module BABYLON {
          */
          */
         protected _uniformBuffer: UniformBuffer;
         protected _uniformBuffer: UniformBuffer;
 
 
+        /** @hidden */
+        public _indexInSceneMaterialArray = -1;
+
+        /** @hidden */
+        public meshMap: Nullable<{[id: string]: AbstractMesh | undefined}>;
+
         /**
         /**
          * Creates a material instance
          * Creates a material instance
          * @param name defines the name of the material
          * @param name defines the name of the material
@@ -788,8 +794,11 @@ module BABYLON {
             this._useUBO = this.getScene().getEngine().supportsUniformBuffers;
             this._useUBO = this.getScene().getEngine().supportsUniformBuffers;
 
 
             if (!doNotAdd) {
             if (!doNotAdd) {
-                this._scene.materials.push(this);
-                this._scene.onNewMaterialAddedObservable.notifyObservers(this);
+                this._scene.addMaterial(this);
+            }
+
+            if (this._scene.useMaterialMeshMap) {
+                this.meshMap = {};
             }
             }
         }
         }
 
 
@@ -1057,17 +1066,20 @@ module BABYLON {
          * @returns an array of meshes bound to the material
          * @returns an array of meshes bound to the material
          */
          */
         public getBindedMeshes(): AbstractMesh[] {
         public getBindedMeshes(): AbstractMesh[] {
-            var result = new Array<AbstractMesh>();
-
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-
-                if (mesh.material === this) {
-                    result.push(mesh);
+            if (this.meshMap) {
+                var result = new Array<AbstractMesh>();
+                for (let meshId in this.meshMap) {
+                    const mesh = this.meshMap[meshId];
+                    if (mesh) {
+                        result.push(mesh);
+                    }
                 }
                 }
+                return result;
+            }
+            else {
+                const meshes = this._scene.meshes;
+                return meshes.filter((mesh) => mesh.material === this);
             }
             }
-
-            return result;
         }
         }
 
 
         /**
         /**
@@ -1100,7 +1112,7 @@ module BABYLON {
                     scene.clipPlane = new Plane(0, 0, 0, 1);
                     scene.clipPlane = new Plane(0, 0, 0, 1);
                 }
                 }
 
 
-                if (this.storeEffectOnSubMeshes) {
+                if (this._storeEffectOnSubMeshes) {
                     if (this.isReadyForSubMesh(mesh, subMesh)) {
                     if (this.isReadyForSubMesh(mesh, subMesh)) {
                         if (onCompiled) {
                         if (onCompiled) {
                             onCompiled(this);
                             onCompiled(this);
@@ -1259,38 +1271,34 @@ module BABYLON {
          * Disposes the material
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
          */
-        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
+        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean, notBoundToMesh?: boolean): void {
+            const scene = this.getScene();
             // Animations
             // Animations
-            this.getScene().stopAnimation(this);
-            this.getScene().freeProcessedMaterials();
+            scene.stopAnimation(this);
+            scene.freeProcessedMaterials();
 
 
             // Remove from scene
             // Remove from scene
-            var index = this._scene.materials.indexOf(this);
-            if (index >= 0) {
-                this._scene.materials.splice(index, 1);
-            }
-            this._scene.onMaterialRemovedObservable.notifyObservers(this);
-
-            // Remove from meshes
-            for (index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-
-                if (mesh.material === this) {
-                    mesh.material = null;
-
-                    if ((<Mesh>mesh).geometry) {
-                        var geometry = <Geometry>((<Mesh>mesh).geometry);
-
-                        if (this.storeEffectOnSubMeshes) {
-                            for (var subMesh of mesh.subMeshes) {
-                                geometry._releaseVertexArrayObject(subMesh._materialEffect);
-                                if (forceDisposeEffect && subMesh._materialEffect) {
-                                    this._scene.getEngine()._releaseEffect(subMesh._materialEffect);
-                                }
-                            }
-                        } else {
-                            geometry._releaseVertexArrayObject(this._effect);
+            scene.removeMaterial(this);
+
+            if (notBoundToMesh !== true) {
+                // Remove from meshes
+                if (this.meshMap) {
+                    for (let meshId in this.meshMap) {
+                        const mesh = this.meshMap[meshId];
+                        if (mesh) {
+                            mesh.material = null; // will set the entry in the map to undefined
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
+                        }
+                    }
+                }
+                else {
+                    const meshes = scene.meshes;
+                    for (let mesh of meshes) {
+                        if (mesh.material === this) {
+                            mesh.material = null;
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -1300,8 +1308,8 @@ module BABYLON {
 
 
             // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
             // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
             if (forceDisposeEffect && this._effect) {
             if (forceDisposeEffect && this._effect) {
-                if (!this.storeEffectOnSubMeshes) {
-                    this._scene.getEngine()._releaseEffect(this._effect);
+                if (!this._storeEffectOnSubMeshes) {
+                    scene.getEngine()._releaseEffect(this._effect);
                 }
                 }
 
 
                 this._effect = null;
                 this._effect = null;
@@ -1320,6 +1328,24 @@ module BABYLON {
             }
             }
         }
         }
 
 
+        /** @hidden */
+        private  releaseVertexArrayObject(mesh: AbstractMesh, forceDisposeEffect?: boolean) {
+            if ((<Mesh>mesh).geometry) {
+                var geometry = <Geometry>((<Mesh>mesh).geometry);
+                const scene = this.getScene();
+                if (this._storeEffectOnSubMeshes) {
+                    for (var subMesh of mesh.subMeshes) {
+                        geometry._releaseVertexArrayObject(subMesh._materialEffect);
+                        if (forceDisposeEffect && subMesh._materialEffect) {
+                            scene.getEngine()._releaseEffect(subMesh._materialEffect);
+                        }
+                    }
+                } else {
+                    geometry._releaseVertexArrayObject(this._effect);
+                }
+            }
+        }
+
         /**
         /**
          * Serializes this material
          * Serializes this material
          * @returns the serialized material object
          * @returns the serialized material object

+ 2 - 2
src/Materials/babylon.multiMaterial.ts

@@ -35,7 +35,7 @@ module BABYLON {
 
 
             this.subMaterials = new Array<Material>();
             this.subMaterials = new Array<Material>();
 
 
-            this.storeEffectOnSubMeshes = true; // multimaterial is considered like a push material
+            this._storeEffectOnSubMeshes = true; // multimaterial is considered like a push material
         }
         }
 
 
         private _hookArray(array: Nullable<Material>[]): void {
         private _hookArray(array: Nullable<Material>[]): void {
@@ -105,7 +105,7 @@ module BABYLON {
             for (var index = 0; index < this.subMaterials.length; index++) {
             for (var index = 0; index < this.subMaterials.length; index++) {
                 var subMaterial = this.subMaterials[index];
                 var subMaterial = this.subMaterials[index];
                 if (subMaterial) {
                 if (subMaterial) {
-                    if (subMaterial.storeEffectOnSubMeshes) {
+                    if (subMaterial._storeEffectOnSubMeshes) {
                         if (!subMaterial.isReadyForSubMesh(mesh, subMesh, useInstances)) {
                         if (!subMaterial.isReadyForSubMesh(mesh, subMesh, useInstances)) {
                             return false;
                             return false;
                         }
                         }

+ 14 - 7
src/Materials/babylon.pushMaterial.ts

@@ -7,11 +7,18 @@ module BABYLON {
 
 
         protected _activeEffect: Effect;
         protected _activeEffect: Effect;
 
 
-        protected _normalMatrix : Matrix = new Matrix();
+        protected _normalMatrix: Matrix = new Matrix();
+
+        /**
+         * Gets or sets a boolean indicating that the material is allowed to do shader hot swapping.
+         * This means that the material can keep using a previous shader while a new one is being compiled.
+         * This is mostly used when shader parallel compilation is supported (true by default)
+         */
+        public allowShaderHotSwapping = true;
 
 
         constructor(name: string, scene: Scene) {
         constructor(name: string, scene: Scene) {
             super(name, scene);
             super(name, scene);
-            this.storeEffectOnSubMeshes = true;
+            this._storeEffectOnSubMeshes = true;
         }
         }
 
 
         public getEffect(): Effect {
         public getEffect(): Effect {
@@ -30,11 +37,11 @@ module BABYLON {
             return this.isReadyForSubMesh(mesh, mesh.subMeshes[0], useInstances);
             return this.isReadyForSubMesh(mesh, mesh.subMeshes[0], useInstances);
         }
         }
 
 
-         /**
-         * Binds the given world matrix to the active effect
-         *
-         * @param world the matrix to bind
-         */
+        /**
+        * Binds the given world matrix to the active effect
+        *
+        * @param world the matrix to bind
+        */
         public bindOnlyWorldMatrix(world: Matrix): void {
         public bindOnlyWorldMatrix(world: Matrix): void {
             this._activeEffect.setMatrix("world", world);
             this._activeEffect.setMatrix("world", world);
         }
         }

+ 3 - 2
src/Materials/babylon.shaderMaterial.ts

@@ -656,8 +656,9 @@ module BABYLON {
          * Disposes the material
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
          */
-        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
+        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean, notBoundToMesh?: boolean): void {
 
 
             if (forceDisposeTextures) {
             if (forceDisposeTextures) {
                 var name: string;
                 var name: string;
@@ -675,7 +676,7 @@ module BABYLON {
 
 
             this._textures = {};
             this._textures = {};
 
 
-            super.dispose(forceDisposeEffect, forceDisposeTextures);
+            super.dispose(forceDisposeEffect, forceDisposeTextures, notBoundToMesh);
         }
         }
 
 
         /**
         /**

+ 15 - 4
src/Materials/babylon.standardMaterial.ts

@@ -983,7 +983,6 @@ module BABYLON {
             // Get correct effect
             // Get correct effect
             if (defines.isDirty) {
             if (defines.isDirty) {
                 defines.markAsProcessed();
                 defines.markAsProcessed();
-                scene.resetCachedMaterial();
 
 
                 // Fallbacks
                 // Fallbacks
                 var fallbacks = new EffectFallbacks();
                 var fallbacks = new EffectFallbacks();
@@ -1106,7 +1105,9 @@ module BABYLON {
                 }
                 }
 
 
                 var join = defines.toString();
                 var join = defines.toString();
-                subMesh.setEffect(scene.getEngine().createEffect(shaderName, <EffectCreationOptions>{
+
+                let previousEffect = subMesh.effect;
+                let effect = scene.getEngine().createEffect(shaderName, <EffectCreationOptions>{
                     attributes: attribs,
                     attributes: attribs,
                     uniformsNames: uniforms,
                     uniformsNames: uniforms,
                     uniformBuffersNames: uniformBuffers,
                     uniformBuffersNames: uniformBuffers,
@@ -1116,9 +1117,19 @@ module BABYLON {
                     onCompiled: this.onCompiled,
                     onCompiled: this.onCompiled,
                     onError: this.onError,
                     onError: this.onError,
                     indexParameters: { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS }
                     indexParameters: { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS }
-                }, engine), defines);
+                }, engine);
 
 
-                this.buildUniformLayout();
+                if (effect) {
+                    // Use previous effect while new one is compiling
+                    if (this.allowShaderHotSwapping && previousEffect && !effect.isReady()) {
+                        effect = previousEffect;
+                        defines.markAsUnprocessed();
+                    } else {
+                        scene.resetCachedMaterial();
+                        subMesh.setEffect(effect, defines);
+                        this.buildUniformLayout();
+                    }
+                }
             }
             }
 
 
             if (!subMesh.effect || !subMesh.effect.isReady()) {
             if (!subMesh.effect || !subMesh.effect.isReady()) {

+ 4 - 2
src/Materials/babylon.uniformBuffer.ts

@@ -616,10 +616,12 @@ module BABYLON {
                 return;
                 return;
             }
             }
 
 
-            let index = this._engine._uniformBuffers.indexOf(this);
+            const uniformBuffers = this._engine._uniformBuffers;
+            let index = uniformBuffers.indexOf(this);
 
 
             if (index !== -1) {
             if (index !== -1) {
-                this._engine._uniformBuffers.splice(index, 1);
+                uniformBuffers[index] = uniformBuffers[uniformBuffers.length - 1];
+                uniformBuffers.pop();
             }
             }
 
 
             if (!this._buffer) {
             if (!this._buffer) {

+ 21 - 4
src/Math/babylon.math.ts

@@ -8,7 +8,7 @@ module BABYLON {
      * Constant used to convert a value to linear space
      * Constant used to convert a value to linear space
      * @ignorenaming
      * @ignorenaming
      */
      */
-     export const ToLinearSpace = 2.2;
+    export const ToLinearSpace = 2.2;
     /**
     /**
      * Constant used to define the minimal number value in Babylon.js
      * Constant used to define the minimal number value in Babylon.js
      * @ignorenaming
      * @ignorenaming
@@ -1978,7 +1978,7 @@ module BABYLON {
          * @param len the length of the vector
          * @param len the length of the vector
          * @returns the current updated Vector3
          * @returns the current updated Vector3
          */
          */
-        public normalizeFromLength(len : number): Vector3 {
+        public normalizeFromLength(len: number): Vector3 {
             if (len === 0 || len === 1.0) {
             if (len === 0 || len === 1.0) {
                 return this;
                 return this;
             }
             }
@@ -4880,9 +4880,26 @@ module BABYLON {
             return this;
             return this;
         }
         }
 
 
-        // Statics
+        /**
+         * Toggles model matrix from being right handed to left handed in place and vice versa
+         */
+        public toggleModelMatrixHandInPlace() {
+            [2, 6, 8, 9, 14].forEach((num) => {
+                this.m[num] *= -1;
+            });
+        }
 
 
         /**
         /**
+         * Toggles projection matrix from being right handed to left handed in place and vice versa
+         */
+        public toggleProjectionMatrixHandInPlace() {
+            [8, 9, 10, 11].forEach((num) => {
+                this.m[num] *= -1;
+            });
+        }
+
+        // Statics
+        /**
          * Creates a matrix from an array
          * Creates a matrix from an array
          * @param array defines the source array
          * @param array defines the source array
          * @param offset defines an offset in the source array
          * @param offset defines an offset in the source array
@@ -7175,7 +7192,7 @@ module BABYLON {
         public static Vector3: Vector3[] = Tools.BuildArray(13, Vector3.Zero); // 13 temp Vector3 at once should be enough
         public static Vector3: Vector3[] = Tools.BuildArray(13, Vector3.Zero); // 13 temp Vector3 at once should be enough
         public static Vector4: Vector4[] = Tools.BuildArray(3, Vector4.Zero); // 3 temp Vector4 at once should be enough
         public static Vector4: Vector4[] = Tools.BuildArray(3, Vector4.Zero); // 3 temp Vector4 at once should be enough
         public static Quaternion: Quaternion[] = Tools.BuildArray(2, Quaternion.Zero); // 2 temp Quaternion at once should be enough
         public static Quaternion: Quaternion[] = Tools.BuildArray(2, Quaternion.Zero); // 2 temp Quaternion at once should be enough
-        public static Matrix: Matrix[] = Tools.BuildArray(6, Matrix.Identity); // 6 temp Matrices at once should be enough
+        public static Matrix: Matrix[] = Tools.BuildArray(8, Matrix.Identity); // 8 temp Matrices at once should be enough
     }
     }
     /**
     /**
      * @hidden
      * @hidden

+ 13 - 4
src/Mesh/babylon.abstractMesh.ts

@@ -264,8 +264,17 @@ module BABYLON {
                 return;
                 return;
             }
             }
 
 
+            // remove from material mesh map id needed
+            if (this._material && this._material.meshMap) {
+                this._material.meshMap[this.uniqueId] = undefined;
+            }
+
             this._material = value;
             this._material = value;
 
 
+            if (value && value.meshMap) {
+                value.meshMap[this.uniqueId] = this;
+            }
+
             if (this.onMaterialChangedObservable.hasObservers) {
             if (this.onMaterialChangedObservable.hasObservers) {
                 this.onMaterialChangedObservable.notifyObservers(this);
                 this.onMaterialChangedObservable.notifyObservers(this);
             }
             }
@@ -1196,7 +1205,7 @@ module BABYLON {
         private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
         private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
             //TODO move this to the collision coordinator!
             //TODO move this to the collision coordinator!
             if (this.getScene().workerCollisions) {
             if (this.getScene().workerCollisions) {
-                newPosition.multiplyInPlace(this._collider._radius);
+                newPosition.multiplyInPlace(this._collider._radius);
             }
             }
 
 
             newPosition.subtractToRef(this._oldPositionForCollisions, this._diffPositionForCollisions);
             newPosition.subtractToRef(this._oldPositionForCollisions, this._diffPositionForCollisions);
@@ -1250,7 +1259,7 @@ module BABYLON {
 
 
                 // Bounding test
                 // Bounding test
                 if (len > 1 && !subMesh._checkCollision(collider)) {
                 if (len > 1 && !subMesh._checkCollision(collider)) {
-                    continue;
+                    continue;
                 }
                 }
 
 
                 this._collideForSubMesh(subMesh, transformMatrix, collider);
                 this._collideForSubMesh(subMesh, transformMatrix, collider);
@@ -1262,7 +1271,7 @@ module BABYLON {
         public _checkCollision(collider: Collider): AbstractMesh {
         public _checkCollision(collider: Collider): AbstractMesh {
             // Bounding box test
             // Bounding box test
             if (!this._boundingInfo || !this._boundingInfo._checkCollision(collider)) {
             if (!this._boundingInfo || !this._boundingInfo._checkCollision(collider)) {
-                return this;
+                return this;
             }
             }
 
 
             // Transformation matrix
             // Transformation matrix
@@ -1307,7 +1316,7 @@ module BABYLON {
 
 
                 // Bounding test
                 // Bounding test
                 if (len > 1 && !subMesh.canIntersects(ray)) {
                 if (len > 1 && !subMesh.canIntersects(ray)) {
-                    continue;
+                    continue;
                 }
                 }
 
 
                 var currentIntersectInfo = subMesh.intersects(ray, (<Vector3[]>this._positions), (<IndicesArray>this.getIndices()), fastCheck);
                 var currentIntersectInfo = subMesh.intersects(ray, (<Vector3[]>this._positions), (<IndicesArray>this.getIndices()), fastCheck);

+ 5 - 5
src/Mesh/babylon.instancedMesh.ts

@@ -7,10 +7,13 @@ module BABYLON {
         private _sourceMesh: Mesh;
         private _sourceMesh: Mesh;
         private _currentLOD: Mesh;
         private _currentLOD: Mesh;
 
 
+        /** @hidden */
+        public _indexInSourceMeshInstanceArray = -1;
+
         constructor(name: string, source: Mesh) {
         constructor(name: string, source: Mesh) {
             super(name, source.getScene());
             super(name, source.getScene());
 
 
-            source.instances.push(this);
+            source.addInstance(this);
 
 
             this._sourceMesh = source;
             this._sourceMesh = source;
 
 
@@ -317,11 +320,8 @@ module BABYLON {
          * Returns nothing.
          * Returns nothing.
          */
          */
         public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
         public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
-
             // Remove from mesh
             // Remove from mesh
-            var index = this._sourceMesh.instances.indexOf(this);
-            this._sourceMesh.instances.splice(index, 1);
-
+            this._sourceMesh.removeInstance(this);
             super.dispose(doNotRecurse, disposeMaterialAndTextures);
             super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
         }
     }
     }

+ 1 - 2
src/Mesh/babylon.linesMesh.ts

@@ -163,8 +163,7 @@ module BABYLON {
          * @param doNotRecurse If children should be disposed
          * @param doNotRecurse If children should be disposed
          */
          */
         public dispose(doNotRecurse?: boolean): void {
         public dispose(doNotRecurse?: boolean): void {
-            this._colorShader.dispose();
-
+            this._colorShader.dispose(false, false, true);
             super.dispose(doNotRecurse);
             super.dispose(doNotRecurse);
         }
         }
 
 

+ 61 - 11
src/Mesh/babylon.mesh.ts

@@ -139,6 +139,8 @@ module BABYLON {
 
 
         /**
         /**
          * Gets the list of instances created from this mesh
          * Gets the list of instances created from this mesh
+         * it is not supposed to be modified manually.
+         * Note also that the order of the InstancedMesh wihin the array is not significant and might change.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
          */
          */
         public instances = new Array<InstancedMesh>();
         public instances = new Array<InstancedMesh>();
@@ -213,6 +215,8 @@ module BABYLON {
 
 
         // Will be used to save a source mesh reference, If any
         // Will be used to save a source mesh reference, If any
         private _source: Nullable<Mesh> = null;
         private _source: Nullable<Mesh> = null;
+        // Will be used to for fast cloned mesh lookup
+        private meshMap: Nullable<{[id: string]: Mesh | undefined}>;
 
 
         /**
         /**
          * Gets the source mesh (the one used to clone this one from)
          * Gets the source mesh (the one used to clone this one from)
@@ -266,6 +270,12 @@ module BABYLON {
 
 
                 // Source mesh
                 // Source mesh
                 this._source = source;
                 this._source = source;
+                if (scene.useClonedMeshhMap) {
+                    if (!source.meshMap) {
+                        source.meshMap = {};
+                    }
+                    source.meshMap[this.uniqueId] = this;
+                }
 
 
                 // Construction Params
                 // Construction Params
                 // Clone parameters allowing mesh to be updated in case of parametric shapes.
                 // Clone parameters allowing mesh to be updated in case of parametric shapes.
@@ -759,11 +769,11 @@ module BABYLON {
 
 
             let mat = this.material || scene.defaultMaterial;
             let mat = this.material || scene.defaultMaterial;
             if (mat) {
             if (mat) {
-                if (mat.storeEffectOnSubMeshes) {
+                if (mat._storeEffectOnSubMeshes) {
                     for (var subMesh of this.subMeshes) {
                     for (var subMesh of this.subMeshes) {
                         let effectiveMaterial = subMesh.getMaterial();
                         let effectiveMaterial = subMesh.getMaterial();
                         if (effectiveMaterial) {
                         if (effectiveMaterial) {
-                            if (effectiveMaterial.storeEffectOnSubMeshes) {
+                            if (effectiveMaterial._storeEffectOnSubMeshes) {
                                 if (!effectiveMaterial.isReadyForSubMesh(this, subMesh, hardwareInstancedRendering)) {
                                 if (!effectiveMaterial.isReadyForSubMesh(this, subMesh, hardwareInstancedRendering)) {
                                     return false;
                                     return false;
                                 }
                                 }
@@ -1533,7 +1543,7 @@ module BABYLON {
 
 
             this._effectiveMaterial = material;
             this._effectiveMaterial = material;
 
 
-            if (this._effectiveMaterial.storeEffectOnSubMeshes) {
+            if (this._effectiveMaterial._storeEffectOnSubMeshes) {
                 if (!this._effectiveMaterial.isReadyForSubMesh(this, subMesh, hardwareInstancedRendering)) {
                 if (!this._effectiveMaterial.isReadyForSubMesh(this, subMesh, hardwareInstancedRendering)) {
                     return this;
                     return this;
                 }
                 }
@@ -1551,7 +1561,7 @@ module BABYLON {
             }
             }
 
 
             var effect: Nullable<Effect>;
             var effect: Nullable<Effect>;
-            if (this._effectiveMaterial.storeEffectOnSubMeshes) {
+            if (this._effectiveMaterial._storeEffectOnSubMeshes) {
                 effect = subMesh.effect;
                 effect = subMesh.effect;
             } else {
             } else {
                 effect = this._effectiveMaterial.getEffect();
                 effect = this._effectiveMaterial.getEffect();
@@ -1584,7 +1594,7 @@ module BABYLON {
 
 
             var world = this.getWorldMatrix();
             var world = this.getWorldMatrix();
 
 
-            if (this._effectiveMaterial.storeEffectOnSubMeshes) {
+            if (this._effectiveMaterial._storeEffectOnSubMeshes) {
                 this._effectiveMaterial.bindForSubMesh(world, this, subMesh);
                 this._effectiveMaterial.bindForSubMesh(world, this, subMesh);
             } else {
             } else {
                 this._effectiveMaterial.bind(world, this);
                 this._effectiveMaterial.bind(world, this);
@@ -2014,13 +2024,31 @@ module BABYLON {
             }
             }
 
 
             // Sources
             // Sources
-            var meshes = this.getScene().meshes;
-            meshes.forEach((abstractMesh: AbstractMesh) => {
-                let mesh = abstractMesh as Mesh;
-                if (mesh._source && mesh._source === this) {
-                    mesh._source = null;
+            if (this._scene.useClonedMeshhMap) {
+                if (this.meshMap) {
+                    for (const uniqueId in this.meshMap) {
+                        const mesh = this.meshMap[uniqueId];
+                        if (mesh) {
+                            mesh._source = null;
+                            this.meshMap[uniqueId] = undefined;
+                        }
+                    }
                 }
                 }
-            });
+
+                if (this._source && this._source.meshMap) {
+                    this._source.meshMap[this.uniqueId] = undefined;
+                }
+            }
+            else {
+                var meshes = this.getScene().meshes;
+                for (const abstractMesh of meshes) {
+                    let mesh = abstractMesh as Mesh;
+                    if (mesh._source && mesh._source === this) {
+                        mesh._source = null;
+                    }
+                }
+            }
+
             this._source = null;
             this._source = null;
 
 
             // Instances
             // Instances
@@ -3780,5 +3808,27 @@ module BABYLON {
 
 
             return meshSubclass;
             return meshSubclass;
         }
         }
+
+        /** @hidden */
+        public addInstance(instance: InstancedMesh) {
+            instance._indexInSourceMeshInstanceArray = this.instances.length;
+            this.instances.push(instance);
+        }
+
+        /** @hidden */
+        public removeInstance(instance: InstancedMesh) {
+            // Remove from mesh
+            const index = instance._indexInSourceMeshInstanceArray;
+            if (index != -1) {
+                if (index !== this.instances.length - 1) {
+                    const last = this.instances[this.instances.length - 1];
+                    this.instances[index] = last;
+                    last._indexInSourceMeshInstanceArray = index;
+                }
+
+                instance._indexInSourceMeshInstanceArray = -1;
+                this.instances.pop();
+            }
+        }
     }
     }
 }
 }

+ 58 - 65
src/Mesh/babylon.transformNode.ts

@@ -93,6 +93,9 @@ module BABYLON {
 
 
         protected _isWorldMatrixFrozen = false;
         protected _isWorldMatrixFrozen = false;
 
 
+        /** @hidden */
+        public _indexInSceneTransformNodesArray = -1;
+
         /**
         /**
         * An event triggered after the world matrix is updated
         * An event triggered after the world matrix is updated
         */
         */
@@ -162,8 +165,8 @@ module BABYLON {
         public set rotationQuaternion(quaternion: Nullable<Quaternion>) {
         public set rotationQuaternion(quaternion: Nullable<Quaternion>) {
             this._rotationQuaternion = quaternion;
             this._rotationQuaternion = quaternion;
             //reset the rotation vector.
             //reset the rotation vector.
-            if (quaternion && this.rotation.length()) {
-                this.rotation.copyFromFloats(0.0, 0.0, 0.0);
+            if (quaternion) {
+                this.rotation.setAll(0.0);
             }
             }
         }
         }
 
 
@@ -222,7 +225,7 @@ module BABYLON {
             }
             }
 
 
             if (this.billboardMode !== this._cache.billboardMode || this.billboardMode !== TransformNode.BILLBOARDMODE_NONE) {
             if (this.billboardMode !== this._cache.billboardMode || this.billboardMode !== TransformNode.BILLBOARDMODE_NONE) {
-                return false;
+                return false;
             }
             }
 
 
             if (this._cache.pivotMatrixUpdated) {
             if (this._cache.pivotMatrixUpdated) {
@@ -234,21 +237,21 @@ module BABYLON {
             }
             }
 
 
             if (!this._cache.position.equals(this._position)) {
             if (!this._cache.position.equals(this._position)) {
-                return false;
+                return false;
             }
             }
 
 
             if (this._rotationQuaternion) {
             if (this._rotationQuaternion) {
                 if (!this._cache.rotationQuaternion.equals(this._rotationQuaternion)) {
                 if (!this._cache.rotationQuaternion.equals(this._rotationQuaternion)) {
-                    return false;
+                    return false;
                 }
                 }
             }
             }
 
 
             if (!this._cache.rotation.equals(this._rotation)) {
             if (!this._cache.rotation.equals(this._rotation)) {
-                return false;
+                return false;
             }
             }
 
 
             if (!this._cache.scaling.equals(this._scaling)) {
             if (!this._cache.scaling.equals(this._scaling)) {
-                return false;
+                return false;
             }
             }
 
 
             return true;
             return true;
@@ -304,7 +307,7 @@ module BABYLON {
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
         public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = true): TransformNode {
         public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = true): TransformNode {
-            this._pivotMatrix = matrix.clone();
+            this._pivotMatrix.copyFrom(matrix);
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
 
 
@@ -391,10 +394,9 @@ module BABYLON {
                 absolutePositionZ = absolutePosition.z;
                 absolutePositionZ = absolutePosition.z;
             }
             }
             if (this.parent) {
             if (this.parent) {
-                var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
-                invertParentWorldMatrix.invert();
-                var worldPosition = new Vector3(absolutePositionX, absolutePositionY, absolutePositionZ);
-                this.position = Vector3.TransformCoordinates(worldPosition, invertParentWorldMatrix);
+                const invertParentWorldMatrix = Tmp.Matrix[0];
+                this.parent.getWorldMatrix().invertToRef(invertParentWorldMatrix);
+                Vector3.TransformCoordinatesFromFloatsToRef(absolutePositionX, absolutePositionY, absolutePositionZ, invertParentWorldMatrix, this.position);
             } else {
             } else {
                 this.position.x = absolutePositionX;
                 this.position.x = absolutePositionX;
                 this.position.y = absolutePositionY;
                 this.position.y = absolutePositionY;
@@ -420,9 +422,8 @@ module BABYLON {
          */
          */
         public getPositionExpressedInLocalSpace(): Vector3 {
         public getPositionExpressedInLocalSpace(): Vector3 {
             this.computeWorldMatrix();
             this.computeWorldMatrix();
-            var invLocalWorldMatrix = this._localWorld.clone();
-            invLocalWorldMatrix.invert();
-
+            const invLocalWorldMatrix = Tmp.Matrix[0];
+            this._localWorld.invertToRef(invLocalWorldMatrix);
             return Vector3.TransformNormal(this.position, invLocalWorldMatrix);
             return Vector3.TransformNormal(this.position, invLocalWorldMatrix);
         }
         }
 
 
@@ -571,34 +572,18 @@ module BABYLON {
             if (!node && !this.parent) {
             if (!node && !this.parent) {
                 return this;
                 return this;
             }
             }
-            if (!node) {
-                var rotation = Tmp.Quaternion[0];
-                var position = Tmp.Vector3[0];
-                var scale = Tmp.Vector3[1];
 
 
+            var quatRotation = Tmp.Quaternion[0];
+            var position = Tmp.Vector3[0];
+            var scale = Tmp.Vector3[1];
+
+            if (!node) {
                 if (this.parent && this.parent.computeWorldMatrix) {
                 if (this.parent && this.parent.computeWorldMatrix) {
                     this.parent.computeWorldMatrix(true);
                     this.parent.computeWorldMatrix(true);
                 }
                 }
                 this.computeWorldMatrix(true);
                 this.computeWorldMatrix(true);
-                this.getWorldMatrix().decompose(scale, rotation, position);
-
-                if (this.rotationQuaternion) {
-                    this.rotationQuaternion.copyFrom(rotation);
-                } else {
-                    rotation.toEulerAnglesToRef(this.rotation);
-                }
-
-                this.scaling.x = scale.x;
-                this.scaling.y = scale.y;
-                this.scaling.z = scale.z;
-
-                this.position.x = position.x;
-                this.position.y = position.y;
-                this.position.z = position.z;
+                this.getWorldMatrix().decompose(scale, quatRotation, position);
             } else {
             } else {
-                var rotation = Tmp.Quaternion[0];
-                var position = Tmp.Vector3[0];
-                var scale = Tmp.Vector3[1];
                 var diffMatrix = Tmp.Matrix[0];
                 var diffMatrix = Tmp.Matrix[0];
                 var invParentMatrix = Tmp.Matrix[1];
                 var invParentMatrix = Tmp.Matrix[1];
 
 
@@ -607,23 +592,18 @@ module BABYLON {
 
 
                 node.getWorldMatrix().invertToRef(invParentMatrix);
                 node.getWorldMatrix().invertToRef(invParentMatrix);
                 this.getWorldMatrix().multiplyToRef(invParentMatrix, diffMatrix);
                 this.getWorldMatrix().multiplyToRef(invParentMatrix, diffMatrix);
-                diffMatrix.decompose(scale, rotation, position);
-
-                if (this.rotationQuaternion) {
-                    this.rotationQuaternion.copyFrom(rotation);
-                } else {
-                    rotation.toEulerAnglesToRef(this.rotation);
-                }
-
-                this.position.x = position.x;
-                this.position.y = position.y;
-                this.position.z = position.z;
+                diffMatrix.decompose(scale, quatRotation, position);
+            }
 
 
-                this.scaling.x = scale.x;
-                this.scaling.y = scale.y;
-                this.scaling.z = scale.z;
+            if (this.rotationQuaternion) {
+                this.rotationQuaternion.copyFrom(quatRotation);
+            } else {
+                quatRotation.toEulerAnglesToRef(this.rotation);
             }
             }
 
 
+            this.scaling.copyFrom(scale);
+            this.position.copyFrom(position);
+
             this.parent = node;
             this.parent = node;
             return this;
             return this;
         }
         }
@@ -693,8 +673,8 @@ module BABYLON {
         public rotate(axis: Vector3, amount: number, space?: Space): TransformNode {
         public rotate(axis: Vector3, amount: number, space?: Space): TransformNode {
             axis.normalize();
             axis.normalize();
             if (!this.rotationQuaternion) {
             if (!this.rotationQuaternion) {
-                this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
-                this.rotation = Vector3.Zero();
+                this.rotationQuaternion = this.rotation.toQuaternion();
+                this.rotation.setAll(0);
             }
             }
             var rotationQuaternion: Quaternion;
             var rotationQuaternion: Quaternion;
             if (!space || (space as any) === Space.LOCAL) {
             if (!space || (space as any) === Space.LOCAL) {
@@ -703,8 +683,8 @@ module BABYLON {
             }
             }
             else {
             else {
                 if (this.parent) {
                 if (this.parent) {
-                    var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
-                    invertParentWorldMatrix.invert();
+                    const invertParentWorldMatrix = Tmp.Matrix[0];
+                    this.parent.getWorldMatrix().invertToRef(invertParentWorldMatrix);
                     axis = Vector3.TransformNormal(axis, invertParentWorldMatrix);
                     axis = Vector3.TransformNormal(axis, invertParentWorldMatrix);
                 }
                 }
                 rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, TransformNode._rotationAxisCache);
                 rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, TransformNode._rotationAxisCache);
@@ -727,19 +707,32 @@ module BABYLON {
             axis.normalize();
             axis.normalize();
             if (!this.rotationQuaternion) {
             if (!this.rotationQuaternion) {
                 this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
                 this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
-                this.rotation.copyFromFloats(0, 0, 0);
+                this.rotation.setAll(0);
             }
             }
-            point.subtractToRef(this.position, Tmp.Vector3[0]);
-            Matrix.TranslationToRef(Tmp.Vector3[0].x, Tmp.Vector3[0].y, Tmp.Vector3[0].z, Tmp.Matrix[0]);
-            Tmp.Matrix[0].invertToRef(Tmp.Matrix[2]);
-            Matrix.RotationAxisToRef(axis, amount, Tmp.Matrix[1]);
-            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[2]);
-            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[2]);
 
 
-            Tmp.Matrix[2].decompose(Tmp.Vector3[0], Tmp.Quaternion[0], Tmp.Vector3[1]);
+            const tmpVector = Tmp.Vector3[0];
+            const finalScale = Tmp.Vector3[1];
+            const finalTranslation = Tmp.Vector3[2];
+
+            const finalRotation = Tmp.Quaternion[0];
+
+            const translationMatrix = Tmp.Matrix[0]; // T
+            const translationMatrixInv = Tmp.Matrix[1]; // T'
+            const rotationMatrix = Tmp.Matrix[2]; // R
+            const finalMatrix = Tmp.Matrix[3]; // T' x R x T
+
+            point.subtractToRef(this.position, tmpVector);
+            Matrix.TranslationToRef(tmpVector.x, tmpVector.y, tmpVector.z, translationMatrix); // T
+            Matrix.TranslationToRef(-tmpVector.x, -tmpVector.y, -tmpVector.z, translationMatrixInv); // T'
+            Matrix.RotationAxisToRef(axis, amount, rotationMatrix); // R
+
+            translationMatrixInv.multiplyToRef(rotationMatrix, finalMatrix); // T' x R
+            finalMatrix.multiplyToRef(translationMatrix, finalMatrix);  // T' x R x T
+
+            finalMatrix.decompose(finalScale, finalRotation, finalTranslation);
 
 
-            this.position.addInPlace(Tmp.Vector3[1]);
-            Tmp.Quaternion[0].multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
+            this.position.addInPlace(finalTranslation);
+            finalRotation.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
 
 
             return this;
             return this;
         }
         }

+ 19 - 20
src/Particles/babylon.gpuParticleSystem.ts

@@ -591,9 +591,9 @@ module BABYLON {
          * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
          * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
          */
          */
         constructor(name: string, options: Partial<{
         constructor(name: string, options: Partial<{
-                        capacity: number,
-                        randomTextureSize: number
-                    }>, scene: Scene, isAnimationSheetEnabled: boolean = false) {
+            capacity: number,
+            randomTextureSize: number
+        }>, scene: Scene, isAnimationSheetEnabled: boolean = false) {
             super(name);
             super(name);
             this._scene = scene || Engine.LastCreatedScene;
             this._scene = scene || Engine.LastCreatedScene;
             // Setup the default processing configuration to the scene.
             // Setup the default processing configuration to the scene.
@@ -626,8 +626,8 @@ module BABYLON {
             this._updateEffectOptions = {
             this._updateEffectOptions = {
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex", "cellStartOffset", "noiseCoordinates1", "noiseCoordinates2"],
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex", "cellStartOffset", "noiseCoordinates1", "noiseCoordinates2"],
                 uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange", "gravity", "emitPower",
                 uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange", "gravity", "emitPower",
-                                "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor",
-                                "angleRange", "radiusRange", "cellInfos", "noiseStrength", "limitVelocityDamping"],
+                    "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor",
+                    "angleRange", "radiusRange", "cellInfos", "noiseStrength", "limitVelocityDamping"],
                 uniformBuffersNames: [],
                 uniformBuffersNames: [],
                 samplers: ["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler", "limitVelocityGradientSampler", "noiseSampler", "dragGradientSampler"],
                 samplers: ["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler", "limitVelocityGradientSampler", "noiseSampler", "dragGradientSampler"],
                 defines: "",
                 defines: "",
@@ -673,7 +673,7 @@ module BABYLON {
         }
         }
 
 
         private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {
         private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {
-            let updateVertexBuffers: {[key: string]: VertexBuffer} = {};
+            let updateVertexBuffers: { [key: string]: VertexBuffer } = {};
             updateVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3);
             updateVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3);
             updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
             updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
             updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
             updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
@@ -725,7 +725,7 @@ module BABYLON {
         }
         }
 
 
         private _createRenderVAO(source: Buffer, spriteSource: Buffer): WebGLVertexArrayObject {
         private _createRenderVAO(source: Buffer, spriteSource: Buffer): WebGLVertexArrayObject {
-            let renderVertexBuffers: {[key: string]: VertexBuffer} = {};
+            let renderVertexBuffers: { [key: string]: VertexBuffer } = {};
             renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, this._attributesStrideSize, true);
             renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, this._attributesStrideSize, true);
             renderVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1, this._attributesStrideSize, true);
             renderVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1, this._attributesStrideSize, true);
             renderVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1, this._attributesStrideSize, true);
             renderVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1, this._attributesStrideSize, true);
@@ -877,10 +877,10 @@ module BABYLON {
             }
             }
 
 
             // Sprite data
             // Sprite data
-            var spriteData = new Float32Array([0.5, 0.5,  1, 1,
-                                              -0.5, 0.5,  0, 1,
-                                             -0.5, -0.5,  0, 0,
-                                             0.5, -0.5,  1, 0]);
+            var spriteData = new Float32Array([0.5, 0.5, 1, 1,
+                -0.5, 0.5, 0, 1,
+                -0.5, -0.5, 0, 0,
+                0.5, -0.5, 1, 0]);
 
 
             // Buffers
             // Buffers
             this._buffer0 = new Buffer(engine, data, false, this._attributesStrideSize);
             this._buffer0 = new Buffer(engine, data, false, this._attributesStrideSize);
@@ -1042,9 +1042,9 @@ module BABYLON {
             }
             }
 
 
             this._renderEffect = new Effect("gpuRenderParticles",
             this._renderEffect = new Effect("gpuRenderParticles",
-                                            ["position", "age", "life", "size", "color", "offset", "uv", "direction", "initialDirection", "angle", "cellIndex"],
-                                            uniforms,
-                                            samplers, this._scene.getEngine(), defines);
+                ["position", "age", "life", "size", "color", "offset", "uv", "direction", "initialDirection", "angle", "cellIndex"],
+                uniforms,
+                samplers, this._scene.getEngine(), defines);
         }
         }
 
 
         /**
         /**
@@ -1161,11 +1161,11 @@ module BABYLON {
                     this._preWarmDone = true;
                     this._preWarmDone = true;
                 }
                 }
 
 
-                if (this._currentRenderId === this._scene.getRenderId()) {
+                if (this._currentRenderId === this._scene.getFrameId()) {
                     return 0;
                     return 0;
                 }
                 }
 
 
-                this._currentRenderId = this._scene.getRenderId();
+                this._currentRenderId = this._scene.getFrameId();
             }
             }
 
 
             // Get everything ready to render
             // Get everything ready to render
@@ -1294,8 +1294,7 @@ module BABYLON {
                 }
                 }
 
 
                 // Draw order
                 // Draw order
-                switch (this.blendMode)
-                {
+                switch (this.blendMode) {
                     case ParticleSystem.BLENDMODE_ADD:
                     case ParticleSystem.BLENDMODE_ADD:
                         this._engine.setAlphaMode(Engine.ALPHA_ADD);
                         this._engine.setAlphaMode(Engine.ALPHA_ADD);
                         break;
                         break;
@@ -1447,7 +1446,7 @@ module BABYLON {
          * @returns the cloned particle system
          * @returns the cloned particle system
          */
          */
         public clone(name: string, newEmitter: any): GPUParticleSystem {
         public clone(name: string, newEmitter: any): GPUParticleSystem {
-            var result = new GPUParticleSystem(name, {capacity: this._capacity, randomTextureSize: this._randomTextureSize}, this._scene);
+            var result = new GPUParticleSystem(name, { capacity: this._capacity, randomTextureSize: this._randomTextureSize }, this._scene);
 
 
             Tools.DeepCopy(this, result);
             Tools.DeepCopy(this, result);
 
 
@@ -1486,7 +1485,7 @@ module BABYLON {
          */
          */
         public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string, doNotStart = false): GPUParticleSystem {
         public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string, doNotStart = false): GPUParticleSystem {
             var name = parsedParticleSystem.name;
             var name = parsedParticleSystem.name;
-            var particleSystem = new GPUParticleSystem(name, {capacity: parsedParticleSystem.capacity, randomTextureSize: parsedParticleSystem.randomTextureSize}, scene);
+            var particleSystem = new GPUParticleSystem(name, { capacity: parsedParticleSystem.capacity, randomTextureSize: parsedParticleSystem.randomTextureSize }, scene);
 
 
             if (parsedParticleSystem.activeParticleCount) {
             if (parsedParticleSystem.activeParticleCount) {
                 particleSystem.activeParticleCount = parsedParticleSystem.activeParticleCount;
                 particleSystem.activeParticleCount = parsedParticleSystem.activeParticleCount;

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

@@ -206,181 +206,181 @@ module BABYLON {
                 for (var index = 0; index < particles.length; index++) {
                 for (var index = 0; index < particles.length; index++) {
                     var particle = particles[index];
                     var particle = particles[index];
 
 
-                        let scaledUpdateSpeed = this._scaledUpdateSpeed;
-                        let previousAge = particle.age;
-                        particle.age += scaledUpdateSpeed;
+                    let scaledUpdateSpeed = this._scaledUpdateSpeed;
+                    let previousAge = particle.age;
+                    particle.age += scaledUpdateSpeed;
 
 
-                        // Evaluate step to death
-                        if (particle.age > particle.lifeTime) {
-                            let diff = particle.age - previousAge;
-                            let oldDiff = particle.lifeTime - previousAge;
+                    // Evaluate step to death
+                    if (particle.age > particle.lifeTime) {
+                        let diff = particle.age - previousAge;
+                        let oldDiff = particle.lifeTime - previousAge;
 
 
-                            scaledUpdateSpeed = (oldDiff * scaledUpdateSpeed) / diff;
+                        scaledUpdateSpeed = (oldDiff * scaledUpdateSpeed) / diff;
 
 
-                            particle.age = particle.lifeTime;
-                        }
+                        particle.age = particle.lifeTime;
+                    }
 
 
-                        let ratio = particle.age / particle.lifeTime;
-
-                        // Color
-                        if (this._colorGradients && this._colorGradients.length > 0) {
-                            Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
-                                if (currentGradient !== particle._currentColorGradient) {
-                                    particle._currentColor1.copyFrom(particle._currentColor2);
-                                    (<ColorGradient>nextGradient).getColorToRef(particle._currentColor2);
-                                    particle._currentColorGradient = (<ColorGradient>currentGradient);
-                                }
-                                Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
-                            });
-                        }
-                        else {
-                            particle.colorStep.scaleToRef(scaledUpdateSpeed, this._scaledColorStep);
-                            particle.color.addInPlace(this._scaledColorStep);
+                    let ratio = particle.age / particle.lifeTime;
 
 
-                            if (particle.color.a < 0) {
-                                particle.color.a = 0;
+                    // Color
+                    if (this._colorGradients && this._colorGradients.length > 0) {
+                        Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
+                            if (currentGradient !== particle._currentColorGradient) {
+                                particle._currentColor1.copyFrom(particle._currentColor2);
+                                (<ColorGradient>nextGradient).getColorToRef(particle._currentColor2);
+                                particle._currentColorGradient = (<ColorGradient>currentGradient);
                             }
                             }
-                        }
+                            Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
+                        });
+                    }
+                    else {
+                        particle.colorStep.scaleToRef(scaledUpdateSpeed, this._scaledColorStep);
+                        particle.color.addInPlace(this._scaledColorStep);
 
 
-                        // Angular speed
-                        if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {
-                            Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
-                                if (currentGradient !== particle._currentAngularSpeedGradient) {
-                                    particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
-                                    particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();
-                                    particle._currentAngularSpeedGradient = (<FactorGradient>currentGradient);
-                                }
-                                particle.angularSpeed = Scalar.Lerp(particle._currentAngularSpeed1, particle._currentAngularSpeed2, scale);
-                            });
-                        }
-                        particle.angle += particle.angularSpeed * scaledUpdateSpeed;
-
-                        // Direction
-                        let directionScale = scaledUpdateSpeed;
-
-                        /// Velocity
-                        if (this._velocityGradients && this._velocityGradients.length > 0) {
-                            Tools.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
-                                if (currentGradient !== particle._currentVelocityGradient) {
-                                    particle._currentVelocity1 = particle._currentVelocity2;
-                                    particle._currentVelocity2 = (<FactorGradient>nextGradient).getFactor();
-                                    particle._currentVelocityGradient = (<FactorGradient>currentGradient);
-                                }
-                                directionScale *= Scalar.Lerp(particle._currentVelocity1, particle._currentVelocity2, scale);
-                            });
+                        if (particle.color.a < 0) {
+                            particle.color.a = 0;
                         }
                         }
+                    }
 
 
-                        particle.direction.scaleToRef(directionScale, this._scaledDirection);
-
-                        /// Limit velocity
-                        if (this._limitVelocityGradients && this._limitVelocityGradients.length > 0) {
-                            Tools.GetCurrentGradient(ratio, this._limitVelocityGradients, (currentGradient, nextGradient, scale) => {
-                                if (currentGradient !== particle._currentLimitVelocityGradient) {
-                                    particle._currentLimitVelocity1 = particle._currentLimitVelocity2;
-                                    particle._currentLimitVelocity2 = (<FactorGradient>nextGradient).getFactor();
-                                    particle._currentLimitVelocityGradient = (<FactorGradient>currentGradient);
-                                }
+                    // Angular speed
+                    if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {
+                        Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
+                            if (currentGradient !== particle._currentAngularSpeedGradient) {
+                                particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
+                                particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();
+                                particle._currentAngularSpeedGradient = (<FactorGradient>currentGradient);
+                            }
+                            particle.angularSpeed = Scalar.Lerp(particle._currentAngularSpeed1, particle._currentAngularSpeed2, scale);
+                        });
+                    }
+                    particle.angle += particle.angularSpeed * scaledUpdateSpeed;
+
+                    // Direction
+                    let directionScale = scaledUpdateSpeed;
+
+                    /// Velocity
+                    if (this._velocityGradients && this._velocityGradients.length > 0) {
+                        Tools.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
+                            if (currentGradient !== particle._currentVelocityGradient) {
+                                particle._currentVelocity1 = particle._currentVelocity2;
+                                particle._currentVelocity2 = (<FactorGradient>nextGradient).getFactor();
+                                particle._currentVelocityGradient = (<FactorGradient>currentGradient);
+                            }
+                            directionScale *= Scalar.Lerp(particle._currentVelocity1, particle._currentVelocity2, scale);
+                        });
+                    }
 
 
-                                let limitVelocity = Scalar.Lerp(particle._currentLimitVelocity1, particle._currentLimitVelocity2, scale);
-                                let currentVelocity = particle.direction.length();
+                    particle.direction.scaleToRef(directionScale, this._scaledDirection);
 
 
-                                if (currentVelocity > limitVelocity) {
-                                    particle.direction.scaleInPlace(this.limitVelocityDamping);
-                                }
-                            });
-                        }
+                    /// Limit velocity
+                    if (this._limitVelocityGradients && this._limitVelocityGradients.length > 0) {
+                        Tools.GetCurrentGradient(ratio, this._limitVelocityGradients, (currentGradient, nextGradient, scale) => {
+                            if (currentGradient !== particle._currentLimitVelocityGradient) {
+                                particle._currentLimitVelocity1 = particle._currentLimitVelocity2;
+                                particle._currentLimitVelocity2 = (<FactorGradient>nextGradient).getFactor();
+                                particle._currentLimitVelocityGradient = (<FactorGradient>currentGradient);
+                            }
 
 
-                        /// Drag
-                        if (this._dragGradients && this._dragGradients.length > 0) {
-                            Tools.GetCurrentGradient(ratio, this._dragGradients, (currentGradient, nextGradient, scale) => {
-                                if (currentGradient !== particle._currentDragGradient) {
-                                    particle._currentDrag1 = particle._currentDrag2;
-                                    particle._currentDrag2 = (<FactorGradient>nextGradient).getFactor();
-                                    particle._currentDragGradient = (<FactorGradient>currentGradient);
-                                }
+                            let limitVelocity = Scalar.Lerp(particle._currentLimitVelocity1, particle._currentLimitVelocity2, scale);
+                            let currentVelocity = particle.direction.length();
 
 
-                                let drag = Scalar.Lerp(particle._currentDrag1, particle._currentDrag2, scale);
+                            if (currentVelocity > limitVelocity) {
+                                particle.direction.scaleInPlace(this.limitVelocityDamping);
+                            }
+                        });
+                    }
 
 
-                                this._scaledDirection.scaleInPlace(1.0 - drag);
-                            });
-                        }
+                    /// Drag
+                    if (this._dragGradients && this._dragGradients.length > 0) {
+                        Tools.GetCurrentGradient(ratio, this._dragGradients, (currentGradient, nextGradient, scale) => {
+                            if (currentGradient !== particle._currentDragGradient) {
+                                particle._currentDrag1 = particle._currentDrag2;
+                                particle._currentDrag2 = (<FactorGradient>nextGradient).getFactor();
+                                particle._currentDragGradient = (<FactorGradient>currentGradient);
+                            }
 
 
-                        particle.position.addInPlace(this._scaledDirection);
+                            let drag = Scalar.Lerp(particle._currentDrag1, particle._currentDrag2, scale);
 
 
-                        // Noise
-                        if (noiseTextureData && noiseTextureSize) {
-                            let fetchedColorR = this._fetchR(particle._randomNoiseCoordinates1.x, particle._randomNoiseCoordinates1.y, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
-                            let fetchedColorG = this._fetchR(particle._randomNoiseCoordinates1.z, particle._randomNoiseCoordinates2.x, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
-                            let fetchedColorB = this._fetchR(particle._randomNoiseCoordinates2.y, particle._randomNoiseCoordinates2.z, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
+                            this._scaledDirection.scaleInPlace(1.0 - drag);
+                        });
+                    }
 
 
-                            let force = Tmp.Vector3[0];
-                            let scaledForce = Tmp.Vector3[1];
+                    particle.position.addInPlace(this._scaledDirection);
 
 
-                            force.copyFromFloats((2 * fetchedColorR - 1) * this.noiseStrength.x, (2 * fetchedColorG - 1) * this.noiseStrength.y, (2 * fetchedColorB - 1) * this.noiseStrength.z);
+                    // Noise
+                    if (noiseTextureData && noiseTextureSize) {
+                        let fetchedColorR = this._fetchR(particle._randomNoiseCoordinates1.x, particle._randomNoiseCoordinates1.y, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
+                        let fetchedColorG = this._fetchR(particle._randomNoiseCoordinates1.z, particle._randomNoiseCoordinates2.x, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
+                        let fetchedColorB = this._fetchR(particle._randomNoiseCoordinates2.y, particle._randomNoiseCoordinates2.z, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
 
 
-                            force.scaleToRef(scaledUpdateSpeed, scaledForce);
-                            particle.direction.addInPlace(scaledForce);
-                        }
+                        let force = Tmp.Vector3[0];
+                        let scaledForce = Tmp.Vector3[1];
 
 
-                        // Gravity
-                        this.gravity.scaleToRef(scaledUpdateSpeed, this._scaledGravity);
-                        particle.direction.addInPlace(this._scaledGravity);
-
-                        // Size
-                        if (this._sizeGradients && this._sizeGradients.length > 0) {
-                            Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
-                                if (currentGradient !== particle._currentSizeGradient) {
-                                    particle._currentSize1 = particle._currentSize2;
-                                    particle._currentSize2 = (<FactorGradient>nextGradient).getFactor();
-                                    particle._currentSizeGradient = (<FactorGradient>currentGradient);
-                                }
-                                particle.size = Scalar.Lerp(particle._currentSize1, particle._currentSize2, scale);
-                            });
-                        }
+                        force.copyFromFloats((2 * fetchedColorR - 1) * this.noiseStrength.x, (2 * fetchedColorG - 1) * this.noiseStrength.y, (2 * fetchedColorB - 1) * this.noiseStrength.z);
 
 
-                        // Remap data
-                        if (this._useRampGradients) {
-                            if (this._colorRemapGradients && this._colorRemapGradients.length > 0) {
-                                Tools.GetCurrentGradient(ratio, this._colorRemapGradients, (currentGradient, nextGradient, scale) => {
-                                    let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
-                                    let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
+                        force.scaleToRef(scaledUpdateSpeed, scaledForce);
+                        particle.direction.addInPlace(scaledForce);
+                    }
 
 
-                                    particle.remapData.x = min;
-                                    particle.remapData.y = max - min;
-                                });
+                    // Gravity
+                    this.gravity.scaleToRef(scaledUpdateSpeed, this._scaledGravity);
+                    particle.direction.addInPlace(this._scaledGravity);
+
+                    // Size
+                    if (this._sizeGradients && this._sizeGradients.length > 0) {
+                        Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
+                            if (currentGradient !== particle._currentSizeGradient) {
+                                particle._currentSize1 = particle._currentSize2;
+                                particle._currentSize2 = (<FactorGradient>nextGradient).getFactor();
+                                particle._currentSizeGradient = (<FactorGradient>currentGradient);
                             }
                             }
+                            particle.size = Scalar.Lerp(particle._currentSize1, particle._currentSize2, scale);
+                        });
+                    }
 
 
-                            if (this._alphaRemapGradients && this._alphaRemapGradients.length > 0) {
-                                Tools.GetCurrentGradient(ratio, this._alphaRemapGradients, (currentGradient, nextGradient, scale) => {
-                                    let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
-                                    let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
+                    // Remap data
+                    if (this._useRampGradients) {
+                        if (this._colorRemapGradients && this._colorRemapGradients.length > 0) {
+                            Tools.GetCurrentGradient(ratio, this._colorRemapGradients, (currentGradient, nextGradient, scale) => {
+                                let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
+                                let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
 
 
-                                    particle.remapData.z = min;
-                                    particle.remapData.w = max - min;
-                                });
-                            }
+                                particle.remapData.x = min;
+                                particle.remapData.y = max - min;
+                            });
                         }
                         }
 
 
-                        if (this._isAnimationSheetEnabled) {
-                            particle.updateCellIndex();
+                        if (this._alphaRemapGradients && this._alphaRemapGradients.length > 0) {
+                            Tools.GetCurrentGradient(ratio, this._alphaRemapGradients, (currentGradient, nextGradient, scale) => {
+                                let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
+                                let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
+
+                                particle.remapData.z = min;
+                                particle.remapData.w = max - min;
+                            });
                         }
                         }
+                    }
 
 
-                        // Update the position of the attached sub-emitters to match their attached particle
-                        particle._inheritParticleInfoToSubEmitters();
-
-                        if (particle.age >= particle.lifeTime) { // Recycle by swapping with last particle
-                            this._emitFromParticle(particle);
-                            if (particle._attachedSubEmitters) {
-                                particle._attachedSubEmitters.forEach((subEmitter) => {
-                                    subEmitter.particleSystem.disposeOnStop = true;
-                                    subEmitter.particleSystem.stop();
-                                });
-                                particle._attachedSubEmitters = null;
-                            }
-                            this.recycleParticle(particle);
-                            index--;
-                            continue;
+                    if (this._isAnimationSheetEnabled) {
+                        particle.updateCellIndex();
+                    }
+
+                    // Update the position of the attached sub-emitters to match their attached particle
+                    particle._inheritParticleInfoToSubEmitters();
+
+                    if (particle.age >= particle.lifeTime) { // Recycle by swapping with last particle
+                        this._emitFromParticle(particle);
+                        if (particle._attachedSubEmitters) {
+                            particle._attachedSubEmitters.forEach((subEmitter) => {
+                                subEmitter.particleSystem.disposeOnStop = true;
+                                subEmitter.particleSystem.stop();
+                            });
+                            particle._attachedSubEmitters = null;
                         }
                         }
+                        this.recycleParticle(particle);
+                        index--;
+                        continue;
+                    }
                 }
                 }
             };
             };
         }
         }
@@ -1138,14 +1138,14 @@ module BABYLON {
                     }
                     }
                     else if (offsetX === 1) {
                     else if (offsetX === 1) {
                         offsetX = 1 - this._epsilon;
                         offsetX = 1 - this._epsilon;
-                         }
+                    }
 
 
                     if (offsetY === 0) {
                     if (offsetY === 0) {
                         offsetY = this._epsilon;
                         offsetY = this._epsilon;
                     }
                     }
                     else if (offsetY === 1) {
                     else if (offsetY === 1) {
                         offsetY = 1 - this._epsilon;
                         offsetY = 1 - this._epsilon;
-                         }
+                    }
                 }
                 }
 
 
                 this._vertexData[offset++] = offsetX;
                 this._vertexData[offset++] = offsetX;
@@ -1565,10 +1565,10 @@ module BABYLON {
                     return;
                     return;
                 }
                 }
 
 
-                if (this._currentRenderId === this._scene.getRenderId()) {
+                if (this._currentRenderId === this._scene.getFrameId()) {
                     return;
                     return;
                 }
                 }
-                this._currentRenderId = this._scene.getRenderId();
+                this._currentRenderId = this._scene.getFrameId();
             }
             }
 
 
             this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene.getAnimationRatio());
             this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene.getAnimationRatio());

+ 14 - 4
src/Particles/babylon.solidParticleSystem.ts

@@ -301,13 +301,23 @@ module BABYLON {
             var index = 0;
             var index = 0;
             var idx = 0;
             var idx = 0;
             const tmpNormal = Tmp.Vector3[0];
             const tmpNormal = Tmp.Vector3[0];
-            const rotMatrix = Tmp.Matrix[0];
-            const invertedRotMatrix = Tmp.Matrix[1];
+            const quaternion = Tmp.Quaternion[0];
+            const invertedRotMatrix = Tmp.Matrix[0];
             for (var p = 0; p < this.particles.length; p++) {
             for (var p = 0; p < this.particles.length; p++) {
                 const particle = this.particles[p];
                 const particle = this.particles[p];
                 const shape = particle._model._shape;
                 const shape = particle._model._shape;
-                particle.getRotationMatrix(rotMatrix);
-                rotMatrix.invertToRef(invertedRotMatrix);
+
+                // computing the inverse of the rotation matrix from the quaternion
+                // is equivalent to computing the matrix of the inverse quaternion, i.e of the conjugate quaternion
+                if (particle.rotationQuaternion) {
+                    particle.rotationQuaternion.conjugateToRef(quaternion);
+                }
+                else {
+                    const rotation = particle.rotation;
+                    Quaternion.RotationYawPitchRollToRef(rotation.y, rotation.x, rotation.z, quaternion);
+                    quaternion.conjugateInPlace();
+                }
+                quaternion.toRotationMatrix(invertedRotMatrix);
 
 
                 for (var pt = 0; pt < shape.length; pt++) {
                 for (var pt = 0; pt < shape.length; pt++) {
                     idx = index + pt * 3;
                     idx = index + pt * 3;

+ 2 - 2
src/PostProcess/babylon.motionBlurPostProcess.ts

@@ -3,7 +3,7 @@ module BABYLON {
      * The Motion Blur Post Process which blurs an image based on the objects velocity in scene.
      * The Motion Blur Post Process which blurs an image based on the objects velocity in scene.
      * Velocity can be affected by each object's rotation, position and scale depending on the transformation speed.
      * Velocity can be affected by each object's rotation, position and scale depending on the transformation speed.
      * As an example, all you have to do is to create the post-process:
      * As an example, all you have to do is to create the post-process:
-     *  var mb = new BABYLON.MotionBlurProcess(
+     *  var mb = new BABYLON.MotionBlurPostProcess(
      *      'mb', // The name of the effect.
      *      'mb', // The name of the effect.
      *      scene, // The scene containing the objects to blur according to their velocity.
      *      scene, // The scene containing the objects to blur according to their velocity.
      *      1.0, // The required width/height ratio to downsize to before computing the render pass.
      *      1.0, // The required width/height ratio to downsize to before computing the render pass.
@@ -11,7 +11,7 @@ module BABYLON {
      * );
      * );
      * Then, all objects moving, rotating and/or scaling will be blurred depending on the transformation speed.
      * Then, all objects moving, rotating and/or scaling will be blurred depending on the transformation speed.
      */
      */
-    export class MotionBlurProcess extends PostProcess {
+    export class MotionBlurPostProcess extends PostProcess {
         /**
         /**
          * Defines how much the image is blurred by the movement. Default value is equal to 1
          * Defines how much the image is blurred by the movement. Default value is equal to 1
          */
          */

+ 20 - 0
src/Tools/babylon.tools.ts

@@ -1,5 +1,25 @@
 module BABYLON {
 module BABYLON {
     /**
     /**
+     * Interface for any object that can request an animation frame
+     */
+    export interface ICustomAnimationFrameRequester {
+        /**
+         * This function will be called when the render loop is ready. If this is not populated, the engine's renderloop function will be called
+         */
+        renderFunction?: Function;
+        /**
+         * Called to request the next frame to render to
+         * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
+         */
+        requestAnimationFrame: Function;
+        /**
+         * You can pass this value to cancelAnimationFrame() to cancel the refresh callback request
+         * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#Return_value
+         */
+        requestID?: number;
+    }
+
+    /**
      * Interface containing an array of animations
      * Interface containing an array of animations
      */
      */
     export interface IAnimatable {
     export interface IAnimatable {

+ 6 - 0
src/babylon.abstractScene.ts

@@ -138,6 +138,9 @@ module BABYLON {
 
 
         /**
         /**
         * All of the materials added to this scene
         * All of the materials added to this scene
+        * In the context of a Scene, it is not supposed to be modified manually.
+        * Any addition or removal should be done using the addMaterial and removeMAterial Scene methods.
+        * Note also that the order of the Material wihin the array is not significant and might change.
         * @see http://doc.babylonjs.com/babylon101/materials
         * @see http://doc.babylonjs.com/babylon101/materials
         */
         */
         public materials = new Array<Material>();
         public materials = new Array<Material>();
@@ -155,6 +158,9 @@ module BABYLON {
 
 
         /**
         /**
         * All of the tranform nodes added to this scene
         * All of the tranform nodes added to this scene
+        * In the context of a Scene, it is not supposed to be modified manually.
+        * Any addition or removal should be done using the addTransformNode and removeTransformNode Scene methods.
+        * Note also that the order of the TransformNode wihin the array is not significant and might change.
         * @see http://doc.babylonjs.com/how_to/transformnode
         * @see http://doc.babylonjs.com/how_to/transformnode
         */
         */
         public transformNodes = new Array<TransformNode>();
         public transformNodes = new Array<TransformNode>();

+ 7 - 2
src/babylon.assetContainer.ts

@@ -20,6 +20,11 @@ module BABYLON {
         constructor(scene: Scene) {
         constructor(scene: Scene) {
             super();
             super();
             this.scene = scene;
             this.scene = scene;
+            this["sounds"] = [];
+            this["effectLayers"] = [];
+            this["layers"] = [];
+            this["lensFlareSystems"] = [];
+            this["proceduralTextures"] = [];
         }
         }
 
 
         /**
         /**
@@ -67,7 +72,7 @@ module BABYLON {
             });
             });
 
 
             for (let component of this.scene._serializableComponents) {
             for (let component of this.scene._serializableComponents) {
-                component.addFromContainer(this.scene);
+                component.addFromContainer(this);
             }
             }
         }
         }
 
 
@@ -116,7 +121,7 @@ module BABYLON {
             });
             });
 
 
             for (let component of this.scene._serializableComponents) {
             for (let component of this.scene._serializableComponents) {
-                component.removeFromContainer(this.scene);
+                component.removeFromContainer(this);
             }
             }
         }
         }
 
 

+ 41 - 10
src/babylon.mixins.ts

@@ -99,7 +99,6 @@ interface Document {
     webkitCancelFullScreen(): void;
     webkitCancelFullScreen(): void;
     requestPointerLock(): void;
     requestPointerLock(): void;
     exitPointerLock(): void;
     exitPointerLock(): void;
-    fullscreen: boolean;
     mozFullScreen: boolean;
     mozFullScreen: boolean;
     msIsFullScreen: boolean;
     msIsFullScreen: boolean;
     readonly webkitIsFullScreen: boolean;
     readonly webkitIsFullScreen: boolean;
@@ -155,15 +154,6 @@ interface HTMLVideoElement {
     mozSrcObject: any;
     mozSrcObject: any;
 }
 }
 
 
-interface Element {
-    webkitRequestFullScreen: () => void;
-}
-
-interface Screen {
-    readonly orientation: string;
-    readonly mozOrientation: string;
-}
-
 interface Math {
 interface Math {
     fround(x: number): number;
     fround(x: number): number;
     imul(a: number, b: number): number;
     imul(a: number, b: number): number;
@@ -187,3 +177,44 @@ interface EXT_disjoint_timer_query {
 interface WebGLUniformLocation {
 interface WebGLUniformLocation {
     _currentState: any;
     _currentState: any;
 }
 }
+
+// WebXR
+interface XRDevice {
+    requestSession(options: XRSessionCreationOptions): Promise<XRSession>;
+}
+interface XRSession {
+    baseLayer: XRWebGLLayer;
+    requestFrameOfReference(type: XRFrameOfReferenceType): Promise<void>;
+    requestHitTest(origin: Float32Array, direction: Float32Array, frameOfReference: any): any;
+    end(): void;
+    requestAnimationFrame: Function;
+}
+interface XRSessionCreationOptions {
+}
+interface XRLayer {
+    getViewport: Function;
+    framebufferWidth: number;
+    framebufferHeight: number;
+}
+interface XRView {
+    projectionMatrix: Float32Array;
+}
+interface XRFrame {
+    getDevicePose: Function;
+    views: Array<XRView>;
+    baseLayer: XRLayer;
+}
+interface XRFrameOfReference {
+}
+interface XRWebGLLayer extends XRLayer {
+    framebuffer: WebGLFramebuffer;
+}
+declare var XRWebGLLayer: {
+    prototype: XRWebGLLayer;
+    new(session: XRSession, context?: WebGLRenderingContext): XRWebGLLayer;
+};
+enum XRFrameOfReferenceType {
+    "head-model",
+    "eye-level",
+    "stage",
+}

+ 119 - 37
src/babylon.scene.ts

@@ -70,6 +70,18 @@ module BABYLON {
          * It will improve performance when the number of geometries becomes important.
          * It will improve performance when the number of geometries becomes important.
          */
          */
         useGeometryIdsMap?: boolean;
         useGeometryIdsMap?: boolean;
+
+        /**
+         * Defines that each material of the scene should keep up-to-date a map of referencing meshes for fast diposing
+         * It will improve performance when the number of mesh becomes important, but might consume a bit more memory
+         */
+        useMaterialMeshMap?: boolean;
+
+        /**
+         * Defines that each mesh of the scene should keep up-to-date a map of referencing cloned meshes for fast diposing
+         * It will improve performance when the number of mesh becomes important, but might consume a bit more memory
+         */
+        useClonedMeshhMap?: boolean;
     }
     }
 
 
     /**
     /**
@@ -253,6 +265,12 @@ module BABYLON {
          */
          */
         public preventDefaultOnPointerDown = true;
         public preventDefaultOnPointerDown = true;
 
 
+        /**
+         * This is used to call preventDefault() on pointer up
+         * in order to block unwanted artifacts like system double clicks
+         */
+        public preventDefaultOnPointerUp = true;
+
         // Metadata
         // Metadata
         /**
         /**
          * Gets or sets user defined metadata
          * Gets or sets user defined metadata
@@ -987,6 +1005,7 @@ module BABYLON {
         private _alternateTransformMatrix: Matrix;
         private _alternateTransformMatrix: Matrix;
         private _useAlternateCameraConfiguration = false;
         private _useAlternateCameraConfiguration = false;
         private _alternateRendering = false;
         private _alternateRendering = false;
+        private _wheelEventName = "";
         /** @hidden */
         /** @hidden */
         public _forcedViewPosition: Nullable<Vector3>;
         public _forcedViewPosition: Nullable<Vector3>;
 
 
@@ -1009,6 +1028,11 @@ module BABYLON {
          */
          */
         public requireLightSorting = false;
         public requireLightSorting = false;
 
 
+        /** @hidden */
+        public readonly useMaterialMeshMap: boolean;
+        /** @hidden */
+        public readonly useClonedMeshhMap: boolean;
+
         private _pointerOverMesh: Nullable<AbstractMesh>;
         private _pointerOverMesh: Nullable<AbstractMesh>;
 
 
         private _pickedDownMesh: Nullable<AbstractMesh>;
         private _pickedDownMesh: Nullable<AbstractMesh>;
@@ -1216,6 +1240,9 @@ module BABYLON {
             if (options && options.useGeometryIdsMap === true) {
             if (options && options.useGeometryIdsMap === true) {
                 this.geometriesById = {};
                 this.geometriesById = {};
             }
             }
+
+            this.useMaterialMeshMap = options && options.useGeometryIdsMap || false;
+            this.useClonedMeshhMap = options && options.useClonedMeshhMap || false;
         }
         }
 
 
         private _defaultMeshCandidates: ISmartArrayLike<AbstractMesh> = {
         private _defaultMeshCandidates: ISmartArrayLike<AbstractMesh> = {
@@ -1609,7 +1636,7 @@ module BABYLON {
             }
             }
 
 
             if (pickResult) {
             if (pickResult) {
-                let type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE;
+                let type = evt.type === this._wheelEventName ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE;
 
 
                 if (this.onPointerMove) {
                 if (this.onPointerMove) {
                     this.onPointerMove(evt, pickResult, type);
                     this.onPointerMove(evt, pickResult, type);
@@ -1726,8 +1753,6 @@ module BABYLON {
         public simulatePointerUp(pickResult: PickingInfo, pointerEventInit?: PointerEventInit): Scene {
         public simulatePointerUp(pickResult: PickingInfo, pointerEventInit?: PointerEventInit): Scene {
             let evt = new PointerEvent("pointerup", pointerEventInit);
             let evt = new PointerEvent("pointerup", pointerEventInit);
             let clickInfo = new ClickInfo();
             let clickInfo = new ClickInfo();
-            clickInfo.singleClick = true;
-            clickInfo.ignore = true;
 
 
             if (this._checkPrePointerObservable(pickResult, evt, PointerEventTypes.POINTERUP)) {
             if (this._checkPrePointerObservable(pickResult, evt, PointerEventTypes.POINTERUP)) {
                 return this;
                 return this;
@@ -1779,30 +1804,20 @@ module BABYLON {
 
 
             let type = PointerEventTypes.POINTERUP;
             let type = PointerEventTypes.POINTERUP;
             if (this.onPointerObservable.hasObservers()) {
             if (this.onPointerObservable.hasObservers()) {
-                if (!clickInfo.ignore) {
-                    if (!clickInfo.hasSwiped) {
-                        if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
-                            let type = PointerEventTypes.POINTERTAP;
-                            let pi = new PointerInfo(type, evt, pickResult);
-                            this._setRayOnPointerInfo(pi);
-                            this.onPointerObservable.notifyObservers(pi, type);
-                        }
-                        if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
-                            let type = PointerEventTypes.POINTERDOUBLETAP;
-                            let pi = new PointerInfo(type, evt, pickResult);
-                            this._setRayOnPointerInfo(pi);
-                            this.onPointerObservable.notifyObservers(pi, type);
-                        }
+                if (!clickInfo.ignore && !clickInfo.hasSwiped) {
+                    if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
+                        type = PointerEventTypes.POINTERTAP;
+                    }
+                    else if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
+                        type = PointerEventTypes.POINTERDOUBLETAP;
                     }
                     }
                 }
                 }
-                else {
-                    let pi = new PointerInfo(type, evt, pickResult);
-                    this._setRayOnPointerInfo(pi);
-                    this.onPointerObservable.notifyObservers(pi, type);
-                }
+                let pi = new PointerInfo(type, evt, pickResult);
+                this._setRayOnPointerInfo(pi);
+                this.onPointerObservable.notifyObservers(pi, type);
             }
             }
 
 
-            if (this.onPointerUp) {
+            if (this.onPointerUp && !clickInfo.ignore) {
                 this.onPointerUp(evt, pickResult, type);
                 this.onPointerUp(evt, pickResult, type);
             }
             }
 
 
@@ -1892,7 +1907,6 @@ module BABYLON {
                             if (Date.now() - this._previousStartingPointerTime > Scene.DoubleClickDelay ||
                             if (Date.now() - this._previousStartingPointerTime > Scene.DoubleClickDelay ||
                                 btn !== this._previousButtonPressed) {
                                 btn !== this._previousButtonPressed) {
                                 clickInfo.singleClick = true;
                                 clickInfo.singleClick = true;
-
                                 cb(clickInfo, this._currentPickResult);
                                 cb(clickInfo, this._currentPickResult);
                             }
                             }
                         }
                         }
@@ -1961,6 +1975,7 @@ module BABYLON {
                         }
                         }
                     }
                     }
                 }
                 }
+
                 clickInfo.ignore = true;
                 clickInfo.ignore = true;
                 cb(clickInfo, this._currentPickResult);
                 cb(clickInfo, this._currentPickResult);
             };
             };
@@ -1970,7 +1985,7 @@ module BABYLON {
                 this._updatePointerPosition(evt);
                 this._updatePointerPosition(evt);
 
 
                 // PreObservable support
                 // PreObservable support
-                if (this._checkPrePointerObservable(null, evt, evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE)) {
+                if (this._checkPrePointerObservable(null, evt, evt.type === this._wheelEventName ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE)) {
                     return;
                     return;
                 }
                 }
 
 
@@ -2037,6 +2052,12 @@ module BABYLON {
                 this._meshPickProceed = false;
                 this._meshPickProceed = false;
 
 
                 this._updatePointerPosition(evt);
                 this._updatePointerPosition(evt);
+
+                if (this.preventDefaultOnPointerUp && canvas) {
+                    evt.preventDefault();
+                    canvas.focus();
+                }
+
                 this._initClickEvent(this.onPrePointerObservable, this.onPointerObservable, evt, (clickInfo: ClickInfo, pickResult: Nullable<PickingInfo>) => {
                 this._initClickEvent(this.onPrePointerObservable, this.onPointerObservable, evt, (clickInfo: ClickInfo, pickResult: Nullable<PickingInfo>) => {
                     // PreObservable support
                     // PreObservable support
                     if (this.onPrePointerObservable.hasObservers()) {
                     if (this.onPrePointerObservable.hasObservers()) {
@@ -2153,9 +2174,13 @@ module BABYLON {
 
 
             if (attachMove) {
             if (attachMove) {
                 canvas.addEventListener(eventPrefix + "move", <any>this._onPointerMove, false);
                 canvas.addEventListener(eventPrefix + "move", <any>this._onPointerMove, false);
+
                 // Wheel
                 // Wheel
-                canvas.addEventListener('mousewheel', <any>this._onPointerMove, false);
-                canvas.addEventListener('DOMMouseScroll', <any>this._onPointerMove, false);
+                this._wheelEventName = "onwheel" in document.createElement("div") ? "wheel" :       // Modern browsers support "wheel"
+                    (<any>document).onmousewheel !== undefined ? "mousewheel" :                     // Webkit and IE support at least "mousewheel"
+                        "DOMMouseScroll";                                                           // let's assume that remaining browsers are older Firefox
+
+                canvas.addEventListener(this._wheelEventName, <any>this._onPointerMove, false);
             }
             }
 
 
             if (attachDown) {
             if (attachDown) {
@@ -2192,8 +2217,7 @@ module BABYLON {
             }
             }
 
 
             // Wheel
             // Wheel
-            canvas.removeEventListener('mousewheel', <any>this._onPointerMove);
-            canvas.removeEventListener('DOMMouseScroll', <any>this._onPointerMove);
+            canvas.removeEventListener(this._wheelEventName, <any>this._onPointerMove);
 
 
             // Keyboard
             // Keyboard
             canvas.removeEventListener("keydown", this._onKeyDown);
             canvas.removeEventListener("keydown", this._onKeyDown);
@@ -2983,7 +3007,8 @@ module BABYLON {
             var index = this.meshes.indexOf(toRemove);
             var index = this.meshes.indexOf(toRemove);
             if (index !== -1) {
             if (index !== -1) {
                 // Remove from the scene if mesh found
                 // Remove from the scene if mesh found
-                this.meshes.splice(index, 1);
+                this.meshes[index] = this.meshes[this.meshes.length - 1];
+                this.meshes.pop();
             }
             }
 
 
             this.onMeshRemovedObservable.notifyObservers(toRemove);
             this.onMeshRemovedObservable.notifyObservers(toRemove);
@@ -3000,6 +3025,7 @@ module BABYLON {
          * @param newTransformNode defines the transform node to add
          * @param newTransformNode defines the transform node to add
          */
          */
         public addTransformNode(newTransformNode: TransformNode) {
         public addTransformNode(newTransformNode: TransformNode) {
+            newTransformNode._indexInSceneTransformNodesArray = this.transformNodes.length;
             this.transformNodes.push(newTransformNode);
             this.transformNodes.push(newTransformNode);
 
 
             this.onNewTransformNodeAddedObservable.notifyObservers(newTransformNode);
             this.onNewTransformNodeAddedObservable.notifyObservers(newTransformNode);
@@ -3011,10 +3037,16 @@ module BABYLON {
          * @returns the index where the transform node was in the transform node list
          * @returns the index where the transform node was in the transform node list
          */
          */
         public removeTransformNode(toRemove: TransformNode): number {
         public removeTransformNode(toRemove: TransformNode): number {
-            var index = this.transformNodes.indexOf(toRemove);
+            var index = toRemove._indexInSceneTransformNodesArray;
             if (index !== -1) {
             if (index !== -1) {
-                // Remove from the scene if found
-                this.transformNodes.splice(index, 1);
+                if (index !== this.transformNodes.length - 1) {
+                    const lastNode = this.transformNodes[this.transformNodes.length - 1];
+                    this.transformNodes[index] = lastNode;
+                    lastNode._indexInSceneTransformNodesArray = index;
+                }
+
+                toRemove._indexInSceneTransformNodesArray = -1;
+                this.transformNodes.pop();
             }
             }
 
 
             this.onTransformNodeRemovedObservable.notifyObservers(toRemove);
             this.onTransformNodeRemovedObservable.notifyObservers(toRemove);
@@ -3160,10 +3192,18 @@ module BABYLON {
          * @returns The index of the removed material
          * @returns The index of the removed material
          */
          */
         public removeMaterial(toRemove: Material): number {
         public removeMaterial(toRemove: Material): number {
-            var index = this.materials.indexOf(toRemove);
+            var index = toRemove._indexInSceneMaterialArray;
             if (index !== -1) {
             if (index !== -1) {
-                this.materials.splice(index, 1);
+                if (index !== this.materials.length - 1) {
+                    const lastMaterial = this.materials[this.materials.length - 1];
+                    this.materials[index] = lastMaterial;
+                    lastMaterial._indexInSceneMaterialArray = index;
+                }
+
+                toRemove._indexInSceneMaterialArray = -1;
+                this.materials.pop();
             }
             }
+
             this.onMaterialRemovedObservable.notifyObservers(toRemove);
             this.onMaterialRemovedObservable.notifyObservers(toRemove);
 
 
             return index;
             return index;
@@ -3279,6 +3319,7 @@ module BABYLON {
          * @param newMaterial The material to add
          * @param newMaterial The material to add
          */
          */
         public addMaterial(newMaterial: Material): void {
         public addMaterial(newMaterial: Material): void {
+            newMaterial._indexInSceneMaterialArray = this.materials.length;
             this.materials.push(newMaterial);
             this.materials.push(newMaterial);
             this.onNewMaterialAddedObservable.notifyObservers(newMaterial);
             this.onNewMaterialAddedObservable.notifyObservers(newMaterial);
         }
         }
@@ -4016,10 +4057,38 @@ module BABYLON {
             this._processedMaterials.dispose();
             this._processedMaterials.dispose();
         }
         }
 
 
+        private _preventFreeActiveMeshesAndRenderingGroups = false;
+
+        /** Gets or sets a boolean blocking all the calls to freeActiveMeshes and freeRenderingGroups
+         * It can be used in order to prevent going through methods freeRenderingGroups and freeActiveMeshes several times to improve performance
+         * when disposing several meshes in a row or a hierarchy of meshes.
+         * When used, it is the responsability of the user to blockfreeActiveMeshesAndRenderingGroups back to false.
+         */
+        public get blockfreeActiveMeshesAndRenderingGroups(): boolean {
+            return this._preventFreeActiveMeshesAndRenderingGroups;
+        }
+
+        public set blockfreeActiveMeshesAndRenderingGroups(value: boolean) {
+            if (this._preventFreeActiveMeshesAndRenderingGroups === value) {
+                return;
+            }
+
+            if (value) {
+                this.freeActiveMeshes();
+                this.freeRenderingGroups();
+            }
+
+            this._preventFreeActiveMeshesAndRenderingGroups = value;
+        }
+
         /**
         /**
          * Clear the active meshes smart array preventing retention point in mesh dispose.
          * Clear the active meshes smart array preventing retention point in mesh dispose.
          */
          */
         public freeActiveMeshes(): void {
         public freeActiveMeshes(): void {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
+
             this._activeMeshes.dispose();
             this._activeMeshes.dispose();
             if (this.activeCamera && this.activeCamera._activeMeshes) {
             if (this.activeCamera && this.activeCamera._activeMeshes) {
                 this.activeCamera._activeMeshes.dispose();
                 this.activeCamera._activeMeshes.dispose();
@@ -4038,6 +4107,10 @@ module BABYLON {
          * Clear the info related to rendering groups preventing retention points during dispose.
          * Clear the info related to rendering groups preventing retention points during dispose.
          */
          */
         public freeRenderingGroups(): void {
         public freeRenderingGroups(): void {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
+
             if (this._renderingManager) {
             if (this._renderingManager) {
                 this._renderingManager.freeRenderingGroups();
                 this._renderingManager.freeRenderingGroups();
             }
             }
@@ -4159,7 +4232,7 @@ module BABYLON {
 
 
                 mesh._preActivate();
                 mesh._preActivate();
 
 
-                if (mesh.isVisible && mesh.visibility > 0 && (mesh.alwaysSelectAsActiveMesh || ((mesh.layerMask & this.activeCamera.layerMask) !== 0 && mesh.isInFrustum(this._frustumPlanes)))) {
+                if (mesh.isVisible && mesh.visibility > 0 && ((mesh.layerMask & this.activeCamera.layerMask) !== 0) && (mesh.alwaysSelectAsActiveMesh || mesh.isInFrustum(this._frustumPlanes))) {
                     this._activeMeshes.push(mesh);
                     this._activeMeshes.push(mesh);
                     this.activeCamera._activeMeshes.push(mesh);
                     this.activeCamera._activeMeshes.push(mesh);
 
 
@@ -4321,7 +4394,16 @@ module BABYLON {
 
 
                 this._intermediateRendering = false;
                 this._intermediateRendering = false;
 
 
-                engine.restoreDefaultFramebuffer(); // Restore back buffer if needed
+                if (this.activeCamera.outputRenderTarget) {
+                    var internalTexture = this.activeCamera.outputRenderTarget.getInternalTexture();
+                    if (internalTexture) {
+                        engine.bindFramebuffer(internalTexture);
+                    } else {
+                        Tools.Error("Camera contains invalid customDefaultRenderTarget");
+                    }
+                } else {
+                    engine.restoreDefaultFramebuffer(); // Restore back buffer if needed
+                }
             }
             }
 
 
             this.onAfterRenderTargetsRenderObservable.notifyObservers(this);
             this.onAfterRenderTargetsRenderObservable.notifyObservers(this);

+ 1 - 1
src/babylon.sceneComponent.ts

@@ -138,7 +138,7 @@ module BABYLON {
     /**
     /**
      * Strong typing of a Active Mesh related stage step action
      * Strong typing of a Active Mesh related stage step action
      */
      */
-    export type ActiveMeshStageAction =  (sourceMesh: AbstractMesh, mesh: AbstractMesh) => void;
+    export type ActiveMeshStageAction = (sourceMesh: AbstractMesh, mesh: AbstractMesh) => void;
 
 
     /**
     /**
      * Strong typing of a Camera related stage step action
      * Strong typing of a Camera related stage step action

二進制
tests/validation/ReferenceImages/cameraRig.png


二進制
tests/validation/ReferenceImages/sps.png


+ 7 - 1
tests/validation/config.json

@@ -2,13 +2,19 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
   "tests": [
     {
     {
+      "title": "Solid particle system",
+      "playgroundId": "#WCDZS#92",
+      "renderCount": 150,
+      "referenceImage": "sps.png"
+    },
+    {
       "title": "Simulate pointer",
       "title": "Simulate pointer",
       "playgroundId": "#8MGKWK#78",
       "playgroundId": "#8MGKWK#78",
       "referenceImage": "simulatePointer.png"
       "referenceImage": "simulatePointer.png"
     },
     },
     {
     {
       "title": "Camera rig",
       "title": "Camera rig",
-      "playgroundId": "#ATL1CS#6",
+      "playgroundId": "#ATL1CS#9",
       "referenceImage": "cameraRig.png"
       "referenceImage": "cameraRig.png"
     },
     },
     {
     {

+ 3 - 3
tests/validation/validation.js

@@ -7,7 +7,7 @@ var config;
 var justOnce;
 var justOnce;
 
 
 var threshold = 25;
 var threshold = 25;
-var errorCountThreshold = 400;
+var errorRatio = 2.5;
 
 
 // Overload the random to make it deterministic
 // Overload the random to make it deterministic
 var seed = 100000,
 var seed = 100000,
@@ -52,7 +52,7 @@ function compare(renderData, referenceCanvas) {
         console.log("Pixel difference: " + differencesCount + " pixels.");
         console.log("Pixel difference: " + differencesCount + " pixels.");
     }
     }
 
 
-    return differencesCount > errorCountThreshold;
+    return (differencesCount * 100) / (width * height) > errorRatio;
 }
 }
 
 
 function getRenderData(canvas, engine) {
 function getRenderData(canvas, engine) {
@@ -235,7 +235,7 @@ function runTest(index, done) {
         var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
         var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
         var pgRoot = "/Playground"
         var pgRoot = "/Playground"
 
 
-        var retryTime = 30 * 1000;
+        var retryTime = 500;
         var maxRetry = 2;
         var maxRetry = 2;
         var retry = 0;
         var retry = 0;