Browse Source

Merge remote-tracking branch 'BabylonJS/master' into prepare-for-vr

Raanan Weber 7 years ago
parent
commit
dabdec916c
76 changed files with 29323 additions and 24562 deletions
  1. 1 0
      .vscode/settings.json
  2. 10000 9781
      Playground/babylon.d.txt
  3. 6 8
      Playground/index.html
  4. BIN
      Playground/textures/360photo.jpg
  5. 1 4
      Tools/Gulp/config.json
  6. 20 0
      Viewer/assets/templates/default/navbar.html
  7. 4 0
      Viewer/src/configuration/mappers.ts
  8. 1 1
      Viewer/src/templating/templateManager.ts
  9. 11103 10868
      dist/preview release/babylon.d.ts
  10. 55 55
      dist/preview release/babylon.js
  11. 1112 402
      dist/preview release/babylon.max.js
  12. 1112 402
      dist/preview release/babylon.no-module.max.js
  13. 56 56
      dist/preview release/babylon.worker.js
  14. 1114 404
      dist/preview release/es6.js
  15. 4 0
      dist/preview release/gui/babylon.gui.d.ts
  16. 165 22
      dist/preview release/gui/babylon.gui.js
  17. 4 4
      dist/preview release/gui/babylon.gui.min.js
  18. 4 0
      dist/preview release/gui/babylon.gui.module.d.ts
  19. 9 4
      dist/preview release/loaders/babylon.objFileLoader.d.ts
  20. 60 44
      dist/preview release/loaders/babylon.objFileLoader.js
  21. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  22. 9 4
      dist/preview release/loaders/babylonjs.loaders.d.ts
  23. 60 44
      dist/preview release/loaders/babylonjs.loaders.js
  24. 4 4
      dist/preview release/loaders/babylonjs.loaders.min.js
  25. 9 4
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  26. 69 82
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  27. 350 315
      dist/preview release/serializers/babylon.glTF2Serializer.js
  28. 2 2
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  29. 69 82
      dist/preview release/serializers/babylonjs.serializers.d.ts
  30. 350 315
      dist/preview release/serializers/babylonjs.serializers.js
  31. 2 2
      dist/preview release/serializers/babylonjs.serializers.min.js
  32. 69 82
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  33. 69 69
      dist/preview release/viewer/babylon.viewer.js
  34. 1183 451
      dist/preview release/viewer/babylon.viewer.max.js
  35. 16 5
      dist/preview release/what's new.md
  36. 183 33
      gui/src/2D/controls/slider.ts
  37. 82 66
      loaders/src/OBJ/babylon.objFileLoader.ts
  38. 153 125
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  39. 229 208
      serializers/src/glTF/2.0/babylon.glTFMaterial.ts
  40. 0 10
      serializers/src/glTF/2.0/shaders/setAlphaToOne.fragment.fx
  41. 10 2
      src/Animations/babylon.runtimeAnimation.ts
  42. 49 16
      src/Audio/babylon.sound.ts
  43. 9 3
      src/Behaviors/Mesh/babylon.pointerDragBehavior.ts
  44. 34 6
      src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts
  45. 4 0
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  46. 16 2
      src/Collisions/babylon.pickingInfo.ts
  47. 61 23
      src/Engine/babylon.engine.ts
  48. 60 14
      src/Gizmos/babylon.axisDragGizmo.ts
  49. 82 15
      src/Gizmos/babylon.axisScaleGizmo.ts
  50. 44 9
      src/Gizmos/babylon.boundingBoxGizmo.ts
  51. 12 13
      src/Gizmos/babylon.gizmo.ts
  52. 82 25
      src/Gizmos/babylon.planeRotationGizmo.ts
  53. 19 11
      src/Gizmos/babylon.positionGizmo.ts
  54. 18 10
      src/Gizmos/babylon.rotationGizmo.ts
  55. 18 10
      src/Gizmos/babylon.scaleGizmo.ts
  56. 1 1
      src/Helpers/babylon.environmentHelper.ts
  57. 32 13
      src/Helpers/babylon.photoDome.ts
  58. 23 12
      src/Helpers/babylon.videoDome.ts
  59. 4 0
      src/Materials/Textures/babylon.rawTexture.ts
  60. 3 1
      src/Materials/Textures/babylon.renderTargetTexture.ts
  61. 4 0
      src/Materials/Textures/babylon.videoTexture.ts
  62. 6 5
      src/Math/babylon.math.ts
  63. 9 0
      src/Mesh/babylon.mesh.vertexData.ts
  64. 4 2
      src/Mesh/babylon.transformNode.ts
  65. 4 4
      src/Particles/EmitterTypes/babylon.boxParticleEmitter.ts
  66. 66 4
      src/Particles/babylon.IParticleSystem.ts
  67. 420 185
      src/Particles/babylon.gpuParticleSystem.ts
  68. 3 0
      src/Particles/babylon.particle.ts
  69. 337 131
      src/Particles/babylon.particleSystem.ts
  70. 12 7
      src/Shaders/ShadersInclude/reflectionFunction.fx
  71. 13 3
      src/Shaders/gpuRenderParticles.vertex.fx
  72. 46 16
      src/Shaders/gpuUpdateParticles.vertex.fx
  73. 3 3
      src/Tools/babylon.khronosTextureContainer.ts
  74. 70 0
      src/Tools/babylon.tools.ts
  75. 4 56
      tests/unit/babylon/serializers/babylon.glTFSerializer.tests.ts
  76. 1 1
      tests/unit/babylon/src/Animations/babylon.animationGroup.tests.ts

+ 1 - 0
.vscode/settings.json

@@ -43,5 +43,6 @@
         "**/*.d.ts": true,
         "**/*.d.ts": true,
         "assets": true
         "assets": true
     },
     },
+    "editor.tabSize": 4,
     "typescript.tsdk": "./Tools/Gulp/node_modules/typescript/lib"
     "typescript.tsdk": "./Tools/Gulp/node_modules/typescript/lib"
 }
 }

File diff suppressed because it is too large
+ 10000 - 9781
Playground/babylon.d.txt


+ 6 - 8
Playground/index.html

@@ -42,21 +42,19 @@
         <!-- Babylon.js -->
         <!-- Babylon.js -->
         <script src="https://preview.babylonjs.com/babylon.js"></script>
         <script src="https://preview.babylonjs.com/babylon.js"></script>
         <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
         <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
-
         <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
         <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
-
         <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
         <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
-
         <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
         <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
-
         <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
         <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
-
         <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
         <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
-
         <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
         <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
 
 
-        <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>
-        <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js"></script>
+        <!-- Extensions -->
+        <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js" async></script>
+        <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js" async></script>
+        <script src="https://www.babylontoolkit.com/playground/scripts/babylon.navmesh.js"></script>
+        <script src="https://www.babylontoolkit.com/playground/scripts/babylon.manager.js"></script>
+                               
         <link href="css/index.css" rel="stylesheet" />
         <link href="css/index.css" rel="stylesheet" />
     </head>
     </head>
 
 

BIN
Playground/textures/360photo.jpg


+ 1 - 4
Tools/Gulp/config.json

@@ -1691,13 +1691,10 @@
                     "../../serializers/src/glTF/2.0/babylon.glTFSerializer.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFSerializer.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFExporter.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFExporter.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFData.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFData.ts",
-                    "../../serializers/src/glTF/2.0/babylon.glTFMaterial.ts",
+                    "../../serializers/src/glTF/2.0/babylon.glTFMaterialExporter.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFAnimation.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFAnimation.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFUtilities.ts"
                     "../../serializers/src/glTF/2.0/babylon.glTFUtilities.ts"
                 ],
                 ],
-                "shaderFiles": [
-                    "../../serializers/src/glTF/2.0/shaders/setAlphaToOne.fragment.fx"
-                ],
                 "output": "babylon.glTF2Serializer.js"
                 "output": "babylon.glTF2Serializer.js"
             }
             }
         ],
         ],

+ 20 - 0
Viewer/assets/templates/default/navbar.html

@@ -139,6 +139,26 @@
         content: "\E740";
         content: "\E740";
     }
     }
 
 
+    /*
+    A few browsers dont support combining the next rule to one. Redudant.
+    */
+
+    viewer:fullscreen .fullscreen-icon:after {
+        content: "\E73F";
+    }
+
+    viewer:-webkit-full-screen .fullscreen-icon:after {
+        content: "\E73F";
+    }
+
+    viewer:-moz-full-screen .fullscreen-icon:after {
+        content: "\E73F";
+    }
+
+    viewer:-ms-full-screen .fullscreen-icon:after {
+        content: "\E73F";
+    }
+
     .help-icon:after {
     .help-icon:after {
         font-size: 16px;
         font-size: 16px;
         content: "\EF4E";
         content: "\EF4E";

+ 4 - 0
Viewer/src/configuration/mappers.ts

@@ -52,6 +52,10 @@ class HTMLMapper implements IMapper {
                         val = true;
                         val = true;
                     } else if (val === "false") {
                     } else if (val === "false") {
                         val = false;
                         val = false;
+                    } else if (val === "undefined") {
+                        val = undefined;
+                    } else if (val === "null") {
+                        val = null;
                     } else {
                     } else {
                         var isnum = !isNaN(parseFloat(val)) && isFinite(val);///^\d+$/.test(val);
                         var isnum = !isNaN(parseFloat(val)) && isFinite(val);///^\d+$/.test(val);
                         if (isnum) {
                         if (isnum) {

+ 1 - 1
Viewer/src/templating/templateManager.ts

@@ -530,7 +530,7 @@ export class Template {
     private _getTemplateAsHtml(templateConfig: ITemplateConfiguration): Promise<string> {
     private _getTemplateAsHtml(templateConfig: ITemplateConfiguration): Promise<string> {
         if (!templateConfig) {
         if (!templateConfig) {
             return Promise.reject('No templateConfig provided');
             return Promise.reject('No templateConfig provided');
-        } else if (templateConfig.html !== undefined) {
+        } else if (templateConfig.html) {
             return Promise.resolve(templateConfig.html);
             return Promise.resolve(templateConfig.html);
         } else {
         } else {
             let location = this._getTemplateLocation(templateConfig);
             let location = this._getTemplateLocation(templateConfig);

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


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


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


File diff suppressed because it is too large
+ 1112 - 402
dist/preview release/babylon.no-module.max.js


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


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


+ 4 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -1195,6 +1195,7 @@ declare module BABYLON.GUI {
         private _minimum;
         private _minimum;
         private _maximum;
         private _maximum;
         private _value;
         private _value;
+        private _isVertical;
         private _background;
         private _background;
         private _borderColor;
         private _borderColor;
         private _barOffset;
         private _barOffset;
@@ -1220,6 +1221,8 @@ declare module BABYLON.GUI {
         maximum: number;
         maximum: number;
         /** Gets or sets current value */
         /** Gets or sets current value */
         value: number;
         value: number;
+        /**Gets or sets a boolean indicating if the slider should be vertical or horizontal */
+        isVertical: boolean;
         /** Gets or sets a boolean indicating if the thumb should be round or square */
         /** Gets or sets a boolean indicating if the thumb should be round or square */
         isThumbCircle: boolean;
         isThumbCircle: boolean;
         /** Gets or sets a value indicating if the thumb can go over main bar extends */
         /** Gets or sets a value indicating if the thumb can go over main bar extends */
@@ -1230,6 +1233,7 @@ declare module BABYLON.GUI {
          */
          */
         constructor(name?: string | undefined);
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
         protected _getTypeName(): string;
+        protected _getThumbThickness(type: string, backgroundLength: number): number;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         private _pointerIsDown;
         private _pointerIsDown;
         private _updateValueFromPointer(x, y);
         private _updateValueFromPointer(x, y);

+ 165 - 22
dist/preview release/gui/babylon.gui.js

@@ -3738,10 +3738,11 @@ var BABYLON;
             function Slider(name) {
             function Slider(name) {
                 var _this = _super.call(this, name) || this;
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
                 _this.name = name;
-                _this._thumbWidth = new GUI.ValueAndUnit(30, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
+                _this._thumbWidth = new GUI.ValueAndUnit(20, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
                 _this._minimum = 0;
                 _this._minimum = 0;
                 _this._maximum = 100;
                 _this._maximum = 100;
                 _this._value = 50;
                 _this._value = 50;
+                _this._isVertical = false;
                 _this._background = "black";
                 _this._background = "black";
                 _this._borderColor = "white";
                 _this._borderColor = "white";
                 _this._barOffset = new GUI.ValueAndUnit(5, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
                 _this._barOffset = new GUI.ValueAndUnit(5, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
@@ -3881,6 +3882,21 @@ var BABYLON;
                 enumerable: true,
                 enumerable: true,
                 configurable: true
                 configurable: true
             });
             });
+            Object.defineProperty(Slider.prototype, "isVertical", {
+                /**Gets or sets a boolean indicating if the slider should be vertical or horizontal */
+                get: function () {
+                    return this._isVertical;
+                },
+                set: function (value) {
+                    if (this._isVertical === value) {
+                        return;
+                    }
+                    this._isVertical = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(Slider.prototype, "isThumbCircle", {
             Object.defineProperty(Slider.prototype, "isThumbCircle", {
                 /** Gets or sets a boolean indicating if the thumb should be round or square */
                 /** Gets or sets a boolean indicating if the thumb should be round or square */
                 get: function () {
                 get: function () {
@@ -3914,24 +3930,80 @@ var BABYLON;
             Slider.prototype._getTypeName = function () {
             Slider.prototype._getTypeName = function () {
                 return "Slider";
                 return "Slider";
             };
             };
+            Slider.prototype._getThumbThickness = function (type, backgroundLength) {
+                var thumbThickness = 0;
+                switch (type) {
+                    case "circle":
+                        if (this._thumbWidth.isPixel) {
+                            thumbThickness = Math.max(this._thumbWidth.getValue(this._host), backgroundLength);
+                        }
+                        else {
+                            thumbThickness = backgroundLength * this._thumbWidth.getValue(this._host);
+                        }
+                        break;
+                    case "rectangle":
+                        if (this._thumbWidth.isPixel) {
+                            thumbThickness = Math.min(this._thumbWidth.getValue(this._host), backgroundLength);
+                        }
+                        else {
+                            thumbThickness = backgroundLength * this._thumbWidth.getValue(this._host);
+                        }
+                }
+                return thumbThickness;
+            };
             Slider.prototype._draw = function (parentMeasure, context) {
             Slider.prototype._draw = function (parentMeasure, context) {
                 context.save();
                 context.save();
                 this._applyStates(context);
                 this._applyStates(context);
                 if (this._processMeasures(parentMeasure, context)) {
                 if (this._processMeasures(parentMeasure, context)) {
                     // Main bar
                     // Main bar
-                    var effectiveThumbWidth;
-                    var effectiveBarOffset;
-                    if (this._thumbWidth.isPixel) {
-                        effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.width);
+                    var effectiveBarOffset = 0;
+                    var type = this.isThumbCircle ? "circle" : "rectangle";
+                    var left = this._currentMeasure.left;
+                    var top = this._currentMeasure.top;
+                    var width = this._currentMeasure.width;
+                    var height = this._currentMeasure.height;
+                    var backgroundBoxLength = Math.max(this._currentMeasure.width, this._currentMeasure.height);
+                    var backgroundBoxThickness = Math.min(this._currentMeasure.width, this._currentMeasure.height);
+                    var effectiveThumbThickness = this._getThumbThickness(type, backgroundBoxThickness);
+                    backgroundBoxLength -= effectiveThumbThickness;
+                    var radius = 0;
+                    //throw error when height is less than width for vertical slider
+                    if ((this._isVertical && this._currentMeasure.height < this._currentMeasure.width)) {
+                        console.error("Height should be greater than width");
+                        return;
+                    }
+                    if (this._barOffset.isPixel) {
+                        effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), backgroundBoxThickness);
                     }
                     }
                     else {
                     else {
-                        effectiveThumbWidth = this._currentMeasure.width * this._thumbWidth.getValue(this._host);
+                        effectiveBarOffset = backgroundBoxThickness * this._barOffset.getValue(this._host);
                     }
                     }
-                    if (this._barOffset.isPixel) {
-                        effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), this._currentMeasure.height);
+                    backgroundBoxThickness -= (effectiveBarOffset * 2);
+                    if (this._isVertical) {
+                        left += effectiveBarOffset;
+                        if (!this.isThumbClamped) {
+                            top += (effectiveThumbThickness / 2);
+                        }
+                        height = backgroundBoxLength;
+                        width = backgroundBoxThickness;
                     }
                     }
                     else {
                     else {
-                        effectiveBarOffset = this._currentMeasure.height * this._barOffset.getValue(this._host);
+                        top += effectiveBarOffset;
+                        if (!this.isThumbClamped) {
+                            left += (effectiveThumbThickness / 2);
+                        }
+                        height = backgroundBoxThickness;
+                        width = backgroundBoxLength;
+                    }
+                    if (this.isThumbClamped && this.isThumbCircle) {
+                        if (this._isVertical)
+                            top += (effectiveThumbThickness / 2);
+                        else
+                            left += (effectiveThumbThickness / 2);
+                        radius = backgroundBoxThickness / 2;
+                    }
+                    else {
+                        radius = (effectiveThumbThickness - effectiveBarOffset) / 2;
                     }
                     }
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowColor = this.shadowColor;
                         context.shadowColor = this.shadowColor;
@@ -3939,15 +4011,39 @@ var BABYLON;
                         context.shadowOffsetX = this.shadowOffsetX;
                         context.shadowOffsetX = this.shadowOffsetX;
                         context.shadowOffsetY = this.shadowOffsetY;
                         context.shadowOffsetY = this.shadowOffsetY;
                     }
                     }
-                    var left = this._currentMeasure.left;
-                    var width = this._currentMeasure.width - effectiveThumbWidth;
-                    var thumbPosition = ((this._value - this._minimum) / (this._maximum - this._minimum)) * width;
+                    var thumbPosition = (this._isVertical) ? ((this._maximum - this._value) / (this._maximum - this._minimum)) * backgroundBoxLength : ((this._value - this._minimum) / (this._maximum - this._minimum)) * backgroundBoxLength;
                     context.fillStyle = this._background;
                     context.fillStyle = this._background;
-                    if (this.isThumbClamped) {
-                        context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width + effectiveThumbWidth, this._currentMeasure.height - effectiveBarOffset * 2);
+                    if (this._isVertical) {
+                        if (this.isThumbClamped) {
+                            if (this.isThumbCircle) {
+                                context.beginPath();
+                                context.arc(left + backgroundBoxThickness / 2, top, radius, Math.PI, 2 * Math.PI);
+                                context.fill();
+                                context.fillRect(left, top, width, height);
+                            }
+                            else {
+                                context.fillRect(left, top, width, height + effectiveThumbThickness);
+                            }
+                        }
+                        else {
+                            context.fillRect(left, top, width, height);
+                        }
                     }
                     }
                     else {
                     else {
-                        context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
+                        if (this.isThumbClamped) {
+                            if (this.isThumbCircle) {
+                                context.beginPath();
+                                context.arc(left + backgroundBoxLength, top + (backgroundBoxThickness / 2), radius, 0, 2 * Math.PI);
+                                context.fill();
+                                context.fillRect(left, top, width, height);
+                            }
+                            else {
+                                context.fillRect(left, top, width + effectiveThumbThickness, height);
+                            }
+                        }
+                        else {
+                            context.fillRect(left, top, width, height);
+                        }
                     }
                     }
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowBlur = 0;
                         context.shadowBlur = 0;
@@ -3955,11 +4051,37 @@ var BABYLON;
                         context.shadowOffsetY = 0;
                         context.shadowOffsetY = 0;
                     }
                     }
                     context.fillStyle = this.color;
                     context.fillStyle = this.color;
-                    if (this.isThumbClamped) {
-                        context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
+                    if (this._isVertical) {
+                        if (this.isThumbClamped) {
+                            if (this.isThumbCircle) {
+                                context.beginPath();
+                                context.arc(left + backgroundBoxThickness / 2, top + backgroundBoxLength, radius, 0, 2 * Math.PI);
+                                context.fill();
+                                context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
+                            }
+                            else {
+                                context.fillRect(left, top + thumbPosition, width, this._currentMeasure.height - thumbPosition);
+                            }
+                        }
+                        else {
+                            context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
+                        }
                     }
                     }
                     else {
                     else {
-                        context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
+                        if (this.isThumbClamped) {
+                            if (this.isThumbCircle) {
+                                context.beginPath();
+                                context.arc(left, top + backgroundBoxThickness / 2, radius, 0, 2 * Math.PI);
+                                context.fill();
+                                context.fillRect(left, top, thumbPosition, height);
+                            }
+                            else {
+                                context.fillRect(left, top, thumbPosition, height);
+                            }
+                        }
+                        else {
+                            context.fillRect(left, top, thumbPosition, height);
+                        }
                     }
                     }
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowColor = this.shadowColor;
                         context.shadowColor = this.shadowColor;
@@ -3969,7 +4091,12 @@ var BABYLON;
                     }
                     }
                     if (this._isThumbCircle) {
                     if (this._isThumbCircle) {
                         context.beginPath();
                         context.beginPath();
-                        context.arc(left + thumbPosition + (effectiveThumbWidth / 2), this._currentMeasure.top + this._currentMeasure.height / 2, effectiveThumbWidth / 2, 0, 2 * Math.PI);
+                        if (this._isVertical) {
+                            context.arc(left + backgroundBoxThickness / 2, top + thumbPosition, radius, 0, 2 * Math.PI);
+                        }
+                        else {
+                            context.arc(left + thumbPosition, top + (backgroundBoxThickness / 2), radius, 0, 2 * Math.PI);
+                        }
                         context.fill();
                         context.fill();
                         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                             context.shadowBlur = 0;
                             context.shadowBlur = 0;
@@ -3980,14 +4107,24 @@ var BABYLON;
                         context.stroke();
                         context.stroke();
                     }
                     }
                     else {
                     else {
-                        context.fillRect(left + thumbPosition, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
+                        if (this._isVertical) {
+                            context.fillRect(left - effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, effectiveThumbThickness);
+                        }
+                        else {
+                            context.fillRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, effectiveThumbThickness, this._currentMeasure.height);
+                        }
                         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                             context.shadowBlur = 0;
                             context.shadowBlur = 0;
                             context.shadowOffsetX = 0;
                             context.shadowOffsetX = 0;
                             context.shadowOffsetY = 0;
                             context.shadowOffsetY = 0;
                         }
                         }
                         context.strokeStyle = this._borderColor;
                         context.strokeStyle = this._borderColor;
-                        context.strokeRect(left + thumbPosition, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
+                        if (this._isVertical) {
+                            context.strokeRect(left - effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, effectiveThumbThickness);
+                        }
+                        else {
+                            context.strokeRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, effectiveThumbThickness, this._currentMeasure.height);
+                        }
                     }
                     }
                 }
                 }
                 context.restore();
                 context.restore();
@@ -3996,8 +4133,14 @@ var BABYLON;
                 if (this.rotation != 0) {
                 if (this.rotation != 0) {
                     this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
                     this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
                     x = this._transformedPosition.x;
                     x = this._transformedPosition.x;
+                    y = this._transformedPosition.y;
+                }
+                if (this._isVertical) {
+                    this.value = this._minimum + (1 - ((y - this._currentMeasure.top) / this._currentMeasure.height)) * (this._maximum - this._minimum);
+                }
+                else {
+                    this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
                 }
                 }
-                this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
             };
             };
             Slider.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
             Slider.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
                 if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {
                 if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {

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


+ 4 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -1200,6 +1200,7 @@ declare module BABYLON.GUI {
         private _minimum;
         private _minimum;
         private _maximum;
         private _maximum;
         private _value;
         private _value;
+        private _isVertical;
         private _background;
         private _background;
         private _borderColor;
         private _borderColor;
         private _barOffset;
         private _barOffset;
@@ -1225,6 +1226,8 @@ declare module BABYLON.GUI {
         maximum: number;
         maximum: number;
         /** Gets or sets current value */
         /** Gets or sets current value */
         value: number;
         value: number;
+        /**Gets or sets a boolean indicating if the slider should be vertical or horizontal */
+        isVertical: boolean;
         /** Gets or sets a boolean indicating if the thumb should be round or square */
         /** Gets or sets a boolean indicating if the thumb should be round or square */
         isThumbCircle: boolean;
         isThumbCircle: boolean;
         /** Gets or sets a value indicating if the thumb can go over main bar extends */
         /** Gets or sets a value indicating if the thumb can go over main bar extends */
@@ -1235,6 +1238,7 @@ declare module BABYLON.GUI {
          */
          */
         constructor(name?: string | undefined);
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
         protected _getTypeName(): string;
+        protected _getThumbThickness(type: string, backgroundLength: number): number;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         private _pointerIsDown;
         private _pointerIsDown;
         private _updateValueFromPointer(x, y);
         private _updateValueFromPointer(x, y);

+ 9 - 4
dist/preview release/loaders/babylon.objFileLoader.d.ts

@@ -28,7 +28,7 @@ declare module BABYLON {
          */
          */
         private static _getTexture(rootUrl, value, scene);
         private static _getTexture(rootUrl, value, scene);
     }
     }
-    class OBJFileLoader implements ISceneLoaderPlugin {
+    class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
         static OPTIMIZE_WITH_UV: boolean;
         static INVERT_Y: boolean;
         static INVERT_Y: boolean;
         name: string;
         name: string;
@@ -57,9 +57,14 @@ declare module BABYLON {
          * @private
          * @private
          */
          */
         private _loadMTL(url, rootUrl, onSuccess);
         private _loadMTL(url, rootUrl, onSuccess);
-        importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean;
-        load(scene: Scene, data: string, rootUrl: string): boolean;
-        loadAssetContainer(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): AssetContainer;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+            meshes: AbstractMesh[];
+            particleSystems: ParticleSystem[];
+            skeletons: Skeleton[];
+            animationGroups: AnimationGroup[];
+        }>;
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
          * Each mesh contains all information given by the OBJ and the MTL file.

+ 60 - 44
dist/preview release/loaders/babylon.objFileLoader.js

@@ -249,26 +249,30 @@ var BABYLON;
             // Loads through the babylon tools to allow fileInput search.
             // Loads through the babylon tools to allow fileInput search.
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
         };
         };
-        OBJFileLoader.prototype.importMesh = function (meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons) {
+        OBJFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
             //get the meshes from OBJ file
             //get the meshes from OBJ file
-            var loadedMeshes = this._parseSolid(meshesNames, scene, data, rootUrl);
-            //Push meshes from OBJ file into the variable mesh of this function
-            if (meshes) {
-                loadedMeshes.forEach(function (mesh) {
-                    meshes.push(mesh);
-                });
-            }
-            return true;
+            return this._parseSolid(meshesNames, scene, data, rootUrl).then(function (meshes) {
+                return {
+                    meshes: meshes,
+                    particleSystems: [],
+                    skeletons: [],
+                    animationGroups: []
+                };
+            });
         };
         };
-        OBJFileLoader.prototype.load = function (scene, data, rootUrl) {
+        OBJFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
             //Get the 3D model
             //Get the 3D model
-            return this.importMesh(null, scene, data, rootUrl, null, null, null);
+            return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(function () {
+                // return void
+            });
         };
         };
-        OBJFileLoader.prototype.loadAssetContainer = function (scene, data, rootUrl, onError) {
-            var container = new BABYLON.AssetContainer(scene);
-            this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
-            container.removeAllFromScene();
-            return container;
+        OBJFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+            return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
+                var container = new BABYLON.AssetContainer(scene);
+                result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });
+                container.removeAllFromScene();
+                return container;
+            });
         };
         };
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
@@ -283,6 +287,7 @@ var BABYLON;
          * @private
          * @private
          */
          */
         OBJFileLoader.prototype._parseSolid = function (meshesNames, scene, data, rootUrl) {
         OBJFileLoader.prototype._parseSolid = function (meshesNames, scene, data, rootUrl) {
+            var _this = this;
             var positions = []; //values for the positions of vertices
             var positions = []; //values for the positions of vertices
             var normals = []; //Values for the normals
             var normals = []; //Values for the normals
             var uvs = []; //Values for the textures
             var uvs = []; //Values for the textures
@@ -752,42 +757,53 @@ var BABYLON;
                 //Push the mesh into an array
                 //Push the mesh into an array
                 babylonMeshesArray.push(babylonMesh);
                 babylonMeshesArray.push(babylonMesh);
             }
             }
+            var mtlPromises = [];
             //load the materials
             //load the materials
             //Check if we have a file to load
             //Check if we have a file to load
             if (fileToLoad !== "") {
             if (fileToLoad !== "") {
                 //Load the file synchronously
                 //Load the file synchronously
-                this._loadMTL(fileToLoad, rootUrl, function (dataLoaded) {
-                    //Create materials thanks MTLLoader function
-                    materialsFromMTLFile.parseMTL(scene, dataLoaded, rootUrl);
-                    //Look at each material loaded in the mtl file
-                    for (var n = 0; n < materialsFromMTLFile.materials.length; n++) {
-                        //Three variables to get all meshes with the same material
-                        var startIndex = 0;
-                        var _indices = [];
-                        var _index;
-                        //The material from MTL file is used in the meshes loaded
-                        //Push the indice in an array
-                        //Check if the material is not used for another mesh
-                        while ((_index = materialToUse.indexOf(materialsFromMTLFile.materials[n].name, startIndex)) > -1) {
-                            _indices.push(_index);
-                            startIndex = _index + 1;
-                        }
-                        //If the material is not used dispose it
-                        if (_index == -1 && _indices.length == 0) {
-                            //If the material is not needed, remove it
-                            materialsFromMTLFile.materials[n].dispose();
-                        }
-                        else {
-                            for (var o = 0; o < _indices.length; o++) {
-                                //Apply the material to the BABYLON.Mesh for each mesh with the material
-                                babylonMeshesArray[_indices[o]].material = materialsFromMTLFile.materials[n];
+                mtlPromises.push(new Promise(function (resolve, reject) {
+                    _this._loadMTL(fileToLoad, rootUrl, function (dataLoaded) {
+                        try {
+                            //Create materials thanks MTLLoader function
+                            materialsFromMTLFile.parseMTL(scene, dataLoaded, rootUrl);
+                            //Look at each material loaded in the mtl file
+                            for (var n = 0; n < materialsFromMTLFile.materials.length; n++) {
+                                //Three variables to get all meshes with the same material
+                                var startIndex = 0;
+                                var _indices = [];
+                                var _index;
+                                //The material from MTL file is used in the meshes loaded
+                                //Push the indice in an array
+                                //Check if the material is not used for another mesh
+                                while ((_index = materialToUse.indexOf(materialsFromMTLFile.materials[n].name, startIndex)) > -1) {
+                                    _indices.push(_index);
+                                    startIndex = _index + 1;
+                                }
+                                //If the material is not used dispose it
+                                if (_index == -1 && _indices.length == 0) {
+                                    //If the material is not needed, remove it
+                                    materialsFromMTLFile.materials[n].dispose();
+                                }
+                                else {
+                                    for (var o = 0; o < _indices.length; o++) {
+                                        //Apply the material to the BABYLON.Mesh for each mesh with the material
+                                        babylonMeshesArray[_indices[o]].material = materialsFromMTLFile.materials[n];
+                                    }
+                                }
                             }
                             }
+                            resolve();
                         }
                         }
-                    }
-                });
+                        catch (e) {
+                            reject(e);
+                        }
+                    });
+                }));
             }
             }
             //Return an array with all BABYLON.Mesh
             //Return an array with all BABYLON.Mesh
-            return babylonMeshesArray;
+            return Promise.all(mtlPromises).then(function () {
+                return babylonMeshesArray;
+            });
         };
         };
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
         OBJFileLoader.INVERT_Y = false;
         OBJFileLoader.INVERT_Y = false;

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 9 - 4
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -46,7 +46,7 @@ declare module BABYLON {
          */
          */
         private static _getTexture(rootUrl, value, scene);
         private static _getTexture(rootUrl, value, scene);
     }
     }
-    class OBJFileLoader implements ISceneLoaderPlugin {
+    class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
         static OPTIMIZE_WITH_UV: boolean;
         static INVERT_Y: boolean;
         static INVERT_Y: boolean;
         name: string;
         name: string;
@@ -75,9 +75,14 @@ declare module BABYLON {
          * @private
          * @private
          */
          */
         private _loadMTL(url, rootUrl, onSuccess);
         private _loadMTL(url, rootUrl, onSuccess);
-        importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean;
-        load(scene: Scene, data: string, rootUrl: string): boolean;
-        loadAssetContainer(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): AssetContainer;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+            meshes: AbstractMesh[];
+            particleSystems: ParticleSystem[];
+            skeletons: Skeleton[];
+            animationGroups: AnimationGroup[];
+        }>;
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
          * Each mesh contains all information given by the OBJ and the MTL file.

+ 60 - 44
dist/preview release/loaders/babylonjs.loaders.js

@@ -448,26 +448,30 @@ var BABYLON;
             // Loads through the babylon tools to allow fileInput search.
             // Loads through the babylon tools to allow fileInput search.
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
         };
         };
-        OBJFileLoader.prototype.importMesh = function (meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons) {
+        OBJFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
             //get the meshes from OBJ file
             //get the meshes from OBJ file
-            var loadedMeshes = this._parseSolid(meshesNames, scene, data, rootUrl);
-            //Push meshes from OBJ file into the variable mesh of this function
-            if (meshes) {
-                loadedMeshes.forEach(function (mesh) {
-                    meshes.push(mesh);
-                });
-            }
-            return true;
+            return this._parseSolid(meshesNames, scene, data, rootUrl).then(function (meshes) {
+                return {
+                    meshes: meshes,
+                    particleSystems: [],
+                    skeletons: [],
+                    animationGroups: []
+                };
+            });
         };
         };
-        OBJFileLoader.prototype.load = function (scene, data, rootUrl) {
+        OBJFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
             //Get the 3D model
             //Get the 3D model
-            return this.importMesh(null, scene, data, rootUrl, null, null, null);
+            return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(function () {
+                // return void
+            });
         };
         };
-        OBJFileLoader.prototype.loadAssetContainer = function (scene, data, rootUrl, onError) {
-            var container = new BABYLON.AssetContainer(scene);
-            this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
-            container.removeAllFromScene();
-            return container;
+        OBJFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+            return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
+                var container = new BABYLON.AssetContainer(scene);
+                result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });
+                container.removeAllFromScene();
+                return container;
+            });
         };
         };
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
@@ -482,6 +486,7 @@ var BABYLON;
          * @private
          * @private
          */
          */
         OBJFileLoader.prototype._parseSolid = function (meshesNames, scene, data, rootUrl) {
         OBJFileLoader.prototype._parseSolid = function (meshesNames, scene, data, rootUrl) {
+            var _this = this;
             var positions = []; //values for the positions of vertices
             var positions = []; //values for the positions of vertices
             var normals = []; //Values for the normals
             var normals = []; //Values for the normals
             var uvs = []; //Values for the textures
             var uvs = []; //Values for the textures
@@ -951,42 +956,53 @@ var BABYLON;
                 //Push the mesh into an array
                 //Push the mesh into an array
                 babylonMeshesArray.push(babylonMesh);
                 babylonMeshesArray.push(babylonMesh);
             }
             }
+            var mtlPromises = [];
             //load the materials
             //load the materials
             //Check if we have a file to load
             //Check if we have a file to load
             if (fileToLoad !== "") {
             if (fileToLoad !== "") {
                 //Load the file synchronously
                 //Load the file synchronously
-                this._loadMTL(fileToLoad, rootUrl, function (dataLoaded) {
-                    //Create materials thanks MTLLoader function
-                    materialsFromMTLFile.parseMTL(scene, dataLoaded, rootUrl);
-                    //Look at each material loaded in the mtl file
-                    for (var n = 0; n < materialsFromMTLFile.materials.length; n++) {
-                        //Three variables to get all meshes with the same material
-                        var startIndex = 0;
-                        var _indices = [];
-                        var _index;
-                        //The material from MTL file is used in the meshes loaded
-                        //Push the indice in an array
-                        //Check if the material is not used for another mesh
-                        while ((_index = materialToUse.indexOf(materialsFromMTLFile.materials[n].name, startIndex)) > -1) {
-                            _indices.push(_index);
-                            startIndex = _index + 1;
-                        }
-                        //If the material is not used dispose it
-                        if (_index == -1 && _indices.length == 0) {
-                            //If the material is not needed, remove it
-                            materialsFromMTLFile.materials[n].dispose();
-                        }
-                        else {
-                            for (var o = 0; o < _indices.length; o++) {
-                                //Apply the material to the BABYLON.Mesh for each mesh with the material
-                                babylonMeshesArray[_indices[o]].material = materialsFromMTLFile.materials[n];
+                mtlPromises.push(new Promise(function (resolve, reject) {
+                    _this._loadMTL(fileToLoad, rootUrl, function (dataLoaded) {
+                        try {
+                            //Create materials thanks MTLLoader function
+                            materialsFromMTLFile.parseMTL(scene, dataLoaded, rootUrl);
+                            //Look at each material loaded in the mtl file
+                            for (var n = 0; n < materialsFromMTLFile.materials.length; n++) {
+                                //Three variables to get all meshes with the same material
+                                var startIndex = 0;
+                                var _indices = [];
+                                var _index;
+                                //The material from MTL file is used in the meshes loaded
+                                //Push the indice in an array
+                                //Check if the material is not used for another mesh
+                                while ((_index = materialToUse.indexOf(materialsFromMTLFile.materials[n].name, startIndex)) > -1) {
+                                    _indices.push(_index);
+                                    startIndex = _index + 1;
+                                }
+                                //If the material is not used dispose it
+                                if (_index == -1 && _indices.length == 0) {
+                                    //If the material is not needed, remove it
+                                    materialsFromMTLFile.materials[n].dispose();
+                                }
+                                else {
+                                    for (var o = 0; o < _indices.length; o++) {
+                                        //Apply the material to the BABYLON.Mesh for each mesh with the material
+                                        babylonMeshesArray[_indices[o]].material = materialsFromMTLFile.materials[n];
+                                    }
+                                }
                             }
                             }
+                            resolve();
                         }
                         }
-                    }
-                });
+                        catch (e) {
+                            reject(e);
+                        }
+                    });
+                }));
             }
             }
             //Return an array with all BABYLON.Mesh
             //Return an array with all BABYLON.Mesh
-            return babylonMeshesArray;
+            return Promise.all(mtlPromises).then(function () {
+                return babylonMeshesArray;
+            });
         };
         };
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
         OBJFileLoader.INVERT_Y = false;
         OBJFileLoader.INVERT_Y = false;

File diff suppressed because it is too large
+ 4 - 4
dist/preview release/loaders/babylonjs.loaders.min.js


+ 9 - 4
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -53,7 +53,7 @@ declare module BABYLON {
          */
          */
         private static _getTexture(rootUrl, value, scene);
         private static _getTexture(rootUrl, value, scene);
     }
     }
-    class OBJFileLoader implements ISceneLoaderPlugin {
+    class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
         static OPTIMIZE_WITH_UV: boolean;
         static INVERT_Y: boolean;
         static INVERT_Y: boolean;
         name: string;
         name: string;
@@ -82,9 +82,14 @@ declare module BABYLON {
          * @private
          * @private
          */
          */
         private _loadMTL(url, rootUrl, onSuccess);
         private _loadMTL(url, rootUrl, onSuccess);
-        importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean;
-        load(scene: Scene, data: string, rootUrl: string): boolean;
-        loadAssetContainer(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): AssetContainer;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+            meshes: AbstractMesh[];
+            particleSystems: ParticleSystem[];
+            skeletons: Skeleton[];
+            animationGroups: AnimationGroup[];
+        }>;
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
          * Each mesh contains all information given by the OBJ and the MTL file.

+ 69 - 82
dist/preview release/serializers/babylon.glTF2Serializer.d.ts

@@ -49,86 +49,99 @@ declare module BABYLON.GLTF2 {
         /**
         /**
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          */
          */
-        private bufferViews;
+        private _bufferViews;
         /**
         /**
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          */
          */
-        private accessors;
+        private _accessors;
         /**
         /**
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          */
          */
-        private nodes;
+        private _nodes;
         /**
         /**
          * Stores the glTF asset information, which represents the glTF version and this file generator
          * Stores the glTF asset information, which represents the glTF version and this file generator
          */
          */
-        private asset;
+        private _asset;
         /**
         /**
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          */
          */
-        private scenes;
+        private _scenes;
         /**
         /**
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          */
          */
-        private meshes;
+        private _meshes;
         /**
         /**
          * Stores all the generated material information, which represents the appearance of each primitive
          * Stores all the generated material information, which represents the appearance of each primitive
          */
          */
-        private materials;
-        private materialMap;
+        _materials: IMaterial[];
+        _materialMap: {
+            [materialID: number]: number;
+        };
         /**
         /**
          * Stores all the generated texture information, which is referenced by glTF materials
          * Stores all the generated texture information, which is referenced by glTF materials
          */
          */
-        private textures;
+        _textures: ITexture[];
         /**
         /**
          * Stores all the generated image information, which is referenced by glTF textures
          * Stores all the generated image information, which is referenced by glTF textures
          */
          */
-        private images;
+        _images: IImage[];
         /**
         /**
          * Stores all the texture samplers
          * Stores all the texture samplers
          */
          */
-        private samplers;
+        _samplers: ISampler[];
         /**
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
          */
         /**
         /**
          * Stores the animations for glTF models
          * Stores the animations for glTF models
          */
          */
-        private animations;
+        private _animations;
         /**
         /**
          * Stores the total amount of bytes stored in the glTF buffer
          * Stores the total amount of bytes stored in the glTF buffer
          */
          */
-        private totalByteLength;
+        private _totalByteLength;
         /**
         /**
          * Stores a reference to the Babylon scene containing the source geometry and material information
          * Stores a reference to the Babylon scene containing the source geometry and material information
          */
          */
-        private babylonScene;
+        private _babylonScene;
         /**
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * Stores a map of the image data, where the key is the file name and the value
          * is the image data
          * is the image data
          */
          */
-        private imageData;
+        _imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        };
         /**
         /**
          * Stores a map of the unique id of a node to its index in the node array
          * Stores a map of the unique id of a node to its index in the node array
          */
          */
-        private nodeMap;
+        private _nodeMap;
         /**
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
          */
-        private convertToRightHandedSystem;
+        private _convertToRightHandedSystem;
         /**
         /**
          * Baked animation sample rate
          * Baked animation sample rate
          */
          */
-        private animationSampleRate;
+        private _animationSampleRate;
         /**
         /**
          * Callback which specifies if a transform node should be exported or not
          * Callback which specifies if a transform node should be exported or not
          */
          */
-        private shouldExportTransformNode;
+        private _shouldExportTransformNode;
+        private _localEngine;
+        private _glTFMaterialExporter;
         /**
         /**
          * Creates a glTF Exporter instance, which can accept optional exporter options
          * Creates a glTF Exporter instance, which can accept optional exporter options
          * @param babylonScene Babylon scene object
          * @param babylonScene Babylon scene object
          * @param options Options to modify the behavior of the exporter
          * @param options Options to modify the behavior of the exporter
          */
          */
         constructor(babylonScene: Scene, options?: IExportOptions);
         constructor(babylonScene: Scene, options?: IExportOptions);
+        /**
+         * Lazy load a local engine with premultiplied alpha set to false
+         */
+        _getLocalEngine(): Engine;
         private reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
         private reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
         /**
         /**
          * Reorders the vertex attribute data based on the primitive mode.  This is necessary when indices are not available and the winding order is
          * Reorders the vertex attribute data based on the primitive mode.  This is necessary when indices are not available and the winding order is
@@ -388,19 +401,28 @@ declare module BABYLON.GLTF2 {
      * Utility methods for working with glTF material conversion properties.  This class should only be used internally
      * Utility methods for working with glTF material conversion properties.  This class should only be used internally
      * @hidden
      * @hidden
      */
      */
-    class _GLTFMaterial {
+    class _GLTFMaterialExporter {
         /**
         /**
          * Represents the dielectric specular values for R, G and B
          * Represents the dielectric specular values for R, G and B
          */
          */
-        private static readonly _dielectricSpecular;
+        private static readonly _DielectricSpecular;
         /**
         /**
          * Allows the maximum specular power to be defined for material calculations
          * Allows the maximum specular power to be defined for material calculations
          */
          */
-        private static _maxSpecularPower;
+        private static readonly _MaxSpecularPower;
+        /**
+         * Mapping to store textures
+         */
+        private _textureMap;
         /**
         /**
          * Numeric tolerance value
          * Numeric tolerance value
          */
          */
-        private static _epsilon;
+        private static readonly _Epsilon;
+        /**
+         * Reference to the glTF Exporter
+         */
+        private _exporter;
+        constructor(exporter: _Exporter);
         /**
         /**
          * Specifies if two colors are approximately equal in value
          * Specifies if two colors are approximately equal in value
          * @param color1 first color to compare to
          * @param color1 first color to compare to
@@ -418,32 +440,25 @@ declare module BABYLON.GLTF2 {
          * @param imageData mapping of texture names to base64 textures
          * @param imageData mapping of texture names to base64 textures
          * @param hasTextureCoords specifies if texture coordinates are present on the material
          * @param hasTextureCoords specifies if texture coordinates are present on the material
          */
          */
-        static _ConvertMaterialsToGLTFAsync(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
+        _convertMaterialsToGLTFAsync(babylonMaterials: Material[], mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Makes a copy of the glTF material without the texture parameters
          * Makes a copy of the glTF material without the texture parameters
          * @param originalMaterial original glTF material
          * @param originalMaterial original glTF material
          * @returns glTF material without texture parameters
          * @returns glTF material without texture parameters
          */
          */
-        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        _stripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
         /**
          * Specifies if the material has any texture parameters present
          * Specifies if the material has any texture parameters present
          * @param material glTF Material
          * @param material glTF Material
          * @returns boolean specifying if texture parameters are present
          * @returns boolean specifying if texture parameters are present
          */
          */
-        static _HasTexturesPresent(material: IMaterial): boolean;
+        _hasTexturesPresent(material: IMaterial): boolean;
         /**
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material
          * @param babylonStandardMaterial
          * @param babylonStandardMaterial
          * @returns glTF Metallic Roughness Material representation
          * @returns glTF Metallic Roughness Material representation
          */
          */
-        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        _convertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
         /**
          * Computes the metallic factor
          * Computes the metallic factor
          * @param diffuse diffused value
          * @param diffuse diffused value
@@ -457,7 +472,7 @@ declare module BABYLON.GLTF2 {
          * @param babylonMaterial Babylon Material
          * @param babylonMaterial Babylon Material
          * @returns The Babylon alpha mode value
          * @returns The Babylon alpha mode value
          */
          */
-        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        _getAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
         /**
          * Converts a Babylon Standard Material to a glTF Material
          * Converts a Babylon Standard Material to a glTF Material
          * @param babylonStandardMaterial BJS Standard Material
          * @param babylonStandardMaterial BJS Standard Material
@@ -468,21 +483,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertStandardMaterialAsync(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
-        /**
-         *
-         * @param texture Texture with alpha to overwrite to one
-         * @param useAlpha Specifies if alpha should be preserved or not
-         * @returns Promise with texture
-         */
-        static _SetAlphaToOneAsync(texture: BaseTexture, useAlpha: boolean): Promise<BaseTexture>;
+        _convertStandardMaterialAsync(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMetalRoughMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMetalRoughMaterial BJS PBR Metallic Roughness Material
@@ -493,14 +494,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertPBRMetallicRoughnessMaterialAsync(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
+        _convertPBRMetallicRoughnessMaterialAsync(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Converts an image typed array buffer to a base64 image
          * Converts an image typed array buffer to a base64 image
          * @param buffer typed array buffer
          * @param buffer typed array buffer
@@ -509,7 +503,7 @@ declare module BABYLON.GLTF2 {
          * @param mimeType mimetype of the image
          * @param mimeType mimetype of the image
          * @returns base64 image string
          * @returns base64 image string
          */
          */
-        private static _CreateBase64FromCanvas(buffer, width, height, mimeType);
+        private _createBase64FromCanvasAsync(buffer, width, height, mimeType);
         /**
         /**
          * Generates a white texture based on the specified width and height
          * Generates a white texture based on the specified width and height
          * @param width width of the texture in pixels
          * @param width width of the texture in pixels
@@ -517,7 +511,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @param scene babylonjs scene
          * @returns white texture
          * @returns white texture
          */
          */
-        private static _CreateWhiteTexture(width, height, scene);
+        private _createWhiteTexture(width, height, scene);
         /**
         /**
          * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null
          * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null
          * @param texture1 first texture to resize
          * @param texture1 first texture to resize
@@ -525,7 +519,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @param scene babylonjs scene
          * @returns resized textures or null
          * @returns resized textures or null
          */
          */
-        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        private _resizeTexturesToSameDimensions(texture1, texture2, scene);
         /**
         /**
          * Convert Specular Glossiness Textures to Metallic Roughness
          * Convert Specular Glossiness Textures to Metallic Roughness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
@@ -536,25 +530,25 @@ declare module BABYLON.GLTF2 {
          * @param mimeType the mime type to use for the texture
          * @param mimeType the mime type to use for the texture
          * @returns pbr metallic roughness interface or null
          * @returns pbr metallic roughness interface or null
          */
          */
-        private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture, specularGlossinessTexture, factors, mimeType);
+        private _convertSpecularGlossinessTexturesToMetallicRoughnessAsync(diffuseTexture, specularGlossinessTexture, factors, mimeType);
         /**
         /**
          * Converts specular glossiness material properties to metallic roughness
          * Converts specular glossiness material properties to metallic roughness
          * @param specularGlossiness interface with specular glossiness material properties
          * @param specularGlossiness interface with specular glossiness material properties
          * @returns interface with metallic roughness material properties
          * @returns interface with metallic roughness material properties
          */
          */
-        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        private _convertSpecularGlossinessToMetallicRoughness(specularGlossiness);
         /**
         /**
          * Calculates the surface reflectance, independent of lighting conditions
          * Calculates the surface reflectance, independent of lighting conditions
          * @param color Color source to calculate brightness from
          * @param color Color source to calculate brightness from
          * @returns number representing the perceived brightness, or zero if color is undefined
          * @returns number representing the perceived brightness, or zero if color is undefined
          */
          */
-        private static _GetPerceivedBrightness(color);
+        private _getPerceivedBrightness(color);
         /**
         /**
          * Returns the maximum color component value
          * Returns the maximum color component value
          * @param color
          * @param color
          * @returns maximum color component value, or zero if color is null or undefined
          * @returns maximum color component value, or zero if color is null or undefined
          */
          */
-        private static _GetMaxComponent(color);
+        private _getMaxComponent(color);
         /**
         /**
          * Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
          * Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -566,10 +560,10 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          * @returns glTF PBR Metallic Roughness factors
          */
          */
-        private static _ConvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
-        private static _GetGLTFTextureSampler(texture);
-        private static _GetGLTFTextureWrapMode(wrapMode);
-        private static _GetGLTFTextureWrapModesSampler(texture);
+        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _getGLTFTextureSampler(texture);
+        private _getGLTFTextureWrapMode(wrapMode);
+        private _getGLTFTextureWrapModesSampler(texture);
         /**
         /**
          * Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
          * Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -581,7 +575,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          * @returns glTF PBR Metallic Roughness factors
          */
          */
-        private static _ConvertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -592,16 +586,9 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertPBRMaterialAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
-        private static SetMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, images, textures, samplers, materials, materialMap, imageData, hasTextureCoords);
-        private static GetPixelsFromTexture(babylonTexture);
+        _convertPBRMaterialAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
+        private setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
+        private getPixelsFromTexture(babylonTexture);
         /**
         /**
          * Extracts a texture from a Babylon texture into file data and glTF data
          * Extracts a texture from a Babylon texture into file data and glTF data
          * @param babylonTexture Babylon texture to extract
          * @param babylonTexture Babylon texture to extract
@@ -611,7 +598,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name and data
          * @param imageData map of image file name and data
          * @return glTF texture info, or null if the texture format is not supported
          * @return glTF texture info, or null if the texture format is not supported
          */
          */
-        private static _ExportTextureAsync(babylonTexture, mimeType, images, textures, samplers, imageData, useAlpha);
+        private _exportTextureAsync(babylonTexture, mimeType);
         /**
         /**
          * Builds a texture from base64 string
          * Builds a texture from base64 string
          * @param base64Texture base64 texture string
          * @param base64Texture base64 texture string
@@ -622,7 +609,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image data
          * @param imageData map of image data
          * @returns glTF texture info, or null if the texture format is not supported
          * @returns glTF texture info, or null if the texture format is not supported
          */
          */
-        private static _GetTextureInfoFromBase64(base64Texture, baseTextureName, mimeType, images, textures, texCoordIndex, samplerIndex, imageData);
+        private _getTextureInfoFromBase64(base64Texture, baseTextureName, mimeType, texCoordIndex, samplerIndex);
     }
     }
 }
 }
 
 

File diff suppressed because it is too large
+ 350 - 315
dist/preview release/serializers/babylon.glTF2Serializer.js


File diff suppressed because it is too large
+ 2 - 2
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 69 - 82
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -57,86 +57,99 @@ declare module BABYLON.GLTF2 {
         /**
         /**
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          */
          */
-        private bufferViews;
+        private _bufferViews;
         /**
         /**
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          */
          */
-        private accessors;
+        private _accessors;
         /**
         /**
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          */
          */
-        private nodes;
+        private _nodes;
         /**
         /**
          * Stores the glTF asset information, which represents the glTF version and this file generator
          * Stores the glTF asset information, which represents the glTF version and this file generator
          */
          */
-        private asset;
+        private _asset;
         /**
         /**
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          */
          */
-        private scenes;
+        private _scenes;
         /**
         /**
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          */
          */
-        private meshes;
+        private _meshes;
         /**
         /**
          * Stores all the generated material information, which represents the appearance of each primitive
          * Stores all the generated material information, which represents the appearance of each primitive
          */
          */
-        private materials;
-        private materialMap;
+        _materials: IMaterial[];
+        _materialMap: {
+            [materialID: number]: number;
+        };
         /**
         /**
          * Stores all the generated texture information, which is referenced by glTF materials
          * Stores all the generated texture information, which is referenced by glTF materials
          */
          */
-        private textures;
+        _textures: ITexture[];
         /**
         /**
          * Stores all the generated image information, which is referenced by glTF textures
          * Stores all the generated image information, which is referenced by glTF textures
          */
          */
-        private images;
+        _images: IImage[];
         /**
         /**
          * Stores all the texture samplers
          * Stores all the texture samplers
          */
          */
-        private samplers;
+        _samplers: ISampler[];
         /**
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
          */
         /**
         /**
          * Stores the animations for glTF models
          * Stores the animations for glTF models
          */
          */
-        private animations;
+        private _animations;
         /**
         /**
          * Stores the total amount of bytes stored in the glTF buffer
          * Stores the total amount of bytes stored in the glTF buffer
          */
          */
-        private totalByteLength;
+        private _totalByteLength;
         /**
         /**
          * Stores a reference to the Babylon scene containing the source geometry and material information
          * Stores a reference to the Babylon scene containing the source geometry and material information
          */
          */
-        private babylonScene;
+        private _babylonScene;
         /**
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * Stores a map of the image data, where the key is the file name and the value
          * is the image data
          * is the image data
          */
          */
-        private imageData;
+        _imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        };
         /**
         /**
          * Stores a map of the unique id of a node to its index in the node array
          * Stores a map of the unique id of a node to its index in the node array
          */
          */
-        private nodeMap;
+        private _nodeMap;
         /**
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
          */
-        private convertToRightHandedSystem;
+        private _convertToRightHandedSystem;
         /**
         /**
          * Baked animation sample rate
          * Baked animation sample rate
          */
          */
-        private animationSampleRate;
+        private _animationSampleRate;
         /**
         /**
          * Callback which specifies if a transform node should be exported or not
          * Callback which specifies if a transform node should be exported or not
          */
          */
-        private shouldExportTransformNode;
+        private _shouldExportTransformNode;
+        private _localEngine;
+        private _glTFMaterialExporter;
         /**
         /**
          * Creates a glTF Exporter instance, which can accept optional exporter options
          * Creates a glTF Exporter instance, which can accept optional exporter options
          * @param babylonScene Babylon scene object
          * @param babylonScene Babylon scene object
          * @param options Options to modify the behavior of the exporter
          * @param options Options to modify the behavior of the exporter
          */
          */
         constructor(babylonScene: Scene, options?: IExportOptions);
         constructor(babylonScene: Scene, options?: IExportOptions);
+        /**
+         * Lazy load a local engine with premultiplied alpha set to false
+         */
+        _getLocalEngine(): Engine;
         private reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
         private reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
         /**
         /**
          * Reorders the vertex attribute data based on the primitive mode.  This is necessary when indices are not available and the winding order is
          * Reorders the vertex attribute data based on the primitive mode.  This is necessary when indices are not available and the winding order is
@@ -396,19 +409,28 @@ declare module BABYLON.GLTF2 {
      * Utility methods for working with glTF material conversion properties.  This class should only be used internally
      * Utility methods for working with glTF material conversion properties.  This class should only be used internally
      * @hidden
      * @hidden
      */
      */
-    class _GLTFMaterial {
+    class _GLTFMaterialExporter {
         /**
         /**
          * Represents the dielectric specular values for R, G and B
          * Represents the dielectric specular values for R, G and B
          */
          */
-        private static readonly _dielectricSpecular;
+        private static readonly _DielectricSpecular;
         /**
         /**
          * Allows the maximum specular power to be defined for material calculations
          * Allows the maximum specular power to be defined for material calculations
          */
          */
-        private static _maxSpecularPower;
+        private static readonly _MaxSpecularPower;
+        /**
+         * Mapping to store textures
+         */
+        private _textureMap;
         /**
         /**
          * Numeric tolerance value
          * Numeric tolerance value
          */
          */
-        private static _epsilon;
+        private static readonly _Epsilon;
+        /**
+         * Reference to the glTF Exporter
+         */
+        private _exporter;
+        constructor(exporter: _Exporter);
         /**
         /**
          * Specifies if two colors are approximately equal in value
          * Specifies if two colors are approximately equal in value
          * @param color1 first color to compare to
          * @param color1 first color to compare to
@@ -426,32 +448,25 @@ declare module BABYLON.GLTF2 {
          * @param imageData mapping of texture names to base64 textures
          * @param imageData mapping of texture names to base64 textures
          * @param hasTextureCoords specifies if texture coordinates are present on the material
          * @param hasTextureCoords specifies if texture coordinates are present on the material
          */
          */
-        static _ConvertMaterialsToGLTFAsync(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
+        _convertMaterialsToGLTFAsync(babylonMaterials: Material[], mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Makes a copy of the glTF material without the texture parameters
          * Makes a copy of the glTF material without the texture parameters
          * @param originalMaterial original glTF material
          * @param originalMaterial original glTF material
          * @returns glTF material without texture parameters
          * @returns glTF material without texture parameters
          */
          */
-        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        _stripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
         /**
          * Specifies if the material has any texture parameters present
          * Specifies if the material has any texture parameters present
          * @param material glTF Material
          * @param material glTF Material
          * @returns boolean specifying if texture parameters are present
          * @returns boolean specifying if texture parameters are present
          */
          */
-        static _HasTexturesPresent(material: IMaterial): boolean;
+        _hasTexturesPresent(material: IMaterial): boolean;
         /**
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material
          * @param babylonStandardMaterial
          * @param babylonStandardMaterial
          * @returns glTF Metallic Roughness Material representation
          * @returns glTF Metallic Roughness Material representation
          */
          */
-        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        _convertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
         /**
          * Computes the metallic factor
          * Computes the metallic factor
          * @param diffuse diffused value
          * @param diffuse diffused value
@@ -465,7 +480,7 @@ declare module BABYLON.GLTF2 {
          * @param babylonMaterial Babylon Material
          * @param babylonMaterial Babylon Material
          * @returns The Babylon alpha mode value
          * @returns The Babylon alpha mode value
          */
          */
-        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        _getAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
         /**
          * Converts a Babylon Standard Material to a glTF Material
          * Converts a Babylon Standard Material to a glTF Material
          * @param babylonStandardMaterial BJS Standard Material
          * @param babylonStandardMaterial BJS Standard Material
@@ -476,21 +491,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertStandardMaterialAsync(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
-        /**
-         *
-         * @param texture Texture with alpha to overwrite to one
-         * @param useAlpha Specifies if alpha should be preserved or not
-         * @returns Promise with texture
-         */
-        static _SetAlphaToOneAsync(texture: BaseTexture, useAlpha: boolean): Promise<BaseTexture>;
+        _convertStandardMaterialAsync(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMetalRoughMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMetalRoughMaterial BJS PBR Metallic Roughness Material
@@ -501,14 +502,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertPBRMetallicRoughnessMaterialAsync(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
+        _convertPBRMetallicRoughnessMaterialAsync(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Converts an image typed array buffer to a base64 image
          * Converts an image typed array buffer to a base64 image
          * @param buffer typed array buffer
          * @param buffer typed array buffer
@@ -517,7 +511,7 @@ declare module BABYLON.GLTF2 {
          * @param mimeType mimetype of the image
          * @param mimeType mimetype of the image
          * @returns base64 image string
          * @returns base64 image string
          */
          */
-        private static _CreateBase64FromCanvas(buffer, width, height, mimeType);
+        private _createBase64FromCanvasAsync(buffer, width, height, mimeType);
         /**
         /**
          * Generates a white texture based on the specified width and height
          * Generates a white texture based on the specified width and height
          * @param width width of the texture in pixels
          * @param width width of the texture in pixels
@@ -525,7 +519,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @param scene babylonjs scene
          * @returns white texture
          * @returns white texture
          */
          */
-        private static _CreateWhiteTexture(width, height, scene);
+        private _createWhiteTexture(width, height, scene);
         /**
         /**
          * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null
          * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null
          * @param texture1 first texture to resize
          * @param texture1 first texture to resize
@@ -533,7 +527,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @param scene babylonjs scene
          * @returns resized textures or null
          * @returns resized textures or null
          */
          */
-        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        private _resizeTexturesToSameDimensions(texture1, texture2, scene);
         /**
         /**
          * Convert Specular Glossiness Textures to Metallic Roughness
          * Convert Specular Glossiness Textures to Metallic Roughness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
@@ -544,25 +538,25 @@ declare module BABYLON.GLTF2 {
          * @param mimeType the mime type to use for the texture
          * @param mimeType the mime type to use for the texture
          * @returns pbr metallic roughness interface or null
          * @returns pbr metallic roughness interface or null
          */
          */
-        private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture, specularGlossinessTexture, factors, mimeType);
+        private _convertSpecularGlossinessTexturesToMetallicRoughnessAsync(diffuseTexture, specularGlossinessTexture, factors, mimeType);
         /**
         /**
          * Converts specular glossiness material properties to metallic roughness
          * Converts specular glossiness material properties to metallic roughness
          * @param specularGlossiness interface with specular glossiness material properties
          * @param specularGlossiness interface with specular glossiness material properties
          * @returns interface with metallic roughness material properties
          * @returns interface with metallic roughness material properties
          */
          */
-        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        private _convertSpecularGlossinessToMetallicRoughness(specularGlossiness);
         /**
         /**
          * Calculates the surface reflectance, independent of lighting conditions
          * Calculates the surface reflectance, independent of lighting conditions
          * @param color Color source to calculate brightness from
          * @param color Color source to calculate brightness from
          * @returns number representing the perceived brightness, or zero if color is undefined
          * @returns number representing the perceived brightness, or zero if color is undefined
          */
          */
-        private static _GetPerceivedBrightness(color);
+        private _getPerceivedBrightness(color);
         /**
         /**
          * Returns the maximum color component value
          * Returns the maximum color component value
          * @param color
          * @param color
          * @returns maximum color component value, or zero if color is null or undefined
          * @returns maximum color component value, or zero if color is null or undefined
          */
          */
-        private static _GetMaxComponent(color);
+        private _getMaxComponent(color);
         /**
         /**
          * Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
          * Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -574,10 +568,10 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          * @returns glTF PBR Metallic Roughness factors
          */
          */
-        private static _ConvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
-        private static _GetGLTFTextureSampler(texture);
-        private static _GetGLTFTextureWrapMode(wrapMode);
-        private static _GetGLTFTextureWrapModesSampler(texture);
+        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _getGLTFTextureSampler(texture);
+        private _getGLTFTextureWrapMode(wrapMode);
+        private _getGLTFTextureWrapModesSampler(texture);
         /**
         /**
          * Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
          * Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -589,7 +583,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          * @returns glTF PBR Metallic Roughness factors
          */
          */
-        private static _ConvertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -600,16 +594,9 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertPBRMaterialAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
-        private static SetMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, images, textures, samplers, materials, materialMap, imageData, hasTextureCoords);
-        private static GetPixelsFromTexture(babylonTexture);
+        _convertPBRMaterialAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
+        private setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
+        private getPixelsFromTexture(babylonTexture);
         /**
         /**
          * Extracts a texture from a Babylon texture into file data and glTF data
          * Extracts a texture from a Babylon texture into file data and glTF data
          * @param babylonTexture Babylon texture to extract
          * @param babylonTexture Babylon texture to extract
@@ -619,7 +606,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name and data
          * @param imageData map of image file name and data
          * @return glTF texture info, or null if the texture format is not supported
          * @return glTF texture info, or null if the texture format is not supported
          */
          */
-        private static _ExportTextureAsync(babylonTexture, mimeType, images, textures, samplers, imageData, useAlpha);
+        private _exportTextureAsync(babylonTexture, mimeType);
         /**
         /**
          * Builds a texture from base64 string
          * Builds a texture from base64 string
          * @param base64Texture base64 texture string
          * @param base64Texture base64 texture string
@@ -630,7 +617,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image data
          * @param imageData map of image data
          * @returns glTF texture info, or null if the texture format is not supported
          * @returns glTF texture info, or null if the texture format is not supported
          */
          */
-        private static _GetTextureInfoFromBase64(base64Texture, baseTextureName, mimeType, images, textures, texCoordIndex, samplerIndex, imageData);
+        private _getTextureInfoFromBase64(base64Texture, baseTextureName, mimeType, texCoordIndex, samplerIndex);
     }
     }
 }
 }
 
 

File diff suppressed because it is too large
+ 350 - 315
dist/preview release/serializers/babylonjs.serializers.js


File diff suppressed because it is too large
+ 2 - 2
dist/preview release/serializers/babylonjs.serializers.min.js


+ 69 - 82
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -64,86 +64,99 @@ declare module BABYLON.GLTF2 {
         /**
         /**
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          */
          */
-        private bufferViews;
+        private _bufferViews;
         /**
         /**
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          */
          */
-        private accessors;
+        private _accessors;
         /**
         /**
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          */
          */
-        private nodes;
+        private _nodes;
         /**
         /**
          * Stores the glTF asset information, which represents the glTF version and this file generator
          * Stores the glTF asset information, which represents the glTF version and this file generator
          */
          */
-        private asset;
+        private _asset;
         /**
         /**
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          */
          */
-        private scenes;
+        private _scenes;
         /**
         /**
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          */
          */
-        private meshes;
+        private _meshes;
         /**
         /**
          * Stores all the generated material information, which represents the appearance of each primitive
          * Stores all the generated material information, which represents the appearance of each primitive
          */
          */
-        private materials;
-        private materialMap;
+        _materials: IMaterial[];
+        _materialMap: {
+            [materialID: number]: number;
+        };
         /**
         /**
          * Stores all the generated texture information, which is referenced by glTF materials
          * Stores all the generated texture information, which is referenced by glTF materials
          */
          */
-        private textures;
+        _textures: ITexture[];
         /**
         /**
          * Stores all the generated image information, which is referenced by glTF textures
          * Stores all the generated image information, which is referenced by glTF textures
          */
          */
-        private images;
+        _images: IImage[];
         /**
         /**
          * Stores all the texture samplers
          * Stores all the texture samplers
          */
          */
-        private samplers;
+        _samplers: ISampler[];
         /**
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
          */
         /**
         /**
          * Stores the animations for glTF models
          * Stores the animations for glTF models
          */
          */
-        private animations;
+        private _animations;
         /**
         /**
          * Stores the total amount of bytes stored in the glTF buffer
          * Stores the total amount of bytes stored in the glTF buffer
          */
          */
-        private totalByteLength;
+        private _totalByteLength;
         /**
         /**
          * Stores a reference to the Babylon scene containing the source geometry and material information
          * Stores a reference to the Babylon scene containing the source geometry and material information
          */
          */
-        private babylonScene;
+        private _babylonScene;
         /**
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * Stores a map of the image data, where the key is the file name and the value
          * is the image data
          * is the image data
          */
          */
-        private imageData;
+        _imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        };
         /**
         /**
          * Stores a map of the unique id of a node to its index in the node array
          * Stores a map of the unique id of a node to its index in the node array
          */
          */
-        private nodeMap;
+        private _nodeMap;
         /**
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
          */
-        private convertToRightHandedSystem;
+        private _convertToRightHandedSystem;
         /**
         /**
          * Baked animation sample rate
          * Baked animation sample rate
          */
          */
-        private animationSampleRate;
+        private _animationSampleRate;
         /**
         /**
          * Callback which specifies if a transform node should be exported or not
          * Callback which specifies if a transform node should be exported or not
          */
          */
-        private shouldExportTransformNode;
+        private _shouldExportTransformNode;
+        private _localEngine;
+        private _glTFMaterialExporter;
         /**
         /**
          * Creates a glTF Exporter instance, which can accept optional exporter options
          * Creates a glTF Exporter instance, which can accept optional exporter options
          * @param babylonScene Babylon scene object
          * @param babylonScene Babylon scene object
          * @param options Options to modify the behavior of the exporter
          * @param options Options to modify the behavior of the exporter
          */
          */
         constructor(babylonScene: Scene, options?: IExportOptions);
         constructor(babylonScene: Scene, options?: IExportOptions);
+        /**
+         * Lazy load a local engine with premultiplied alpha set to false
+         */
+        _getLocalEngine(): Engine;
         private reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
         private reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
         /**
         /**
          * Reorders the vertex attribute data based on the primitive mode.  This is necessary when indices are not available and the winding order is
          * Reorders the vertex attribute data based on the primitive mode.  This is necessary when indices are not available and the winding order is
@@ -403,19 +416,28 @@ declare module BABYLON.GLTF2 {
      * Utility methods for working with glTF material conversion properties.  This class should only be used internally
      * Utility methods for working with glTF material conversion properties.  This class should only be used internally
      * @hidden
      * @hidden
      */
      */
-    class _GLTFMaterial {
+    class _GLTFMaterialExporter {
         /**
         /**
          * Represents the dielectric specular values for R, G and B
          * Represents the dielectric specular values for R, G and B
          */
          */
-        private static readonly _dielectricSpecular;
+        private static readonly _DielectricSpecular;
         /**
         /**
          * Allows the maximum specular power to be defined for material calculations
          * Allows the maximum specular power to be defined for material calculations
          */
          */
-        private static _maxSpecularPower;
+        private static readonly _MaxSpecularPower;
+        /**
+         * Mapping to store textures
+         */
+        private _textureMap;
         /**
         /**
          * Numeric tolerance value
          * Numeric tolerance value
          */
          */
-        private static _epsilon;
+        private static readonly _Epsilon;
+        /**
+         * Reference to the glTF Exporter
+         */
+        private _exporter;
+        constructor(exporter: _Exporter);
         /**
         /**
          * Specifies if two colors are approximately equal in value
          * Specifies if two colors are approximately equal in value
          * @param color1 first color to compare to
          * @param color1 first color to compare to
@@ -433,32 +455,25 @@ declare module BABYLON.GLTF2 {
          * @param imageData mapping of texture names to base64 textures
          * @param imageData mapping of texture names to base64 textures
          * @param hasTextureCoords specifies if texture coordinates are present on the material
          * @param hasTextureCoords specifies if texture coordinates are present on the material
          */
          */
-        static _ConvertMaterialsToGLTFAsync(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
+        _convertMaterialsToGLTFAsync(babylonMaterials: Material[], mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Makes a copy of the glTF material without the texture parameters
          * Makes a copy of the glTF material without the texture parameters
          * @param originalMaterial original glTF material
          * @param originalMaterial original glTF material
          * @returns glTF material without texture parameters
          * @returns glTF material without texture parameters
          */
          */
-        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        _stripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
         /**
          * Specifies if the material has any texture parameters present
          * Specifies if the material has any texture parameters present
          * @param material glTF Material
          * @param material glTF Material
          * @returns boolean specifying if texture parameters are present
          * @returns boolean specifying if texture parameters are present
          */
          */
-        static _HasTexturesPresent(material: IMaterial): boolean;
+        _hasTexturesPresent(material: IMaterial): boolean;
         /**
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material
          * @param babylonStandardMaterial
          * @param babylonStandardMaterial
          * @returns glTF Metallic Roughness Material representation
          * @returns glTF Metallic Roughness Material representation
          */
          */
-        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        _convertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
         /**
          * Computes the metallic factor
          * Computes the metallic factor
          * @param diffuse diffused value
          * @param diffuse diffused value
@@ -472,7 +487,7 @@ declare module BABYLON.GLTF2 {
          * @param babylonMaterial Babylon Material
          * @param babylonMaterial Babylon Material
          * @returns The Babylon alpha mode value
          * @returns The Babylon alpha mode value
          */
          */
-        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        _getAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
         /**
          * Converts a Babylon Standard Material to a glTF Material
          * Converts a Babylon Standard Material to a glTF Material
          * @param babylonStandardMaterial BJS Standard Material
          * @param babylonStandardMaterial BJS Standard Material
@@ -483,21 +498,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertStandardMaterialAsync(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
-        /**
-         *
-         * @param texture Texture with alpha to overwrite to one
-         * @param useAlpha Specifies if alpha should be preserved or not
-         * @returns Promise with texture
-         */
-        static _SetAlphaToOneAsync(texture: BaseTexture, useAlpha: boolean): Promise<BaseTexture>;
+        _convertStandardMaterialAsync(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMetalRoughMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMetalRoughMaterial BJS PBR Metallic Roughness Material
@@ -508,14 +509,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertPBRMetallicRoughnessMaterialAsync(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
+        _convertPBRMetallicRoughnessMaterialAsync(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
         /**
         /**
          * Converts an image typed array buffer to a base64 image
          * Converts an image typed array buffer to a base64 image
          * @param buffer typed array buffer
          * @param buffer typed array buffer
@@ -524,7 +518,7 @@ declare module BABYLON.GLTF2 {
          * @param mimeType mimetype of the image
          * @param mimeType mimetype of the image
          * @returns base64 image string
          * @returns base64 image string
          */
          */
-        private static _CreateBase64FromCanvas(buffer, width, height, mimeType);
+        private _createBase64FromCanvasAsync(buffer, width, height, mimeType);
         /**
         /**
          * Generates a white texture based on the specified width and height
          * Generates a white texture based on the specified width and height
          * @param width width of the texture in pixels
          * @param width width of the texture in pixels
@@ -532,7 +526,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @param scene babylonjs scene
          * @returns white texture
          * @returns white texture
          */
          */
-        private static _CreateWhiteTexture(width, height, scene);
+        private _createWhiteTexture(width, height, scene);
         /**
         /**
          * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null
          * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null
          * @param texture1 first texture to resize
          * @param texture1 first texture to resize
@@ -540,7 +534,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @param scene babylonjs scene
          * @returns resized textures or null
          * @returns resized textures or null
          */
          */
-        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        private _resizeTexturesToSameDimensions(texture1, texture2, scene);
         /**
         /**
          * Convert Specular Glossiness Textures to Metallic Roughness
          * Convert Specular Glossiness Textures to Metallic Roughness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
@@ -551,25 +545,25 @@ declare module BABYLON.GLTF2 {
          * @param mimeType the mime type to use for the texture
          * @param mimeType the mime type to use for the texture
          * @returns pbr metallic roughness interface or null
          * @returns pbr metallic roughness interface or null
          */
          */
-        private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture, specularGlossinessTexture, factors, mimeType);
+        private _convertSpecularGlossinessTexturesToMetallicRoughnessAsync(diffuseTexture, specularGlossinessTexture, factors, mimeType);
         /**
         /**
          * Converts specular glossiness material properties to metallic roughness
          * Converts specular glossiness material properties to metallic roughness
          * @param specularGlossiness interface with specular glossiness material properties
          * @param specularGlossiness interface with specular glossiness material properties
          * @returns interface with metallic roughness material properties
          * @returns interface with metallic roughness material properties
          */
          */
-        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        private _convertSpecularGlossinessToMetallicRoughness(specularGlossiness);
         /**
         /**
          * Calculates the surface reflectance, independent of lighting conditions
          * Calculates the surface reflectance, independent of lighting conditions
          * @param color Color source to calculate brightness from
          * @param color Color source to calculate brightness from
          * @returns number representing the perceived brightness, or zero if color is undefined
          * @returns number representing the perceived brightness, or zero if color is undefined
          */
          */
-        private static _GetPerceivedBrightness(color);
+        private _getPerceivedBrightness(color);
         /**
         /**
          * Returns the maximum color component value
          * Returns the maximum color component value
          * @param color
          * @param color
          * @returns maximum color component value, or zero if color is null or undefined
          * @returns maximum color component value, or zero if color is null or undefined
          */
          */
-        private static _GetMaxComponent(color);
+        private _getMaxComponent(color);
         /**
         /**
          * Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
          * Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -581,10 +575,10 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          * @returns glTF PBR Metallic Roughness factors
          */
          */
-        private static _ConvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
-        private static _GetGLTFTextureSampler(texture);
-        private static _GetGLTFTextureWrapMode(wrapMode);
-        private static _GetGLTFTextureWrapModesSampler(texture);
+        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _getGLTFTextureSampler(texture);
+        private _getGLTFTextureWrapMode(wrapMode);
+        private _getGLTFTextureWrapModesSampler(texture);
         /**
         /**
          * Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
          * Convert a PBRMaterial (Specular/Glossiness) to Metallic Roughness factors
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -596,7 +590,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          * @returns glTF PBR Metallic Roughness factors
          */
          */
-        private static _ConvertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -607,16 +601,9 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
          */
-        static _ConvertPBRMaterialAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], materialMap: {
-            [materialID: number]: number;
-        }, imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }, hasTextureCoords: boolean): Promise<void>;
-        private static SetMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, images, textures, samplers, materials, materialMap, imageData, hasTextureCoords);
-        private static GetPixelsFromTexture(babylonTexture);
+        _convertPBRMaterialAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, hasTextureCoords: boolean): Promise<void>;
+        private setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
+        private getPixelsFromTexture(babylonTexture);
         /**
         /**
          * Extracts a texture from a Babylon texture into file data and glTF data
          * Extracts a texture from a Babylon texture into file data and glTF data
          * @param babylonTexture Babylon texture to extract
          * @param babylonTexture Babylon texture to extract
@@ -626,7 +613,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name and data
          * @param imageData map of image file name and data
          * @return glTF texture info, or null if the texture format is not supported
          * @return glTF texture info, or null if the texture format is not supported
          */
          */
-        private static _ExportTextureAsync(babylonTexture, mimeType, images, textures, samplers, imageData, useAlpha);
+        private _exportTextureAsync(babylonTexture, mimeType);
         /**
         /**
          * Builds a texture from base64 string
          * Builds a texture from base64 string
          * @param base64Texture base64 texture string
          * @param base64Texture base64 texture string
@@ -637,7 +624,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image data
          * @param imageData map of image data
          * @returns glTF texture info, or null if the texture format is not supported
          * @returns glTF texture info, or null if the texture format is not supported
          */
          */
-        private static _GetTextureInfoFromBase64(base64Texture, baseTextureName, mimeType, images, textures, texCoordIndex, samplerIndex, imageData);
+        private _getTextureInfoFromBase64(base64Texture, baseTextureName, mimeType, texCoordIndex, samplerIndex);
     }
     }
 }
 }
 
 

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


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


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

@@ -8,7 +8,7 @@
 - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
 - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
 - New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
-- New `serialize` and `Parse` functions to serialize and parse all procedural textures from the Procedural Textures Library ([julien-moreau](https://github.com/julien-moreau))
+- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, rotation, scale and bounding box ([TrevorDev](https://github.com/TrevorDev))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
   - Improved CPU particles rendering performance (up to x2 on low end devices)
   - Improved CPU particles rendering performance (up to x2 on low end devices)
   - Added support for `isBillboardBased`. [Doc](http://doc.babylonjs.com/babylon101/particles#alignment)
   - Added support for `isBillboardBased`. [Doc](http://doc.babylonjs.com/babylon101/particles#alignment)
@@ -16,6 +16,9 @@
   - Added support for `radiusRange` for sphere emitter. [Doc](http://doc.babylonjs.com/babylon101/particles#sphere-emitter)
   - Added support for `radiusRange` for sphere emitter. [Doc](http://doc.babylonjs.com/babylon101/particles#sphere-emitter)
   - Added support for `ParticleSystem.BLENDMODE_ADD` alpha mode. [Doc](http://doc.babylonjs.com/babylon101/particles#particle-blending)
   - Added support for `ParticleSystem.BLENDMODE_ADD` alpha mode. [Doc](http://doc.babylonjs.com/babylon101/particles#particle-blending)
   - Added support for color gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#particle-colors)
   - Added support for color gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#particle-colors)
+  - Added support for pre-warming. [Doc](http://doc.babylonjs.com/babylon101/particles#pre-warming)
+  - Added support for `minInitialRotation` and `maxInitialRotation`. [Doc](http://doc.babylonjs.com/babylon101/particles#rotation)
+  - Added support for size gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#size)
 
 
 ## Updates
 ## Updates
 
 
@@ -34,20 +37,24 @@
 - Get a root mesh from an asset container, load a mesh from a file with a single string url ([TrevorDev](https://github.com/TrevorDev))
 - Get a root mesh from an asset container, load a mesh from a file with a single string url ([TrevorDev](https://github.com/TrevorDev))
 - UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
 - UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
 - AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
 - AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
-- PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh ([TrevorDev](https://github.com/TrevorDev))
-- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include position, rotation, scale, and bounding box ([TrevorDev](https://github.com/TrevorDev))
+- New behaviors: PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh ([TrevorDev](https://github.com/TrevorDev))
+- New `serialize` and `Parse` functions to serialize and parse all procedural textures from the Procedural Textures Library ([julien-moreau](https://github.com/julien-moreau))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 - AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))
 - AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))
 - Added sprite isVisible field ([TrevorDev](https://github.com/TrevorDev))
 - Added sprite isVisible field ([TrevorDev](https://github.com/TrevorDev))
 - EnvironmentHelper will recreate ground and skybox meshes if force-disposed ([RaananW](https://github.com/RaananW))
 - EnvironmentHelper will recreate ground and skybox meshes if force-disposed ([RaananW](https://github.com/RaananW))
-- Added viewport caching mecanism in engine ([sebavan](http://www.github.com/sebavan))
-- Added unpackFlipY caching mecanism in engine ([sebavan](http://www.github.com/sebavan))
+- Added viewport caching mechanism in engine ([sebavan](http://www.github.com/sebavan))
+- Added unpackFlipY caching mechanism in engine ([sebavan](http://www.github.com/sebavan))
 - Added rebind optimization of video texture ([sebavan](http://www.github.com/sebavan))
 - Added rebind optimization of video texture ([sebavan](http://www.github.com/sebavan))
 - Fix Background Material effect caching ([sebavan](http://www.github.com/sebavan))
 - Fix Background Material effect caching ([sebavan](http://www.github.com/sebavan))
 - Prevent texture ```getSize``` to generate garbage collection ([sebavan](http://www.github.com/sebavan))
 - Prevent texture ```getSize``` to generate garbage collection ([sebavan](http://www.github.com/sebavan))
 - Prevent ```lodGenerationScale``` and ```lodGenerationOffset``` to force rebind ([sebavan](http://www.github.com/sebavan))
 - Prevent ```lodGenerationScale``` and ```lodGenerationOffset``` to force rebind ([sebavan](http://www.github.com/sebavan))
 - Added poster property on VideoTexture ([sebavan](http://www.github.com/sebavan))
 - Added poster property on VideoTexture ([sebavan](http://www.github.com/sebavan))
 - Added ```onUserActionRequestedObservable``` to workaround and detect autoplay video policy restriction on VideoTexture ([sebavan](http://www.github.com/sebavan))
 - Added ```onUserActionRequestedObservable``` to workaround and detect autoplay video policy restriction on VideoTexture ([sebavan](http://www.github.com/sebavan))
+- `Sound` now accepts `MediaStream` as source to enable easier WebAudio and WebRTC integrations ([menduz](https://github.com/menduz))
+- Vector x, y and z constructor parameters are now optional and default to 0 ([TrevorDev](https://github.com/TrevorDev))
+- New vertical mode for sliders in 2D GUI. [Demo](https://www.babylonjs-playground.com/#U9AC0N#53) ([Saket Saurabh](https://github.com/ssaket))
+- Add internal texture `format` support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
 
 
 ### glTF Loader
 ### glTF Loader
 
 
@@ -79,6 +86,8 @@
 - SceneSerializer.SerializeMesh now serializes all materials kinds (not only StandardMaterial) ([julien-moreau](https://github.com/julien-moreau))
 - SceneSerializer.SerializeMesh now serializes all materials kinds (not only StandardMaterial) ([julien-moreau](https://github.com/julien-moreau))
 - WindowsMotionController's trackpad field will be updated prior to it's onTrackpadChangedObservable event ([TrevorDev](https://github.com/TrevorDev))
 - WindowsMotionController's trackpad field will be updated prior to it's onTrackpadChangedObservable event ([TrevorDev](https://github.com/TrevorDev))
 - VR experience helper's controllers will not fire pointer events when laser's are disabled, instead the camera ray pointer event will be used ([TrevorDev](https://github.com/TrevorDev))
 - VR experience helper's controllers will not fire pointer events when laser's are disabled, instead the camera ray pointer event will be used ([TrevorDev](https://github.com/TrevorDev))
+- Node's setParent(node.parent) will no longer throw an exception when parent is undefined and will behave the same as setParent(null) ([TrevorDev](https://github.com/TrevorDev))
+- Mesh.MergeMeshes flips triangles on meshes with negative scaling ([SvenFrankson](http://svenfrankson.com))
 
 
 ### Core Engine
 ### Core Engine
 
 
@@ -106,10 +115,12 @@
 - Meshes with skeletons could have incorrect animations ([RaananW](https://github.com/RaananW))
 - Meshes with skeletons could have incorrect animations ([RaananW](https://github.com/RaananW))
 - Removed element IDs from viewer's templates to allow muitiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
 - Removed element IDs from viewer's templates to allow muitiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
 - Viewer is not using Engine.LastCreatedScene anymore, to support multiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
 - Viewer is not using Engine.LastCreatedScene anymore, to support multiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
+- Template location was ignored if html was defined ([RaananW](https://github.com/RaananW))
 
 
 ### Loaders
 ### Loaders
 
 
 - STL Loader only supported binary downloads and no data: urls [#4473](https://github.com/BabylonJS/Babylon.js/issues/4473) ([RaananW](https://github.com/RaananW))
 - STL Loader only supported binary downloads and no data: urls [#4473](https://github.com/BabylonJS/Babylon.js/issues/4473) ([RaananW](https://github.com/RaananW))
+- OBJ Loader is now an async loader [#4571](https://github.com/BabylonJS/Babylon.js/issues/4571) ([RaananW](https://github.com/RaananW))
 
 
 ## Breaking changes
 ## Breaking changes
 
 

+ 183 - 33
gui/src/2D/controls/slider.ts

@@ -5,10 +5,11 @@ module BABYLON.GUI {
      * Class used to create slider controls
      * Class used to create slider controls
      */
      */
     export class Slider extends Control {
     export class Slider extends Control {
-        private _thumbWidth = new ValueAndUnit(30, ValueAndUnit.UNITMODE_PIXEL, false);
+        private _thumbWidth = new ValueAndUnit(20, ValueAndUnit.UNITMODE_PIXEL, false);
         private _minimum = 0;
         private _minimum = 0;
         private _maximum = 100;
         private _maximum = 100;
         private _value = 50;
         private _value = 50;
+        private _isVertical = false;
         private _background = "black";
         private _background = "black";
         private _borderColor = "white";
         private _borderColor = "white";
         private _barOffset = new ValueAndUnit(5, ValueAndUnit.UNITMODE_PIXEL, false);
         private _barOffset = new ValueAndUnit(5, ValueAndUnit.UNITMODE_PIXEL, false);
@@ -135,6 +136,20 @@ module BABYLON.GUI {
             this.onValueChangedObservable.notifyObservers(this._value);
             this.onValueChangedObservable.notifyObservers(this._value);
         }
         }
 
 
+        /**Gets or sets a boolean indicating if the slider should be vertical or horizontal */
+        public get isVertical(): boolean {
+            return this._isVertical;
+        }
+
+        public set isVertical(value: boolean) {
+            if(this._isVertical === value){
+                return;
+            }
+
+            this._isVertical = value;
+            this._markAsDirty();
+        }
+
         /** Gets or sets a boolean indicating if the thumb should be round or square */
         /** Gets or sets a boolean indicating if the thumb should be round or square */
         public get isThumbCircle(): boolean {
         public get isThumbCircle(): boolean {
             return this._isThumbCircle;
             return this._isThumbCircle;
@@ -177,27 +192,94 @@ module BABYLON.GUI {
             return "Slider";
             return "Slider";
         }
         }
 
 
+        protected _getThumbThickness(type:string, backgroundLength:number): number {
+            var thumbThickness = 0;
+            switch(type) {
+                case "circle":
+                    if (this._thumbWidth.isPixel) {
+                        thumbThickness = Math.max(this._thumbWidth.getValue(this._host), backgroundLength);
+                    }
+                    else {
+                        thumbThickness = backgroundLength * this._thumbWidth.getValue(this._host);
+                    }
+                    break;
+                case "rectangle":
+                    if (this._thumbWidth.isPixel) {
+                        thumbThickness = Math.min(this._thumbWidth.getValue(this._host), backgroundLength);
+                    }
+                    else {
+                        thumbThickness = backgroundLength * this._thumbWidth.getValue(this._host);
+                    }
+            }
+            return thumbThickness;
+        }
+
+
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
             context.save();
 
 
             this._applyStates(context);
             this._applyStates(context);
             if (this._processMeasures(parentMeasure, context)) {
             if (this._processMeasures(parentMeasure, context)) {
                 // Main bar
                 // Main bar
-                var effectiveThumbWidth;
-                var effectiveBarOffset;
-
-                if (this._thumbWidth.isPixel) {
-                    effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.width);
+                var effectiveBarOffset = 0;
+                var type = this.isThumbCircle ? "circle" : "rectangle";
+                var left = this._currentMeasure.left;
+                var top = this._currentMeasure.top;
+                var width = this._currentMeasure.width;
+                var height = this._currentMeasure.height;
+            
+                var backgroundBoxLength = Math.max(this._currentMeasure.width, this._currentMeasure.height);
+                var backgroundBoxThickness = Math.min(this._currentMeasure.width, this._currentMeasure.height);
+                
+                var effectiveThumbThickness = this._getThumbThickness(type, backgroundBoxThickness);
+                backgroundBoxLength -= effectiveThumbThickness;
+
+                var radius = 0;
+
+
+                //throw error when height is less than width for vertical slider
+                if ((this._isVertical && this._currentMeasure.height < this._currentMeasure.width)) {
+                    console.error("Height should be greater than width");
+                    return;
+                }
+                if (this._barOffset.isPixel) {
+                    effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), backgroundBoxThickness);
                 }
                 }
                 else {
                 else {
-                    effectiveThumbWidth = this._currentMeasure.width * this._thumbWidth.getValue(this._host);
+                    effectiveBarOffset = backgroundBoxThickness * this._barOffset.getValue(this._host);
                 }
                 }
 
 
-                if (this._barOffset.isPixel) {
-                    effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), this._currentMeasure.height);
+                backgroundBoxThickness -= (effectiveBarOffset * 2);
+
+                if (this._isVertical) {
+                    left += effectiveBarOffset;
+                    if(!this.isThumbClamped) {
+                       top += (effectiveThumbThickness / 2);
+                    }
+    
+                    height = backgroundBoxLength;
+                    width = backgroundBoxThickness;
+
                 }
                 }
                 else {
                 else {
-                    effectiveBarOffset = this._currentMeasure.height * this._barOffset.getValue(this._host);
+                    top += effectiveBarOffset;
+                    if(!this.isThumbClamped) {
+                       left += (effectiveThumbThickness / 2);
+                    }
+                    height = backgroundBoxThickness;
+                    width = backgroundBoxLength;
+                }
+
+                if(this.isThumbClamped && this.isThumbCircle){
+                    if(this._isVertical)
+                        top += (effectiveThumbThickness / 2);
+                    else 
+                        left += (effectiveThumbThickness / 2);
+
+                    radius = backgroundBoxThickness / 2;
+                }
+                else{
+                    radius = (effectiveThumbThickness- effectiveBarOffset) / 2;
                 }
                 }
 
 
                 if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                 if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
@@ -207,16 +289,40 @@ module BABYLON.GUI {
                     context.shadowOffsetY = this.shadowOffsetY;
                     context.shadowOffsetY = this.shadowOffsetY;
                 }
                 }
 
 
-                var left = this._currentMeasure.left;
-                var width = this._currentMeasure.width - effectiveThumbWidth;
-                var thumbPosition = ((this._value - this._minimum) / (this._maximum - this._minimum)) * width;
-
+                var thumbPosition = (this._isVertical) ? ((this._maximum - this._value) / (this._maximum - this._minimum)) * backgroundBoxLength : ((this._value - this._minimum) / (this._maximum - this._minimum)) * backgroundBoxLength;
                 context.fillStyle = this._background;
                 context.fillStyle = this._background;
-                if (this.isThumbClamped) {
-                    context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width + effectiveThumbWidth, this._currentMeasure.height - effectiveBarOffset * 2);
+                
+                if(this._isVertical){
+                    if (this.isThumbClamped) {
+                        if (this.isThumbCircle) {
+                            context.beginPath();
+                            context.arc(left + backgroundBoxThickness / 2, top, radius, Math.PI, 2 * Math.PI);
+                            context.fill();
+                            context.fillRect(left, top, width , height);
+                        }
+                        else {
+                            context.fillRect(left, top, width, height + effectiveThumbThickness);
+                        }
+                    }
+                    else {
+                        context.fillRect(left, top , width, height);
+                    }
                 }
                 }
-                else {
-                    context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
+                else{
+                    if (this.isThumbClamped) {
+                        if (this.isThumbCircle) {
+                            context.beginPath();
+                            context.arc(left + backgroundBoxLength , top + (backgroundBoxThickness/2), radius, 0,  2* Math.PI);
+                            context.fill();
+                            context.fillRect(left, top, width, height);
+                        }
+                        else {
+                            context.fillRect(left, top, width + effectiveThumbThickness, height);
+                        }
+                    }
+                    else {
+                        context.fillRect(left, top, width, height);
+                    }
                 }
                 }
 
 
                 if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                 if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
@@ -224,47 +330,84 @@ module BABYLON.GUI {
                     context.shadowOffsetX = 0;
                     context.shadowOffsetX = 0;
                     context.shadowOffsetY = 0;
                     context.shadowOffsetY = 0;
                 }
                 }
-
+                
                 context.fillStyle = this.color;
                 context.fillStyle = this.color;
-                if (this.isThumbClamped) {
-                    context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
+                if(this._isVertical){
+                    if (this.isThumbClamped) {
+                        if (this.isThumbCircle) {
+                            context.beginPath();
+                            context.arc(left + backgroundBoxThickness / 2, top + backgroundBoxLength, radius, 0, 2 * Math.PI);
+                            context.fill();
+                            context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
+                        }
+                        else {
+                            context.fillRect(left, top + thumbPosition, width, this._currentMeasure.height - thumbPosition);
+                        }
+                    }
+                    else {
+                        context.fillRect(left, top + thumbPosition ,width,  height - thumbPosition);
+                    }
                 }
                 }
-                else {
-                    context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
+                else{
+                    if (this.isThumbClamped) {
+                        if (this.isThumbCircle) {
+                            context.beginPath();
+                            context.arc(left , top + backgroundBoxThickness/2 , radius, 0, 2 * Math.PI);
+                            context.fill();
+                            context.fillRect(left, top, thumbPosition, height);
+                        }
+                        else {
+                            context.fillRect(left, top, thumbPosition, height);
+                        }
+                    }
+                    else {
+                        context.fillRect(left , top, thumbPosition,  height);
+                    }
                 }
                 }
-
+               
+               
                 if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                 if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     context.shadowColor = this.shadowColor;
                     context.shadowColor = this.shadowColor;
                     context.shadowBlur = this.shadowBlur;
                     context.shadowBlur = this.shadowBlur;
                     context.shadowOffsetX = this.shadowOffsetX;
                     context.shadowOffsetX = this.shadowOffsetX;
                     context.shadowOffsetY = this.shadowOffsetY;
                     context.shadowOffsetY = this.shadowOffsetY;
                 }
                 }
-
                 if (this._isThumbCircle) {
                 if (this._isThumbCircle) {
                     context.beginPath();
                     context.beginPath();
-                    context.arc(left + thumbPosition + (effectiveThumbWidth / 2), this._currentMeasure.top + this._currentMeasure.height / 2, effectiveThumbWidth / 2, 0, 2 * Math.PI);
+                    if (this._isVertical) {
+                        context.arc(left + backgroundBoxThickness / 2, top + thumbPosition, radius, 0, 2 * Math.PI);
+                    }
+                    else {
+                        context.arc(left + thumbPosition, top + ( backgroundBoxThickness/2), radius, 0, 2 * Math.PI);
+                    }
                     context.fill();
                     context.fill();
-
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowBlur = 0;
                         context.shadowBlur = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetY = 0;
                         context.shadowOffsetY = 0;
                     }
                     }
-
                     context.strokeStyle = this._borderColor;
                     context.strokeStyle = this._borderColor;
                     context.stroke();
                     context.stroke();
                 }
                 }
                 else {
                 else {
-                    context.fillRect(left + thumbPosition, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
-
+                    if (this._isVertical) {
+                        context.fillRect(left - effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, effectiveThumbThickness);
+                    }
+                    else {
+                        context.fillRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, effectiveThumbThickness, this._currentMeasure.height);
+                    }
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowBlur = 0;
                         context.shadowBlur = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetY = 0;
                         context.shadowOffsetY = 0;
                     }
                     }
-
                     context.strokeStyle = this._borderColor;
                     context.strokeStyle = this._borderColor;
-                    context.strokeRect(left + thumbPosition, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
+                    if (this._isVertical) {
+                        context.strokeRect(left - effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, effectiveThumbThickness);
+                    }
+                    else {
+                        context.strokeRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, effectiveThumbThickness, this._currentMeasure.height);
+                    }
                 }
                 }
             }
             }
             context.restore();
             context.restore();
@@ -278,8 +421,15 @@ module BABYLON.GUI {
             if (this.rotation != 0) {
             if (this.rotation != 0) {
                 this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
                 this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
                 x = this._transformedPosition.x;
                 x = this._transformedPosition.x;
+                y = this._transformedPosition.y;
+            }
+            
+            if(this._isVertical){
+                this.value = this._minimum + (1 - ((y - this._currentMeasure.top) / this._currentMeasure.height)) * (this._maximum - this._minimum);
+            }
+            else{
+                this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
             }
             }
-            this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
         }
         }
 
 
         public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
         public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {

+ 82 - 66
loaders/src/OBJ/babylon.objFileLoader.ts

@@ -24,7 +24,7 @@ module BABYLON {
             if (data instanceof ArrayBuffer) {
             if (data instanceof ArrayBuffer) {
                 return;
                 return;
             }
             }
-            
+
             //Split the lines from the file
             //Split the lines from the file
             var lines = data.split('\n');
             var lines = data.split('\n');
             //Space char
             //Space char
@@ -202,7 +202,7 @@ module BABYLON {
         }
         }
     }
     }
 
 
-    export class OBJFileLoader implements ISceneLoaderPlugin {
+    export class OBJFileLoader implements ISceneLoaderPluginAsync {
 
 
         public static OPTIMIZE_WITH_UV = false;
         public static OPTIMIZE_WITH_UV = false;
         public static INVERT_Y = false;
         public static INVERT_Y = false;
@@ -252,28 +252,32 @@ module BABYLON {
                 () => { console.warn("Error - Unable to load " + pathOfFile); });
                 () => { console.warn("Error - Unable to load " + pathOfFile); });
         }
         }
 
 
-        public importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             //get the meshes from OBJ file
             //get the meshes from OBJ file
-            var loadedMeshes = this._parseSolid(meshesNames, scene, data, rootUrl);
-            //Push meshes from OBJ file into the variable mesh of this function
-            if (meshes) {
-                loadedMeshes.forEach(function (mesh) {
-                    meshes.push(mesh);
-                });
-            }
-            return true;
+            return this._parseSolid(meshesNames, scene, data, rootUrl).then(meshes => {
+                return {
+                    meshes,
+                    particleSystems: [],
+                    skeletons: [],
+                    animationGroups: []
+                }
+            });
         }
         }
 
 
-        public load(scene: Scene, data: string, rootUrl: string): boolean {
+        public loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
             //Get the 3D model
             //Get the 3D model
-            return this.importMesh(null, scene, data, rootUrl, null, null, null);
+            return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(() => {
+                // return void
+            });
         }
         }
 
 
-        public loadAssetContainer(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): AssetContainer {
-            var container = new AssetContainer(scene);
-            this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
-            container.removeAllFromScene();
-            return container;
+        public loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer> {
+            return this.importMeshAsync(null, scene, data, rootUrl).then(result => {
+                var container = new AssetContainer(scene);
+                result.meshes.forEach(mesh => container.meshes.push(mesh));
+                container.removeAllFromScene();
+                return container;
+            });
         }
         }
 
 
         /**
         /**
@@ -288,7 +292,7 @@ module BABYLON {
          * @returns Array<AbstractMesh>
          * @returns Array<AbstractMesh>
          * @private
          * @private
          */
          */
-        private _parseSolid(meshesNames: any, scene: BABYLON.Scene, data: string, rootUrl: string): Array<AbstractMesh> {
+        private _parseSolid(meshesNames: any, scene: BABYLON.Scene, data: string, rootUrl: string): Promise<Array<AbstractMesh>> {
 
 
             var positions: Array<BABYLON.Vector3> = [];      //values for the positions of vertices
             var positions: Array<BABYLON.Vector3> = [];      //values for the positions of vertices
             var normals: Array<BABYLON.Vector3> = [];      //Values for the normals
             var normals: Array<BABYLON.Vector3> = [];      //Values for the normals
@@ -702,15 +706,15 @@ module BABYLON {
                         uvs?: Array<number>;
                         uvs?: Array<number>;
                         materialName: string;
                         materialName: string;
                     } =
                     } =
-                        //Set the name of the current obj mesh
-                        {
-                            name: line.substring(2).trim(),
-                            indices: undefined,
-                            positions: undefined,
-                            normals: undefined,
-                            uvs: undefined,
-                            materialName: ""
-                        };
+                    //Set the name of the current obj mesh
+                    {
+                        name: line.substring(2).trim(),
+                        indices: undefined,
+                        positions: undefined,
+                        normals: undefined,
+                        uvs: undefined,
+                        materialName: ""
+                    };
                     addPreviousObjMesh();
                     addPreviousObjMesh();
 
 
                     //Push the last mesh created with only the name
                     //Push the last mesh created with only the name
@@ -739,15 +743,15 @@ module BABYLON {
                             uvs?: Array<number>;
                             uvs?: Array<number>;
                             materialName: string;
                             materialName: string;
                         } =
                         } =
-                            //Set the name of the current obj mesh
-                            {
-                                name: objMeshName + "_mm" + increment.toString(),
-                                indices: undefined,
-                                positions: undefined,
-                                normals: undefined,
-                                uvs: undefined,
-                                materialName: materialNameFromObj
-                            };
+                        //Set the name of the current obj mesh
+                        {
+                            name: objMeshName + "_mm" + increment.toString(),
+                            indices: undefined,
+                            positions: undefined,
+                            normals: undefined,
+                            uvs: undefined,
+                            materialName: materialNameFromObj
+                        };
                         increment++;
                         increment++;
                         //If meshes are already defined
                         //If meshes are already defined
                         meshesFromObj.push(objMesh);
                         meshesFromObj.push(objMesh);
@@ -847,49 +851,61 @@ module BABYLON {
                 //Set the data from the VertexBuffer to the current BABYLON.Mesh
                 //Set the data from the VertexBuffer to the current BABYLON.Mesh
                 vertexData.applyToMesh(babylonMesh);
                 vertexData.applyToMesh(babylonMesh);
                 if (OBJFileLoader.INVERT_Y) {
                 if (OBJFileLoader.INVERT_Y) {
-                    babylonMesh.scaling.y *=-1;
+                    babylonMesh.scaling.y *= -1;
                 }
                 }
 
 
                 //Push the mesh into an array
                 //Push the mesh into an array
                 babylonMeshesArray.push(babylonMesh);
                 babylonMeshesArray.push(babylonMesh);
             }
             }
+
+            let mtlPromises: Array<Promise<any>> = [];
             //load the materials
             //load the materials
             //Check if we have a file to load
             //Check if we have a file to load
             if (fileToLoad !== "") {
             if (fileToLoad !== "") {
                 //Load the file synchronously
                 //Load the file synchronously
-                this._loadMTL(fileToLoad, rootUrl, function (dataLoaded) {
-                    //Create materials thanks MTLLoader function
-                    materialsFromMTLFile.parseMTL(scene, dataLoaded, rootUrl);
-                    //Look at each material loaded in the mtl file
-                    for (var n = 0; n < materialsFromMTLFile.materials.length; n++) {
-                        //Three variables to get all meshes with the same material
-                        var startIndex = 0;
-                        var _indices = [];
-                        var _index;
-
-                        //The material from MTL file is used in the meshes loaded
-                        //Push the indice in an array
-                        //Check if the material is not used for another mesh
-                        while ((_index = materialToUse.indexOf(materialsFromMTLFile.materials[n].name, startIndex)) > -1) {
-                            _indices.push(_index);
-                            startIndex = _index + 1;
-                        }
-                        //If the material is not used dispose it
-                        if (_index == -1 && _indices.length == 0) {
-                            //If the material is not needed, remove it
-                            materialsFromMTLFile.materials[n].dispose();
-                        } else {
-                            for (var o = 0; o < _indices.length; o++) {
-                                //Apply the material to the BABYLON.Mesh for each mesh with the material
-                                babylonMeshesArray[_indices[o]].material = materialsFromMTLFile.materials[n];
+                mtlPromises.push(new Promise((resolve, reject) => {
+                    this._loadMTL(fileToLoad, rootUrl, function (dataLoaded) {
+                        try {
+                            //Create materials thanks MTLLoader function
+                            materialsFromMTLFile.parseMTL(scene, dataLoaded, rootUrl);
+                            //Look at each material loaded in the mtl file
+                            for (var n = 0; n < materialsFromMTLFile.materials.length; n++) {
+                                //Three variables to get all meshes with the same material
+                                var startIndex = 0;
+                                var _indices = [];
+                                var _index;
+
+                                //The material from MTL file is used in the meshes loaded
+                                //Push the indice in an array
+                                //Check if the material is not used for another mesh
+                                while ((_index = materialToUse.indexOf(materialsFromMTLFile.materials[n].name, startIndex)) > -1) {
+                                    _indices.push(_index);
+                                    startIndex = _index + 1;
+                                }
+                                //If the material is not used dispose it
+                                if (_index == -1 && _indices.length == 0) {
+                                    //If the material is not needed, remove it
+                                    materialsFromMTLFile.materials[n].dispose();
+                                } else {
+                                    for (var o = 0; o < _indices.length; o++) {
+                                        //Apply the material to the BABYLON.Mesh for each mesh with the material
+                                        babylonMeshesArray[_indices[o]].material = materialsFromMTLFile.materials[n];
+                                    }
+                                }
                             }
                             }
+                            resolve();
+                        } catch (e) {
+                            reject(e);
                         }
                         }
-                    }
 
 
-                });
+                    });
+                }));
+
             }
             }
             //Return an array with all BABYLON.Mesh
             //Return an array with all BABYLON.Mesh
-            return babylonMeshesArray;
+            return Promise.all(mtlPromises).then(() => {
+                return babylonMeshesArray;
+            });
         }
         }
 
 
     }
     }

+ 153 - 125
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -32,86 +32,90 @@ module BABYLON.GLTF2 {
         /**
         /**
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          * Stores all generated buffer views, which represents views into the main glTF buffer data
          */
          */
-        private bufferViews: IBufferView[];
+        private _bufferViews: IBufferView[];
         /**
         /**
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
          */
          */
-        private accessors: IAccessor[];
+        private _accessors: IAccessor[];
         /**
         /**
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          * Stores all the generated nodes, which contains transform and/or mesh information per node
          */
          */
-        private nodes: INode[];
+        private _nodes: INode[];
         /**
         /**
          * Stores the glTF asset information, which represents the glTF version and this file generator
          * Stores the glTF asset information, which represents the glTF version and this file generator
          */
          */
-        private asset: IAsset;
+        private _asset: IAsset;
         /**
         /**
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          * Stores all the generated glTF scenes, which stores multiple node hierarchies
          */
          */
-        private scenes: IScene[];
+        private _scenes: IScene[];
         /**
         /**
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          * Stores all the generated mesh information, each containing a set of primitives to render in glTF
          */
          */
-        private meshes: IMesh[];
+        private _meshes: IMesh[];
         /**
         /**
          * Stores all the generated material information, which represents the appearance of each primitive
          * Stores all the generated material information, which represents the appearance of each primitive
          */
          */
-        private materials: IMaterial[];
+        public _materials: IMaterial[];
 
 
-        private materialMap: { [materialID: number]: number };
+        public _materialMap: { [materialID: number]: number };
         /**
         /**
          * Stores all the generated texture information, which is referenced by glTF materials
          * Stores all the generated texture information, which is referenced by glTF materials
          */
          */
-        private textures: ITexture[];
+        public _textures: ITexture[];
         /**
         /**
          * Stores all the generated image information, which is referenced by glTF textures
          * Stores all the generated image information, which is referenced by glTF textures
          */
          */
-        private images: IImage[];
+        public _images: IImage[];
 
 
         /**
         /**
          * Stores all the texture samplers
          * Stores all the texture samplers
          */
          */
-        private samplers: ISampler[];
+        public _samplers: ISampler[];
         /**
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
          */
         /**
         /**
          * Stores the animations for glTF models
          * Stores the animations for glTF models
          */
          */
-        private animations: IAnimation[];
+        private _animations: IAnimation[];
         /**
         /**
          * Stores the total amount of bytes stored in the glTF buffer
          * Stores the total amount of bytes stored in the glTF buffer
          */
          */
-        private totalByteLength: number;
+        private _totalByteLength: number;
         /**
         /**
          * Stores a reference to the Babylon scene containing the source geometry and material information
          * Stores a reference to the Babylon scene containing the source geometry and material information
          */
          */
-        private babylonScene: Scene;
+        private _babylonScene: Scene;
         /**
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * Stores a map of the image data, where the key is the file name and the value
          * is the image data
          * is the image data
          */
          */
-        private imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } };
+        public _imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } };
 
 
         /**
         /**
          * Stores a map of the unique id of a node to its index in the node array
          * Stores a map of the unique id of a node to its index in the node array
          */
          */
-        private nodeMap: { [key: number]: number };
+        private _nodeMap: { [key: number]: number };
 
 
         /**
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
          */
-        private convertToRightHandedSystem: boolean;
+        private _convertToRightHandedSystem: boolean;
 
 
         /**
         /**
          * Baked animation sample rate
          * Baked animation sample rate
          */
          */
-        private animationSampleRate: number;
+        private _animationSampleRate: number;
 
 
         /**
         /**
          * Callback which specifies if a transform node should be exported or not
          * Callback which specifies if a transform node should be exported or not
          */
          */
-        private shouldExportTransformNode: ((babylonTransformNode: TransformNode) => boolean);
+        private _shouldExportTransformNode: ((babylonTransformNode: TransformNode) => boolean);
+
+        private _localEngine: Engine;
+
+        private _glTFMaterialExporter: _GLTFMaterialExporter;
 
 
         /**
         /**
          * Creates a glTF Exporter instance, which can accept optional exporter options
          * Creates a glTF Exporter instance, which can accept optional exporter options
@@ -119,24 +123,43 @@ module BABYLON.GLTF2 {
          * @param options Options to modify the behavior of the exporter
          * @param options Options to modify the behavior of the exporter
          */
          */
         public constructor(babylonScene: Scene, options?: IExportOptions) {
         public constructor(babylonScene: Scene, options?: IExportOptions) {
-            this.asset = { generator: "BabylonJS", version: "2.0" };
-            this.babylonScene = babylonScene;
-            this.bufferViews = [];
-            this.accessors = [];
-            this.meshes = [];
-            this.scenes = [];
-            this.nodes = [];
-            this.images = [];
-            this.materials = [];
-            this.materialMap = [];
-            this.textures = [];
-            this.samplers = [];
-            this.animations = [];
-            this.imageData = {};
-            this.convertToRightHandedSystem = this.babylonScene.useRightHandedSystem ? false : true;
+            this._asset = { generator: "BabylonJS", version: "2.0" };
+            this._babylonScene = babylonScene;
+            this._bufferViews = [];
+            this._accessors = [];
+            this._meshes = [];
+            this._scenes = [];
+            this._nodes = [];
+            this._images = [];
+            this._materials = [];
+            this._materialMap = [];
+            this._textures = [];
+            this._samplers = [];
+            this._animations = [];
+            this._imageData = {};
+            this._convertToRightHandedSystem = this._babylonScene.useRightHandedSystem ? false : true;
             const _options = options || {};
             const _options = options || {};
-            this.shouldExportTransformNode = _options.shouldExportTransformNode ? _options.shouldExportTransformNode : (babylonTransformNode: TransformNode) => true;
-            this.animationSampleRate = _options.animationSampleRate ? _options.animationSampleRate : 1 / 60;
+            this._shouldExportTransformNode = _options.shouldExportTransformNode ? _options.shouldExportTransformNode : (babylonTransformNode: TransformNode) => true;
+            this._animationSampleRate = _options.animationSampleRate ? _options.animationSampleRate : 1 / 60;
+
+
+            this._glTFMaterialExporter = new _GLTFMaterialExporter(this);
+        }
+
+        /**
+         * Lazy load a local engine with premultiplied alpha set to false
+         */
+        public _getLocalEngine(): Engine {
+            if (!this._localEngine) {
+                const localCanvas = document.createElement('canvas');
+                localCanvas.id = "WriteCanvas";
+                localCanvas.width = 2048;
+                localCanvas.height = 2048;
+                this._localEngine = new Engine(localCanvas, true, { premultipliedAlpha: false, preserveDrawingBuffer: true });
+                this._localEngine.setViewport(new Viewport(0, 0, 1, 1));
+            }
+
+            return this._localEngine;
         }
         }
 
 
         private reorderIndicesBasedOnPrimitiveMode(submesh: SubMesh, primitiveMode: number, babylonIndices: IndicesArray, byteOffset: number, binaryWriter: _BinaryWriter) {
         private reorderIndicesBasedOnPrimitiveMode(submesh: SubMesh, primitiveMode: number, babylonIndices: IndicesArray, byteOffset: number, binaryWriter: _BinaryWriter) {
@@ -182,7 +205,7 @@ module BABYLON.GLTF2 {
          * @param binaryWriter The binary data for the glTF file
          * @param binaryWriter The binary data for the glTF file
          */
          */
         private reorderVertexAttributeDataBasedOnPrimitiveMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter): void {
         private reorderVertexAttributeDataBasedOnPrimitiveMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter): void {
-            if (this.convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
+            if (this._convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
                 switch (primitiveMode) {
                 switch (primitiveMode) {
                     case Material.TriangleFillMode: {
                     case Material.TriangleFillMode: {
                         this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
                         this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
@@ -412,7 +435,7 @@ module BABYLON.GLTF2 {
          */
          */
         private writeVertexAttributeData(vertices: Vector2[] | Vector3[] | Vector4[], byteOffset: number, vertexAttributeKind: string, meshAttributeArray: FloatArray, binaryWriter: _BinaryWriter) {
         private writeVertexAttributeData(vertices: Vector2[] | Vector3[] | Vector4[], byteOffset: number, vertexAttributeKind: string, meshAttributeArray: FloatArray, binaryWriter: _BinaryWriter) {
             for (let vertex of vertices) {
             for (let vertex of vertices) {
-                if (this.convertToRightHandedSystem && !(vertexAttributeKind === VertexBuffer.ColorKind) && !(vertex instanceof Vector2)) {
+                if (this._convertToRightHandedSystem && !(vertexAttributeKind === VertexBuffer.ColorKind) && !(vertex instanceof Vector2)) {
                     if (vertex instanceof Vector3) {
                     if (vertex instanceof Vector3) {
                         (vertexAttributeKind === VertexBuffer.PositionKind) ? _GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : _GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
                         (vertexAttributeKind === VertexBuffer.PositionKind) ? _GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : _GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
                     }
                     }
@@ -445,7 +468,7 @@ module BABYLON.GLTF2 {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                         index = k * stride;
                         index = k * stride;
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
-                        if (this.convertToRightHandedSystem) {
+                        if (this._convertToRightHandedSystem) {
                             _GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
                             _GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
                         }
                         }
                         vertexAttributes.push(vertexData.asArray());
                         vertexAttributes.push(vertexData.asArray());
@@ -456,7 +479,7 @@ module BABYLON.GLTF2 {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                         index = k * stride;
                         index = k * stride;
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
-                        if (this.convertToRightHandedSystem) {
+                        if (this._convertToRightHandedSystem) {
                             _GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
                             _GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
                         }
                         }
                         vertexAttributes.push(vertexData.asArray());
                         vertexAttributes.push(vertexData.asArray());
@@ -467,7 +490,7 @@ module BABYLON.GLTF2 {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                         index = k * stride;
                         index = k * stride;
                         const vertexData = Vector4.FromArray(meshAttributeArray, index);
                         const vertexData = Vector4.FromArray(meshAttributeArray, index);
-                        if (this.convertToRightHandedSystem) {
+                        if (this._convertToRightHandedSystem) {
                             _GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
                             _GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
                         }
                         }
                         vertexAttributes.push(vertexData.asArray());
                         vertexAttributes.push(vertexData.asArray());
@@ -486,7 +509,7 @@ module BABYLON.GLTF2 {
                 case VertexBuffer.UV2Kind: {
                 case VertexBuffer.UV2Kind: {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                         index = k * stride;
                         index = k * stride;
-                        vertexAttributes.push(this.convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
+                        vertexAttributes.push(this._convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
                     }
                     }
                     break;
                     break;
                 }
                 }
@@ -510,61 +533,61 @@ module BABYLON.GLTF2 {
          * @returns json data as string
          * @returns json data as string
          */
          */
         private generateJSON(shouldUseGlb: boolean, glTFPrefix?: string, prettyPrint?: boolean): string {
         private generateJSON(shouldUseGlb: boolean, glTFPrefix?: string, prettyPrint?: boolean): string {
-            let buffer: IBuffer = { byteLength: this.totalByteLength };
+            let buffer: IBuffer = { byteLength: this._totalByteLength };
             let imageName: string;
             let imageName: string;
             let imageData: { data: Uint8Array, mimeType: ImageMimeType };
             let imageData: { data: Uint8Array, mimeType: ImageMimeType };
             let bufferView: IBufferView;
             let bufferView: IBufferView;
-            let byteOffset: number = this.totalByteLength;
+            let byteOffset: number = this._totalByteLength;
 
 
             let glTF: IGLTF = {
             let glTF: IGLTF = {
-                asset: this.asset
+                asset: this._asset
             };
             };
             if (buffer.byteLength) {
             if (buffer.byteLength) {
                 glTF.buffers = [buffer];
                 glTF.buffers = [buffer];
             }
             }
-            if (this.nodes && this.nodes.length) {
-                glTF.nodes = this.nodes;
+            if (this._nodes && this._nodes.length) {
+                glTF.nodes = this._nodes;
             }
             }
-            if (this.meshes && this.meshes.length) {
-                glTF.meshes = this.meshes;
+            if (this._meshes && this._meshes.length) {
+                glTF.meshes = this._meshes;
             }
             }
-            if (this.scenes && this.scenes.length) {
-                glTF.scenes = this.scenes;
+            if (this._scenes && this._scenes.length) {
+                glTF.scenes = this._scenes;
                 glTF.scene = 0;
                 glTF.scene = 0;
             }
             }
-            if (this.bufferViews && this.bufferViews.length) {
-                glTF.bufferViews = this.bufferViews;
+            if (this._bufferViews && this._bufferViews.length) {
+                glTF.bufferViews = this._bufferViews;
             }
             }
-            if (this.accessors && this.accessors.length) {
-                glTF.accessors = this.accessors;
+            if (this._accessors && this._accessors.length) {
+                glTF.accessors = this._accessors;
             }
             }
-            if (this.animations && this.animations.length) {
-                glTF.animations = this.animations;
+            if (this._animations && this._animations.length) {
+                glTF.animations = this._animations;
             }
             }
-            if (this.materials && this.materials.length) {
-                glTF.materials = this.materials;
+            if (this._materials && this._materials.length) {
+                glTF.materials = this._materials;
             }
             }
-            if (this.textures && this.textures.length) {
-                glTF.textures = this.textures;
+            if (this._textures && this._textures.length) {
+                glTF.textures = this._textures;
             }
             }
-            if (this.samplers && this.samplers.length) {
-                glTF.samplers = this.samplers;
+            if (this._samplers && this._samplers.length) {
+                glTF.samplers = this._samplers;
             }
             }
-            if (this.images && this.images.length) {
+            if (this._images && this._images.length) {
                 if (!shouldUseGlb) {
                 if (!shouldUseGlb) {
-                    glTF.images = this.images;
+                    glTF.images = this._images;
                 }
                 }
                 else {
                 else {
                     glTF.images = [];
                     glTF.images = [];
 
 
-                    this.images.forEach((image) => {
+                    this._images.forEach((image) => {
                         if (image.uri) {
                         if (image.uri) {
-                            imageData = this.imageData[image.uri];
+                            imageData = this._imageData[image.uri];
                             imageName = image.uri.split('.')[0] + " image";
                             imageName = image.uri.split('.')[0] + " image";
                             bufferView = _GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                             bufferView = _GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                             byteOffset += imageData.data.buffer.byteLength;
                             byteOffset += imageData.data.buffer.byteLength;
-                            this.bufferViews.push(bufferView);
-                            image.bufferView = this.bufferViews.length - 1;
+                            this._bufferViews.push(bufferView);
+                            image.bufferView = this._bufferViews.length - 1;
                             image.name = imageName;
                             image.name = imageName;
                             image.mimeType = imageData.mimeType;
                             image.mimeType = imageData.mimeType;
                             image.uri = undefined;
                             image.uri = undefined;
@@ -606,9 +629,9 @@ module BABYLON.GLTF2 {
                 container.glTFFiles[glTFFileName] = jsonText;
                 container.glTFFiles[glTFFileName] = jsonText;
                 container.glTFFiles[glTFBinFile] = bin;
                 container.glTFFiles[glTFBinFile] = bin;
 
 
-                if (this.imageData) {
-                    for (let image in this.imageData) {
-                        container.glTFFiles[image] = new Blob([this.imageData[image].data], { type: this.imageData[image].mimeType });
+                if (this._imageData) {
+                    for (let image in this._imageData) {
+                        container.glTFFiles[image] = new Blob([this._imageData[image].data], { type: this._imageData[image].mimeType });
                     }
                     }
                 }
                 }
 
 
@@ -623,7 +646,10 @@ module BABYLON.GLTF2 {
          */
          */
         private _generateBinaryAsync(): Promise<ArrayBuffer> {
         private _generateBinaryAsync(): Promise<ArrayBuffer> {
             let binaryWriter = new _BinaryWriter(4);
             let binaryWriter = new _BinaryWriter(4);
-            return this.createSceneAsync(this.babylonScene, binaryWriter).then(() => {
+            return this.createSceneAsync(this._babylonScene, binaryWriter).then(() => {
+                if (this._localEngine) {
+                    this._localEngine.dispose();
+                }
                 return binaryWriter.getArrayBuffer();
                 return binaryWriter.getArrayBuffer();
             });
             });
         }
         }
@@ -655,8 +681,8 @@ module BABYLON.GLTF2 {
                 const jsonLength = jsonText.length;
                 const jsonLength = jsonText.length;
                 let imageByteLength = 0;
                 let imageByteLength = 0;
 
 
-                for (let key in this.imageData) {
-                    imageByteLength += this.imageData[key].data.byteLength;
+                for (let key in this._imageData) {
+                    imageByteLength += this._imageData[key].data.byteLength;
                 }
                 }
                 const jsonPadding = this._getPadding(jsonLength);
                 const jsonPadding = this._getPadding(jsonLength);
                 const binPadding = this._getPadding(binaryBuffer.byteLength);
                 const binPadding = this._getPadding(binaryBuffer.byteLength);
@@ -711,8 +737,8 @@ module BABYLON.GLTF2 {
                 const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
                 const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
 
 
                 // binary data
                 // binary data
-                for (let key in this.imageData) {
-                    glbData.push(this.imageData[key].data.buffer);
+                for (let key in this._imageData) {
+                    glbData.push(this._imageData[key].data.buffer);
                 }
                 }
                 glbData.push(binPaddingBuffer);
                 glbData.push(binPaddingBuffer);
 
 
@@ -723,6 +749,8 @@ module BABYLON.GLTF2 {
                 const container = new GLTFData();
                 const container = new GLTFData();
                 container.glTFFiles[glbFileName] = glbFile;
                 container.glTFFiles[glbFileName] = glbFile;
 
 
+                this._localEngine.dispose();
+
                 return container;
                 return container;
             });
             });
         }
         }
@@ -734,7 +762,7 @@ module BABYLON.GLTF2 {
          */
          */
         private setNodeTransformation(node: INode, babylonTransformNode: TransformNode): void {
         private setNodeTransformation(node: INode, babylonTransformNode: TransformNode): void {
             if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
             if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                node.translation = this.convertToRightHandedSystem ? _GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                node.translation = this._convertToRightHandedSystem ? _GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
             }
             }
 
 
             if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
             if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
@@ -746,7 +774,7 @@ module BABYLON.GLTF2 {
                 rotationQuaternion.multiplyInPlace(babylonTransformNode.rotationQuaternion);
                 rotationQuaternion.multiplyInPlace(babylonTransformNode.rotationQuaternion);
             }
             }
             if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
             if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
-                if (this.convertToRightHandedSystem) {
+                if (this._convertToRightHandedSystem) {
                     _GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
                     _GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
 
 
                 }
                 }
@@ -781,7 +809,7 @@ module BABYLON.GLTF2 {
                 if (vertexData) {
                 if (vertexData) {
                     const byteLength = vertexData.length * 4;
                     const byteLength = vertexData.length * 4;
                     const bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                     const bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
-                    this.bufferViews.push(bufferView);
+                    this._bufferViews.push(bufferView);
 
 
                     this.writeAttributeData(
                     this.writeAttributeData(
                         kind,
                         kind,
@@ -854,27 +882,27 @@ module BABYLON.GLTF2 {
         private setAttributeKind(meshPrimitive: IMeshPrimitive, attributeKind: string): void {
         private setAttributeKind(meshPrimitive: IMeshPrimitive, attributeKind: string): void {
             switch (attributeKind) {
             switch (attributeKind) {
                 case VertexBuffer.PositionKind: {
                 case VertexBuffer.PositionKind: {
-                    meshPrimitive.attributes.POSITION = this.accessors.length - 1;
+                    meshPrimitive.attributes.POSITION = this._accessors.length - 1;
                     break;
                     break;
                 }
                 }
                 case VertexBuffer.NormalKind: {
                 case VertexBuffer.NormalKind: {
-                    meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                    meshPrimitive.attributes.NORMAL = this._accessors.length - 1;
                     break;
                     break;
                 }
                 }
                 case VertexBuffer.ColorKind: {
                 case VertexBuffer.ColorKind: {
-                    meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
+                    meshPrimitive.attributes.COLOR_0 = this._accessors.length - 1;
                     break;
                     break;
                 }
                 }
                 case VertexBuffer.TangentKind: {
                 case VertexBuffer.TangentKind: {
-                    meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
+                    meshPrimitive.attributes.TANGENT = this._accessors.length - 1;
                     break;
                     break;
                 }
                 }
                 case VertexBuffer.UVKind: {
                 case VertexBuffer.UVKind: {
-                    meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                    meshPrimitive.attributes.TEXCOORD_0 = this._accessors.length - 1;
                     break;
                     break;
                 }
                 }
                 case VertexBuffer.UV2Kind: {
                 case VertexBuffer.UV2Kind: {
-                    meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                    meshPrimitive.attributes.TEXCOORD_1 = this._accessors.length - 1;
                     break;
                     break;
                 }
                 }
                 default: {
                 default: {
@@ -926,7 +954,7 @@ module BABYLON.GLTF2 {
                         }
                         }
 
 
                         this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride);
                         this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride);
-                        attribute.bufferViewIndex = this.bufferViews.length - 1;
+                        attribute.bufferViewIndex = this._bufferViews.length - 1;
                         vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                         vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                     }
                     }
                 }
                 }
@@ -936,8 +964,8 @@ module BABYLON.GLTF2 {
                     if (indices) {
                     if (indices) {
                         const byteLength = indices.length * 4;
                         const byteLength = indices.length * 4;
                         bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                         bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
-                        this.bufferViews.push(bufferView);
-                        indexBufferViewIndex = this.bufferViews.length - 1;
+                        this._bufferViews.push(bufferView);
+                        indexBufferViewIndex = this._bufferViews.length - 1;
 
 
                         for (let k = 0, length = indices.length; k < length; ++k) {
                         for (let k = 0, length = indices.length; k < length; ++k) {
                             binaryWriter.setUInt32(indices[k]);
                             binaryWriter.setUInt32(indices[k]);
@@ -963,21 +991,21 @@ module BABYLON.GLTF2 {
                                         baseColorFactor: bufferMesh.color.asArray().concat([bufferMesh.alpha])
                                         baseColorFactor: bufferMesh.color.asArray().concat([bufferMesh.alpha])
                                     }
                                     }
                                 }
                                 }
-                                this.materials.push(material);
-                                materialIndex = this.materials.length - 1;
+                                this._materials.push(material);
+                                materialIndex = this._materials.length - 1;
                             }
                             }
                             else if (babylonMaterial instanceof MultiMaterial) {
                             else if (babylonMaterial instanceof MultiMaterial) {
                                 babylonMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
                                 babylonMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
                                 if (babylonMaterial) {
                                 if (babylonMaterial) {
-                                    materialIndex = this.materialMap[babylonMaterial.uniqueId];
+                                    materialIndex = this._materialMap[babylonMaterial.uniqueId];
                                 }
                                 }
                             }
                             }
                             else {
                             else {
-                                materialIndex = this.materialMap[babylonMaterial.uniqueId];
+                                materialIndex = this._materialMap[babylonMaterial.uniqueId];
                             }
                             }
                         }
                         }
 
 
-                        let glTFMaterial: Nullable<IMaterial> = materialIndex != null ? this.materials[materialIndex] : null;
+                        let glTFMaterial: Nullable<IMaterial> = materialIndex != null ? this._materials[materialIndex] : null;
 
 
                         const meshPrimitive: IMeshPrimitive = { attributes: {} };
                         const meshPrimitive: IMeshPrimitive = { attributes: {} };
                         this.setPrimitiveMode(meshPrimitive, primitiveMode);
                         this.setPrimitiveMode(meshPrimitive, primitiveMode);
@@ -985,7 +1013,7 @@ module BABYLON.GLTF2 {
                         for (const attribute of attributeData) {
                         for (const attribute of attributeData) {
                             const attributeKind = attribute.kind;
                             const attributeKind = attribute.kind;
                             if (attributeKind === VertexBuffer.UVKind || attributeKind === VertexBuffer.UV2Kind) {
                             if (attributeKind === VertexBuffer.UVKind || attributeKind === VertexBuffer.UV2Kind) {
-                                if (glTFMaterial && !_GLTFMaterial._HasTexturesPresent(glTFMaterial)) {
+                                if (glTFMaterial && !this._glTFMaterialExporter._hasTexturesPresent(glTFMaterial)) {
                                     continue;
                                     continue;
                                 }
                                 }
                             }
                             }
@@ -998,10 +1026,10 @@ module BABYLON.GLTF2 {
                                     if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                     if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                         minMax = { min: null, max: null };
                                         minMax = { min: null, max: null };
                                         if (attributeKind == VertexBuffer.PositionKind) {
                                         if (attributeKind == VertexBuffer.PositionKind) {
-                                            minMax = _GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this.convertToRightHandedSystem);
+                                            minMax = _GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                         }
                                         }
                                         const accessor = _GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, AccessorComponentType.FLOAT, vertexData.length / stride, 0, minMax.min, minMax.max);
                                         const accessor = _GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, AccessorComponentType.FLOAT, vertexData.length / stride, 0, minMax.min, minMax.max);
-                                        this.accessors.push(accessor);
+                                        this._accessors.push(accessor);
                                         this.setAttributeKind(meshPrimitive, attributeKind);
                                         this.setAttributeKind(meshPrimitive, attributeKind);
                                         if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
                                         if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
                                             uvCoordsPresent = true;
                                             uvCoordsPresent = true;
@@ -1013,15 +1041,15 @@ module BABYLON.GLTF2 {
                         if (indexBufferViewIndex) {
                         if (indexBufferViewIndex) {
                             // Create accessor
                             // Create accessor
                             const accessor = _GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, AccessorType.SCALAR, AccessorComponentType.UNSIGNED_INT, submesh.indexCount, submesh.indexStart * 4, null, null);
                             const accessor = _GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, AccessorType.SCALAR, AccessorComponentType.UNSIGNED_INT, submesh.indexCount, submesh.indexStart * 4, null, null);
-                            this.accessors.push(accessor);
-                            meshPrimitive.indices = this.accessors.length - 1;
+                            this._accessors.push(accessor);
+                            meshPrimitive.indices = this._accessors.length - 1;
                         }
                         }
                         if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
                         if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
-                            let sideOrientation = this.babylonScene.materials[materialIndex].sideOrientation;
+                            let sideOrientation = this._babylonScene.materials[materialIndex].sideOrientation;
 
 
-                            if (this.convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
+                            if (this._convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
                                 //Overwrite the indices to be counter-clockwise
                                 //Overwrite the indices to be counter-clockwise
-                                let byteOffset = indexBufferViewIndex != null ? this.bufferViews[indexBufferViewIndex].byteOffset : null;
+                                let byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
                                 if (byteOffset == null) { byteOffset = 0; }
                                 if (byteOffset == null) { byteOffset = 0; }
                                 let babylonIndices: Nullable<IndicesArray> = null;
                                 let babylonIndices: Nullable<IndicesArray> = null;
                                 if (indexBufferViewIndex != null) {
                                 if (indexBufferViewIndex != null) {
@@ -1034,7 +1062,7 @@ module BABYLON.GLTF2 {
                                     for (let attribute of attributeData) {
                                     for (let attribute of attributeData) {
                                         let vertexData = bufferMesh.getVerticesData(attribute.kind);
                                         let vertexData = bufferMesh.getVerticesData(attribute.kind);
                                         if (vertexData) {
                                         if (vertexData) {
-                                            let byteOffset = this.bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset;
+                                            let byteOffset = this._bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset;
                                             if (!byteOffset) {
                                             if (!byteOffset) {
                                                 byteOffset = 0
                                                 byteOffset = 0
                                             }
                                             }
@@ -1044,10 +1072,10 @@ module BABYLON.GLTF2 {
                                 }
                                 }
                             }
                             }
 
 
-                            if (!uvCoordsPresent && _GLTFMaterial._HasTexturesPresent(this.materials[materialIndex])) {
-                                const newMat = _GLTFMaterial._StripTexturesFromMaterial(this.materials[materialIndex]);
-                                this.materials.push(newMat);
-                                materialIndex = this.materials.length - 1;
+                            if (!uvCoordsPresent && this._glTFMaterialExporter._hasTexturesPresent(this._materials[materialIndex])) {
+                                const newMat = this._glTFMaterialExporter._stripTexturesFromMaterial(this._materials[materialIndex]);
+                                this._materials.push(newMat);
+                                materialIndex = this._materials.length - 1;
                             }
                             }
 
 
                             meshPrimitive.material = materialIndex;
                             meshPrimitive.material = materialIndex;
@@ -1072,23 +1100,23 @@ module BABYLON.GLTF2 {
             let directDescendents: Node[];
             let directDescendents: Node[];
             const nodes = [...babylonScene.transformNodes, ...babylonScene.meshes];
             const nodes = [...babylonScene.transformNodes, ...babylonScene.meshes];
 
 
-            return _GLTFMaterial._ConvertMaterialsToGLTFAsync(babylonScene.materials, ImageMimeType.PNG, this.images, this.textures, this.samplers, this.materials, this.materialMap, this.imageData, true).then(() => {
-                this.nodeMap = this.createNodeMapAndAnimations(babylonScene, nodes, this.shouldExportTransformNode, binaryWriter);
+            return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, ImageMimeType.PNG, true).then(() => {
+                this._nodeMap = this.createNodeMapAndAnimations(babylonScene, nodes, this._shouldExportTransformNode, binaryWriter);
 
 
-                this.totalByteLength = binaryWriter.getByteOffset();
+                this._totalByteLength = binaryWriter.getByteOffset();
 
 
 
 
                 // Build Hierarchy with the node map.
                 // Build Hierarchy with the node map.
                 for (let babylonTransformNode of nodes) {
                 for (let babylonTransformNode of nodes) {
-                    glTFNodeIndex = this.nodeMap[babylonTransformNode.uniqueId];
+                    glTFNodeIndex = this._nodeMap[babylonTransformNode.uniqueId];
                     if (glTFNodeIndex != null) {
                     if (glTFNodeIndex != null) {
-                        glTFNode = this.nodes[glTFNodeIndex];
+                        glTFNode = this._nodes[glTFNodeIndex];
                         if (!babylonTransformNode.parent) {
                         if (!babylonTransformNode.parent) {
-                            if (!this.shouldExportTransformNode(babylonTransformNode)) {
+                            if (!this._shouldExportTransformNode(babylonTransformNode)) {
                                 Tools.Log("Omitting " + babylonTransformNode.name + " from scene.");
                                 Tools.Log("Omitting " + babylonTransformNode.name + " from scene.");
                             }
                             }
                             else {
                             else {
-                                if (this.convertToRightHandedSystem) {
+                                if (this._convertToRightHandedSystem) {
                                     if (glTFNode.translation) {
                                     if (glTFNode.translation) {
                                         glTFNode.translation[2] *= -1;
                                         glTFNode.translation[2] *= -1;
                                         glTFNode.translation[0] *= -1;
                                         glTFNode.translation[0] *= -1;
@@ -1104,15 +1132,15 @@ module BABYLON.GLTF2 {
                         if (!glTFNode.children && directDescendents && directDescendents.length) {
                         if (!glTFNode.children && directDescendents && directDescendents.length) {
                             glTFNode.children = [];
                             glTFNode.children = [];
                             for (let descendent of directDescendents) {
                             for (let descendent of directDescendents) {
-                                if (this.nodeMap[descendent.uniqueId] != null) {
-                                    glTFNode.children.push(this.nodeMap[descendent.uniqueId]);
+                                if (this._nodeMap[descendent.uniqueId] != null) {
+                                    glTFNode.children.push(this._nodeMap[descendent.uniqueId]);
                                 }
                                 }
                             }
                             }
                         }
                         }
                     }
                     }
                 };
                 };
                 if (scene.nodes.length) {
                 if (scene.nodes.length) {
-                    this.scenes.push(scene);
+                    this._scenes.push(scene);
                 }
                 }
             });
             });
         }
         }
@@ -1140,12 +1168,12 @@ module BABYLON.GLTF2 {
                 if (shouldExportTransformNode(babylonTransformNode)) {
                 if (shouldExportTransformNode(babylonTransformNode)) {
                     node = this.createNode(babylonTransformNode, binaryWriter);
                     node = this.createNode(babylonTransformNode, binaryWriter);
 
 
-                    this.nodes.push(node);
-                    nodeIndex = this.nodes.length - 1;
+                    this._nodes.push(node);
+                    nodeIndex = this._nodes.length - 1;
                     nodeMap[babylonTransformNode.uniqueId] = nodeIndex;
                     nodeMap[babylonTransformNode.uniqueId] = nodeIndex;
 
 
                     if (!babylonScene.animationGroups.length && babylonTransformNode.animations.length) {
                     if (!babylonScene.animationGroups.length && babylonTransformNode.animations.length) {
-                        _GLTFAnimation._CreateNodeAnimationFromTransformNodeAnimations(babylonTransformNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this.nodes, binaryWriter, this.bufferViews, this.accessors, this.convertToRightHandedSystem, this.animationSampleRate);
+                        _GLTFAnimation._CreateNodeAnimationFromTransformNodeAnimations(babylonTransformNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, this._convertToRightHandedSystem, this._animationSampleRate);
                     }
                     }
                 }
                 }
                 else {
                 else {
@@ -1154,16 +1182,16 @@ module BABYLON.GLTF2 {
             };
             };
 
 
             if (runtimeGLTFAnimation.channels.length && runtimeGLTFAnimation.samplers.length) {
             if (runtimeGLTFAnimation.channels.length && runtimeGLTFAnimation.samplers.length) {
-                this.animations.push(runtimeGLTFAnimation);
+                this._animations.push(runtimeGLTFAnimation);
             }
             }
             idleGLTFAnimations.forEach((idleGLTFAnimation) => {
             idleGLTFAnimations.forEach((idleGLTFAnimation) => {
                 if (idleGLTFAnimation.channels.length && idleGLTFAnimation.samplers.length) {
                 if (idleGLTFAnimation.channels.length && idleGLTFAnimation.samplers.length) {
-                    this.animations.push(idleGLTFAnimation);
+                    this._animations.push(idleGLTFAnimation);
                 }
                 }
             });
             });
 
 
             if (babylonScene.animationGroups.length) {
             if (babylonScene.animationGroups.length) {
-                _GLTFAnimation._CreateNodeAnimationFromAnimationGroups(babylonScene, this.animations, nodeMap, this.nodes, binaryWriter, this.bufferViews, this.accessors, this.convertToRightHandedSystem, this.animationSampleRate);
+                _GLTFAnimation._CreateNodeAnimationFromAnimationGroups(babylonScene, this._animations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, this._convertToRightHandedSystem, this._animationSampleRate);
             }
             }
 
 
             return nodeMap;
             return nodeMap;
@@ -1190,8 +1218,8 @@ module BABYLON.GLTF2 {
             this.setPrimitiveAttributes(mesh, babylonTransformNode, binaryWriter);
             this.setPrimitiveAttributes(mesh, babylonTransformNode, binaryWriter);
 
 
             if (mesh.primitives.length) {
             if (mesh.primitives.length) {
-                this.meshes.push(mesh);
-                node.mesh = this.meshes.length - 1;
+                this._meshes.push(mesh);
+                node.mesh = this._meshes.length - 1;
             }
             }
 
 
             return node;
             return node;

File diff suppressed because it is too large
+ 229 - 208
serializers/src/glTF/2.0/babylon.glTFMaterial.ts


+ 0 - 10
serializers/src/glTF/2.0/shaders/setAlphaToOne.fragment.fx

@@ -1,10 +0,0 @@
-precision highp float;
-
-uniform sampler2D textureSampler;
-
-varying vec2 vUV;
-
-void main(void) {
-    vec4 color = texture2D(textureSampler, vUV);
-    gl_FragColor = vec4(color.rgb, 1.0);
-}

+ 10 - 2
src/Animations/babylon.runtimeAnimation.ts

@@ -399,9 +399,17 @@
                 this._stopped = true;
                 this._stopped = true;
                 return false;
                 return false;
             }
             }
-            var returnValue = true;
+
             let keys = this._animation.getKeys();
             let keys = this._animation.getKeys();
 
 
+            // Return immediately if there is only one key frame.
+            if (keys.length === 1) {
+                this.setValue(keys[0].value, weight);
+                return !loop;
+            }
+
+            var returnValue = true;
+
             // Adding a start key at frame 0 if missing
             // Adding a start key at frame 0 if missing
             if (keys[0].frame !== 0) {
             if (keys[0].frame !== 0) {
                 var newKey = { frame: 0, value: keys[0].value };
                 var newKey = { frame: 0, value: keys[0].value };
@@ -424,7 +432,7 @@
                     to++;
                     to++;
                 }
                 }
             }
             }
-            
+
             // Compute ratio
             // Compute ratio
             var range = to - from;
             var range = to - from;
             var offsetValue;
             var offsetValue;

+ 49 - 16
src/Audio/babylon.sound.ts

@@ -42,12 +42,12 @@ module BABYLON {
         private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
         private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
         private _isOutputConnected = false;
         private _isOutputConnected = false;
         private _htmlAudioElement: HTMLAudioElement;
         private _htmlAudioElement: HTMLAudioElement;
-        private _urlType: string = "Unknown";
+        private _urlType: 'Unknown' | 'String' | 'Array' | 'ArrayBuffer' | 'MediaStream' = "Unknown";
 
 
         /**
         /**
         * Create a sound and attach it to a scene
         * Create a sound and attach it to a scene
         * @param name Name of your sound 
         * @param name Name of your sound 
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer 
+        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
         */
         */
@@ -95,14 +95,33 @@ module BABYLON {
                 // if no parameter is passed, you need to call setAudioBuffer yourself to prepare the sound
                 // if no parameter is passed, you need to call setAudioBuffer yourself to prepare the sound
                 if (urlOrArrayBuffer) {
                 if (urlOrArrayBuffer) {
                     try {
                     try {
-                        if (typeof (urlOrArrayBuffer) === "string") this._urlType = "String";
-                        if (Array.isArray(urlOrArrayBuffer)) this._urlType = "Array";
-                        if (urlOrArrayBuffer instanceof ArrayBuffer) this._urlType = "ArrayBuffer";
+                        if (typeof (urlOrArrayBuffer) === "string") {
+                            this._urlType = "String";
+                        } else if (urlOrArrayBuffer instanceof ArrayBuffer) {
+                            this._urlType = "ArrayBuffer";
+                        } else if (urlOrArrayBuffer instanceof MediaStream) {
+                            this._urlType = "MediaStream";
+                        } else if (Array.isArray(urlOrArrayBuffer)) {
+                            this._urlType = "Array";
+                        }
 
 
                         var urls: string[] = [];
                         var urls: string[] = [];
                         var codecSupportedFound = false;
                         var codecSupportedFound = false;
 
 
                         switch (this._urlType) {
                         switch (this._urlType) {
+                            case "MediaStream":
+                                this._streaming = true;
+                                this._isReadyToPlay = true;
+                                this._streamingSource = Engine.audioEngine.audioContext.createMediaStreamSource(urlOrArrayBuffer);
+
+                                if (this.autoplay) {
+                                    this.play();
+                                }
+
+                                if (this._readyToPlayCallback) {
+                                    this._readyToPlayCallback();
+                                }
+                                break;
                             case "ArrayBuffer":
                             case "ArrayBuffer":
                                 if ((<ArrayBuffer>urlOrArrayBuffer).byteLength > 0) {
                                 if ((<ArrayBuffer>urlOrArrayBuffer).byteLength > 0) {
                                     codecSupportedFound = true;
                                     codecSupportedFound = true;
@@ -131,8 +150,8 @@ module BABYLON {
                                     if (codecSupportedFound) {
                                     if (codecSupportedFound) {
                                         // Loading sound using XHR2
                                         // Loading sound using XHR2
                                         if (!this._streaming) {
                                         if (!this._streaming) {
-                                            this._scene._loadFile(url, (data) => { 
-                                                this._soundLoaded(data as ArrayBuffer); 
+                                            this._scene._loadFile(url, (data) => {
+                                                this._soundLoaded(data as ArrayBuffer);
                                             }, undefined, true, true, (exception) => {
                                             }, undefined, true, true, (exception) => {
                                                 if (exception) {
                                                 if (exception) {
                                                     Tools.Error("XHR " + exception.status + " error on: " + url + ".");
                                                     Tools.Error("XHR " + exception.status + " error on: " + url + ".");
@@ -210,7 +229,7 @@ module BABYLON {
         }
         }
 
 
         public dispose() {
         public dispose() {
-            if (Engine.audioEngine.canUseWebAudio) { 
+            if (Engine.audioEngine.canUseWebAudio) {
                 if (this.isPlaying) {
                 if (this.isPlaying) {
                     this.stop();
                     this.stop();
                 }
                 }
@@ -241,6 +260,10 @@ module BABYLON {
                     document.body.removeChild(this._htmlAudioElement);
                     document.body.removeChild(this._htmlAudioElement);
                 }
                 }
 
 
+                if (this._streamingSource) {
+                    this._streamingSource.disconnect();
+                }
+
                 if (this._connectedMesh && this._registerFunc) {
                 if (this._connectedMesh && this._registerFunc) {
                     this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                     this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                     this._connectedMesh = null;
                     this._connectedMesh = null;
@@ -282,7 +305,7 @@ module BABYLON {
                 this._playbackRate = options.playbackRate || this._playbackRate;
                 this._playbackRate = options.playbackRate || this._playbackRate;
                 this._updateSpatialParameters();
                 this._updateSpatialParameters();
                 if (this.isPlaying) {
                 if (this.isPlaying) {
-                    if (this._streaming) {
+                    if (this._streaming && this._htmlAudioElement) {
                         this._htmlAudioElement.playbackRate = this._playbackRate;
                         this._htmlAudioElement.playbackRate = this._playbackRate;
                     }
                     }
                     else {
                     else {
@@ -449,7 +472,9 @@ module BABYLON {
                         }
                         }
                         this._streamingSource.disconnect();
                         this._streamingSource.disconnect();
                         this._streamingSource.connect(this._inputAudioNode);
                         this._streamingSource.connect(this._inputAudioNode);
-                        this._htmlAudioElement.play();
+                        if (this._htmlAudioElement) {
+                            this._htmlAudioElement.play();
+                        }
                     }
                     }
                     else {
                     else {
                         this._soundSource = Engine.audioEngine.audioContext.createBufferSource();
                         this._soundSource = Engine.audioEngine.audioContext.createBufferSource();
@@ -486,10 +511,14 @@ module BABYLON {
         public stop(time?: number) {
         public stop(time?: number) {
             if (this.isPlaying) {
             if (this.isPlaying) {
                 if (this._streaming) {
                 if (this._streaming) {
-                    this._htmlAudioElement.pause();
-                    // Test needed for Firefox or it will generate an Invalid State Error
-                    if (this._htmlAudioElement.currentTime > 0) {
-                        this._htmlAudioElement.currentTime = 0;
+                    if (this._htmlAudioElement) {
+                        this._htmlAudioElement.pause();
+                        // Test needed for Firefox or it will generate an Invalid State Error
+                        if (this._htmlAudioElement.currentTime > 0) {
+                            this._htmlAudioElement.currentTime = 0;
+                        }
+                    } else {
+                        this._streamingSource.disconnect();
                     }
                     }
                 }
                 }
                 else if (Engine.audioEngine.audioContext && this._soundSource) {
                 else if (Engine.audioEngine.audioContext && this._soundSource) {
@@ -508,7 +537,11 @@ module BABYLON {
             if (this.isPlaying) {
             if (this.isPlaying) {
                 this.isPaused = true;
                 this.isPaused = true;
                 if (this._streaming) {
                 if (this._streaming) {
-                    this._htmlAudioElement.pause();
+                    if (this._htmlAudioElement) {
+                        this._htmlAudioElement.pause();
+                    } else {
+                        this._streamingSource.disconnect();
+                    }
                 }
                 }
                 else if (Engine.audioEngine.audioContext) {
                 else if (Engine.audioEngine.audioContext) {
                     this.stop(0);
                     this.stop(0);
@@ -534,7 +567,7 @@ module BABYLON {
         public setPlaybackRate(newPlaybackRate: number) {
         public setPlaybackRate(newPlaybackRate: number) {
             this._playbackRate = newPlaybackRate;
             this._playbackRate = newPlaybackRate;
             if (this.isPlaying) {
             if (this.isPlaying) {
-                if (this._streaming) {
+                if (this._streaming && this._htmlAudioElement) {
                     this._htmlAudioElement.playbackRate = this._playbackRate;
                     this._htmlAudioElement.playbackRate = this._playbackRate;
                 }
                 }
                 else if (this._soundSource) {
                 else if (this._soundSource) {

+ 9 - 3
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -24,6 +24,10 @@ module BABYLON {
          * The distance towards the target drag position to move each frame. This can be useful to avoid jitter. Set this to 1 for no delay. (Default: 0.2)
          * The distance towards the target drag position to move each frame. This can be useful to avoid jitter. Set this to 1 for no delay. (Default: 0.2)
          */
          */
         public dragDeltaRatio = 0.2;
         public dragDeltaRatio = 0.2;
+        /**
+         * If the drag plane orientation should be updated during the dragging (Default: true)
+         */
+        public updateDragPlane = true;
         // Debug mode will display drag planes to help visualize behavior
         // Debug mode will display drag planes to help visualize behavior
         private _debugMode = false;
         private _debugMode = false;
         private _moving = false;
         private _moving = false;
@@ -149,8 +153,10 @@ module BABYLON {
                         var pickedPoint = this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
                         var pickedPoint = this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
                         
                         
                         if (pickedPoint) {
                         if (pickedPoint) {
-                            this._updateDragPlanePosition(pointerInfo.pickInfo.ray, pickedPoint);
-
+                            if(this.updateDragPlane){
+                                this._updateDragPlanePosition(pointerInfo.pickInfo.ray, pickedPoint);
+                            }
+                            
                             // depending on the drag mode option drag accordingly
                             // depending on the drag mode option drag accordingly
                             if(this.options.dragAxis){
                             if(this.options.dragAxis){
                                 // Convert local drag axis to world
                                 // Convert local drag axis to world
@@ -209,7 +215,7 @@ module BABYLON {
         private _lineA = new Vector3(0,0,0);
         private _lineA = new Vector3(0,0,0);
         private _lineB = new Vector3(0,0,0);
         private _lineB = new Vector3(0,0,0);
         private _localAxis = new Vector3(0,0,0);
         private _localAxis = new Vector3(0,0,0);
-        private _lookAt = new Vector3(0,0,0); 
+        private _lookAt = new Vector3(0,0,0);
         // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
         // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
         private _updateDragPlanePosition(ray:Ray, dragPlanePosition:Vector3){
         private _updateDragPlanePosition(ray:Ray, dragPlanePosition:Vector3){
             this._pointA.copyFrom(dragPlanePosition);
             this._pointA.copyFrom(dragPlanePosition);

+ 34 - 6
src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts

@@ -11,6 +11,8 @@ module BABYLON {
         private _virtualOriginMesh:AbstractMesh;
         private _virtualOriginMesh:AbstractMesh;
         private _virtualDragMesh:AbstractMesh;
         private _virtualDragMesh:AbstractMesh;
         private _pointerObserver:Nullable<Observer<PointerInfo>>;
         private _pointerObserver:Nullable<Observer<PointerInfo>>;
+        private _moving = false;
+        private _startingOrientation = new Quaternion();
         /**
         /**
          * How much faster the object should move when the controller is moving towards it. This is useful to bring objects that are far away from the user to them faster. Set this to 0 to avoid any speed increase. (Default: 5)
          * How much faster the object should move when the controller is moving towards it. This is useful to bring objects that are far away from the user to them faster. Set this to 0 to avoid any speed increase. (Default: 5)
          */
          */
@@ -83,18 +85,23 @@ module BABYLON {
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh);
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh);
                         this._virtualDragMesh.position.copyFrom(pickedMesh.absolutePosition);
                         this._virtualDragMesh.position.copyFrom(pickedMesh.absolutePosition);
                         if(!pickedMesh.rotationQuaternion){
                         if(!pickedMesh.rotationQuaternion){
-                            pickedMesh.rotationQuaternion = new Quaternion();
+                            pickedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(pickedMesh.rotation.y,pickedMesh.rotation.x,pickedMesh.rotation.z);
                         }
                         }
+                        var oldParent=pickedMesh.parent;
+                        pickedMesh.setParent(null);
                         this._virtualDragMesh.rotationQuaternion!.copyFrom(pickedMesh.rotationQuaternion);
                         this._virtualDragMesh.rotationQuaternion!.copyFrom(pickedMesh.rotationQuaternion);
+                        pickedMesh.setParent(oldParent);
                         this._virtualOriginMesh.addChild(this._virtualDragMesh);
                         this._virtualOriginMesh.addChild(this._virtualDragMesh);
-
+                        
                         // Update state
                         // Update state
+                        this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
                         this.dragging = true;
                         this.dragging = true;
                         this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
                         this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
                     }
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
                         this.dragging = false;
                         this.dragging = false;
+                        this._moving = false;
                         this.currentDraggingPointerID = -1;
                         this.currentDraggingPointerID = -1;
                         pickedMesh = null;
                         pickedMesh = null;
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh);
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh);
@@ -105,7 +112,7 @@ module BABYLON {
                         var originDragDifference = pointerInfo.pickInfo.ray.origin.subtract(lastSixDofOriginPosition);
                         var originDragDifference = pointerInfo.pickInfo.ray.origin.subtract(lastSixDofOriginPosition);
                         lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
                         lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
                         var localOriginDragDifference = Vector3.TransformCoordinates(originDragDifference, Matrix.Invert(this._virtualOriginMesh.getWorldMatrix().getRotationMatrix()));
                         var localOriginDragDifference = Vector3.TransformCoordinates(originDragDifference, Matrix.Invert(this._virtualOriginMesh.getWorldMatrix().getRotationMatrix()));
-                        
+
                         this._virtualOriginMesh.addChild(this._virtualDragMesh);
                         this._virtualOriginMesh.addChild(this._virtualDragMesh);
                         // Determine how much the controller moved to/away towards the dragged object and use this to move the object further when its further away
                         // Determine how much the controller moved to/away towards the dragged object and use this to move the object further when its further away
                         var zDragDistance = Vector3.Dot(localOriginDragDifference, this._virtualOriginMesh.position.normalizeToNew());
                         var zDragDistance = Vector3.Dot(localOriginDragDifference, this._virtualOriginMesh.position.normalizeToNew());
@@ -113,26 +120,47 @@ module BABYLON {
                         if(this._virtualDragMesh.position.z < 0){
                         if(this._virtualDragMesh.position.z < 0){
                             this._virtualDragMesh.position.z = 0;
                             this._virtualDragMesh.position.z = 0;
                         }
                         }
-                        
+
                         // Update the controller position
                         // Update the controller position
                         this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
                         this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
                         this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.subtract(pointerInfo.pickInfo.ray.direction));
                         this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.subtract(pointerInfo.pickInfo.ray.direction));
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh)
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh)
-                    
+                        
                         // Move the virtualObjectsPosition into the picked mesh's space if needed
                         // Move the virtualObjectsPosition into the picked mesh's space if needed
                         this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
                         this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
                         if(pickedMesh.parent){
                         if(pickedMesh.parent){
                             Vector3.TransformCoordinatesToRef(this._targetPosition, Matrix.Invert(pickedMesh.parent.getWorldMatrix()), this._targetPosition);
                             Vector3.TransformCoordinatesToRef(this._targetPosition, Matrix.Invert(pickedMesh.parent.getWorldMatrix()), this._targetPosition);
                         }
                         }
+
+                        if(!this._moving){
+                            this._startingOrientation.copyFrom(this._virtualDragMesh.rotationQuaternion!)
+                        }
+                        this._moving = true;
                     }
                     }
                 }
                 }
             });
             });
 
 
+            var tmpQuaternion = new Quaternion();
             // On every frame move towards target scaling to avoid jitter caused by vr controllers
             // On every frame move towards target scaling to avoid jitter caused by vr controllers
             this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(()=>{
             this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(()=>{
-                if(this.dragging && pickedMesh){
+                if(this.dragging && this._moving && pickedMesh){
                     // Slowly move mesh to avoid jitter
                     // Slowly move mesh to avoid jitter
                     pickedMesh.position.addInPlace(this._targetPosition.subtract(pickedMesh.position).scale(this.dragDeltaRatio));
                     pickedMesh.position.addInPlace(this._targetPosition.subtract(pickedMesh.position).scale(this.dragDeltaRatio));
+                    
+                    // Get change in rotation
+                    tmpQuaternion.copyFrom(this._startingOrientation);
+                    tmpQuaternion.x = -tmpQuaternion.x;
+                    tmpQuaternion.y = -tmpQuaternion.y;
+                    tmpQuaternion.z = -tmpQuaternion.z;
+                    this._virtualDragMesh.rotationQuaternion!.multiplyToRef(tmpQuaternion, tmpQuaternion);
+                    // Convert change in rotation to only y axis rotation
+                    Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y,0,0, tmpQuaternion);
+                    tmpQuaternion.multiplyToRef(this._startingOrientation, tmpQuaternion);
+                    // Slowly move mesh to avoid jitter
+                    var oldParent=pickedMesh.parent;
+                    pickedMesh.setParent(null);
+                    Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
+                    pickedMesh.setParent(oldParent);
                 }
                 }
             });
             });
         }
         }

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

@@ -621,6 +621,7 @@ module BABYLON {
             document.addEventListener("mozfullscreenchange", this._onFullscreenChange, false);
             document.addEventListener("mozfullscreenchange", this._onFullscreenChange, false);
             document.addEventListener("webkitfullscreenchange", this._onFullscreenChange, false);
             document.addEventListener("webkitfullscreenchange", this._onFullscreenChange, false);
             document.addEventListener("msfullscreenchange", this._onFullscreenChange, false);
             document.addEventListener("msfullscreenchange", this._onFullscreenChange, false);
+            (<any>document).onmsfullscreenchange = this._onFullscreenChange;
 
 
             // Display vr button when headset is connected
             // Display vr button when headset is connected
             if (webVROptions.createFallbackVRDeviceOrientationFreeCamera) {
             if (webVROptions.createFallbackVRDeviceOrientationFreeCamera) {
@@ -724,6 +725,8 @@ module BABYLON {
                 this._fullscreenVRpresenting = document.webkitIsFullScreen;
                 this._fullscreenVRpresenting = document.webkitIsFullScreen;
             } else if (document.msIsFullScreen !== undefined) {
             } else if (document.msIsFullScreen !== undefined) {
                 this._fullscreenVRpresenting = document.msIsFullScreen;
                 this._fullscreenVRpresenting = document.msIsFullScreen;
+            } else if ((<any>document).msFullscreenElement !== undefined) {
+                this._fullscreenVRpresenting = (<any>document).msFullscreenElement;
             }
             }
             if (!this._fullscreenVRpresenting && this._canvas) {
             if (!this._fullscreenVRpresenting && this._canvas) {
                 this.exitVR();
                 this.exitVR();
@@ -1796,6 +1799,7 @@ module BABYLON {
             document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("msfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("msfullscreenchange", this._onFullscreenChange);
+            (<any>document).onmsfullscreenchange = null;
 
 
             this._scene.getEngine().onVRDisplayChangedObservable.removeCallback(this._onVRDisplayChanged);
             this._scene.getEngine().onVRDisplayChangedObservable.removeCallback(this._onVRDisplayChanged);
             this._scene.getEngine().onVRRequestPresentStart.removeCallback(this._onVRRequestPresentStart);
             this._scene.getEngine().onVRRequestPresentStart.removeCallback(this._onVRRequestPresentStart);

+ 16 - 2
src/Collisions/babylon.pickingInfo.ts

@@ -92,10 +92,24 @@
             }
             }
 
 
             if (useWorldCoordinates) {
             if (useWorldCoordinates) {
-                result = Vector3.TransformNormal(result, this.pickedMesh.getWorldMatrix());
+                let wm = this.pickedMesh.getWorldMatrix();
+
+                if (this.pickedMesh.nonUniformScaling) {
+                    Tmp.Matrix[0].copyFrom(wm);
+                    wm = Tmp.Matrix[0];
+                    wm.setTranslationFromFloats(0, 0, 0);
+                    wm.invert();
+                    wm.transposeToRef(Tmp.Matrix[1]);
+
+                    wm = Tmp.Matrix[1];
+                }
+
+                result = Vector3.TransformNormal(result, wm);
             }
             }
 
 
-            return Vector3.Normalize(result);
+            result.normalize();
+
+            return result;
         }
         }
 
 
         /**
         /**

+ 61 - 23
src/Engine/babylon.engine.ts

@@ -3433,7 +3433,8 @@
         }
         }
 
 
         /**
         /**
-         * Create an effect to use with particle systems
+         * Create an effect to use with particle systems.
+         * Please note that some parameters like animation sheets or not being billboard are not supported in this configuration
          * @param fragmentName defines the base name of the effect (The name of file without .fragment.fx)
          * @param fragmentName defines the base name of the effect (The name of file without .fragment.fx)
          * @param uniformsNames defines a list of attribute names 
          * @param uniformsNames defines a list of attribute names 
          * @param samplers defines an array of string used to represent textures
          * @param samplers defines an array of string used to represent textures
@@ -3446,14 +3447,25 @@
         public createEffectForParticles(fragmentName: string, uniformsNames: string[] = [], samplers: string[] = [], defines = "", fallbacks?: EffectFallbacks,
         public createEffectForParticles(fragmentName: string, uniformsNames: string[] = [], samplers: string[] = [], defines = "", fallbacks?: EffectFallbacks,
             onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void): Effect {
             onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void): Effect {
 
 
+            var attributesNamesOrOptions = ParticleSystem._GetAttributeNamesOrOptions();
+            var effectCreationOption = ParticleSystem._GetEffectCreationOptions();
+
+            if (defines.indexOf(" BILLBOARD") === -1) {
+                defines += "\n#define BILLBOARD\n";
+            }
+
+            if (samplers.indexOf("diffuseSampler") === -1) {
+                samplers.push("diffuseSampler");
+            }
+
             return this.createEffect(
             return this.createEffect(
                 {
                 {
                     vertex: "particles",
                     vertex: "particles",
                     fragmentElement: fragmentName
                     fragmentElement: fragmentName
                 },
                 },
-                ["position", "color", "options"],
-                ["view", "projection"].concat(uniformsNames),
-                ["diffuseSampler"].concat(samplers), defines, fallbacks, onCompiled, onError);
+                attributesNamesOrOptions,
+                effectCreationOption.concat(uniformsNames),
+                samplers, defines, fallbacks, onCompiled, onError);
         }
         }
 
 
         /**
         /**
@@ -4310,22 +4322,42 @@
                             return false;
                             return false;
                         }
                         }
 
 
-                        // Using shaders to rescale because canvas.drawImage is lossy
-                        let source = new InternalTexture(this, InternalTexture.DATASOURCE_TEMP);
-                        this._bindTextureDirectly(gl.TEXTURE_2D, source, true);
-                        gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img);
+                        let maxTextureSize = this._caps.maxTextureSize;
 
 
-                        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-                        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
-                        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-                        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+                        if (img.width > maxTextureSize || img.height > maxTextureSize) {
+                            this._prepareWorkingCanvas();
+                            if (!this._workingCanvas || !this._workingContext) {
+                                return false;
+                            }
 
 
-                        this._rescaleTexture(source, texture, scene, internalFormat, () => {
-                            this._releaseTexture(source);
-                            this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
+                            this._workingCanvas.width = potWidth;
+                            this._workingCanvas.height = potHeight;
+        
+                            this._workingContext.drawImage(img, 0, 0, img.width, img.height, 0, 0, potWidth, potHeight);
+                            gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, this._workingCanvas);
+    
+                            texture.width = potWidth;
+                            texture.height = potHeight;
 
 
-                            continuationCallback();
-                        });
+                            return false;
+                        } else {
+                            // Using shaders when possible to rescale because canvas.drawImage is lossy
+                            let source = new InternalTexture(this, InternalTexture.DATASOURCE_TEMP);
+                            this._bindTextureDirectly(gl.TEXTURE_2D, source, true);
+                            gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img);
+
+                            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+                            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+                            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+                            this._rescaleTexture(source, texture, scene, internalFormat, () => {
+                                this._releaseTexture(source);
+                                this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
+
+                                continuationCallback();
+                            });
+                        }
 
 
                         return true;
                         return true;
                     }, samplingMode);
                     }, samplingMode);
@@ -5545,12 +5577,13 @@
          * @param createPolynomials if a polynomial sphere should be created for the cube texture
          * @param createPolynomials if a polynomial sphere should be created for the cube texture
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
+         * @param fallback defines texture to use while falling back when (compressed) texture file not found.
          * @returns the cube texture as an InternalTexture
          * @returns the cube texture as an InternalTexture
          */
          */
-        public createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format?: number, forcedExtension: any = null, createPolynomials = false, lodScale: number = 0, lodOffset: number = 0): InternalTexture {
+        public createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format?: number, forcedExtension: any = null, createPolynomials = false, lodScale: number = 0, lodOffset: number = 0, fallback: Nullable<InternalTexture> = null): InternalTexture {
             var gl = this._gl;
             var gl = this._gl;
 
 
-            var texture = new InternalTexture(this, InternalTexture.DATASOURCE_CUBE);
+            var texture = fallback ? fallback : new InternalTexture(this, InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
             texture.isCube = true;
             texture.url = rootUrl;
             texture.url = rootUrl;
             texture.generateMipMaps = !noMipmap;
             texture.generateMipMaps = !noMipmap;
@@ -5567,8 +5600,7 @@
             var isEnv = false;
             var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
-            if (this._textureFormatInUse) {
-                extension = this._textureFormatInUse;
+            if (this._textureFormatInUse && !fallback) {
                 rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
                 rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
                 isKTX = true;
                 isKTX = true;
             } else {
             } else {
@@ -5577,6 +5609,11 @@
             }
             }
 
 
             let onerror = (request?: XMLHttpRequest, exception?: any) => {
             let onerror = (request?: XMLHttpRequest, exception?: any) => {
+                if(isKTX){
+                    //remove the format appended to the rootUrl in the original createCubeTexture call.
+                    var exp = new RegExp("" + this._textureFormatInUse + "$");
+                    this.createCubeTexture(rootUrl.replace(exp, ""), scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                }
                 if (onError && request) {
                 if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                     onError(request.status + " " + request.statusText, exception);
                 }
                 }
@@ -6117,8 +6154,9 @@
 
 
         private _prepareWebGLTexture(texture: InternalTexture, scene: Nullable<Scene>, width: number, height: number, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
         private _prepareWebGLTexture(texture: InternalTexture, scene: Nullable<Scene>, width: number, height: number, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
             processFunction: (width: number, height: number, continuationCallback: () => void) => boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE): void {
             processFunction: (width: number, height: number, continuationCallback: () => void) => boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE): void {
-            var potWidth = this.needPOTTextures ? Tools.GetExponentOfTwo(width, this.getCaps().maxTextureSize) : width;
-            var potHeight = this.needPOTTextures ? Tools.GetExponentOfTwo(height, this.getCaps().maxTextureSize) : height;
+            var maxTextureSize = this.getCaps().maxTextureSize;    
+            var potWidth = Math.min(maxTextureSize, this.needPOTTextures ? Tools.GetExponentOfTwo(width, maxTextureSize) : width);
+            var potHeight = Math.min(maxTextureSize, this.needPOTTextures ? Tools.GetExponentOfTwo(height, maxTextureSize) : height);
 
 
             var gl = this._gl;
             var gl = this._gl;
             if (!gl) {
             if (!gl) {

+ 60 - 14
src/Gizmos/babylon.axisDragGizmo.ts

@@ -4,6 +4,16 @@ module BABYLON {
      */
      */
     export class AxisDragGizmo extends Gizmo {
     export class AxisDragGizmo extends Gizmo {
         private _dragBehavior:PointerDragBehavior;
         private _dragBehavior:PointerDragBehavior;
+        private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
+        /**
+         * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+         */
+        public snapDistance = 0;
+        /**
+         * Event that fires each time the gizmo snaps to a new location.
+         * * snapDistance is the the change in distance
+         */
+        public onSnapObservable = new Observable<{snapDistance:number}>();
         /**
         /**
          * Creates an AxisDragGizmo
          * Creates an AxisDragGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -12,49 +22,85 @@ module BABYLON {
          */
          */
         constructor(gizmoLayer:UtilityLayerRenderer, dragAxis:Vector3, color:Color3){
         constructor(gizmoLayer:UtilityLayerRenderer, dragAxis:Vector3, color:Color3){
             super(gizmoLayer);
             super(gizmoLayer);
-            this.updateGizmoRotationToMatchAttachedMesh = false;
             
             
             // Create Material
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
             coloredMaterial.disableLighting = true;
             coloredMaterial.emissiveColor = color;
             coloredMaterial.emissiveColor = color;
 
 
+            var hoverMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
+            hoverMaterial.disableLighting = true;
+            hoverMaterial.emissiveColor = color.add(new Color3(0.2,0.2,0.2));
+
             // Build mesh on root node
             // Build mesh on root node
+            var arrow = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
             var arrowMesh = BABYLON.MeshBuilder.CreateCylinder("yPosMesh", {diameterTop:0, height: 2, tessellation: 96}, gizmoLayer.utilityLayerScene);
             var arrowMesh = BABYLON.MeshBuilder.CreateCylinder("yPosMesh", {diameterTop:0, height: 2, tessellation: 96}, gizmoLayer.utilityLayerScene);
-            var arrowTail = BABYLON.MeshBuilder.CreateCylinder("yPosMesh", {diameter:0.03, height: 0.2, tessellation: 96}, gizmoLayer.utilityLayerScene);
-            this._rootMesh.addChild(arrowMesh);
-            this._rootMesh.addChild(arrowTail);
+            var arrowTail = BABYLON.MeshBuilder.CreateCylinder("yPosMesh", {diameter:0.015, height: 0.3, tessellation: 96}, gizmoLayer.utilityLayerScene);
+            arrow.addChild(arrowMesh);
+            arrow.addChild(arrowTail);
 
 
             // Position arrow pointing in its drag axis
             // Position arrow pointing in its drag axis
-            arrowMesh.scaling.scaleInPlace(0.1);
+            arrowMesh.scaling.scaleInPlace(0.05);
             arrowMesh.material = coloredMaterial;
             arrowMesh.material = coloredMaterial;
             arrowMesh.rotation.x = Math.PI/2;
             arrowMesh.rotation.x = Math.PI/2;
             arrowMesh.position.z+=0.3;
             arrowMesh.position.z+=0.3;
             arrowTail.rotation.x = Math.PI/2;
             arrowTail.rotation.x = Math.PI/2;
             arrowTail.material = coloredMaterial;
             arrowTail.material = coloredMaterial;
-            arrowTail.position.z+=0.2;
-            this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
+            arrowTail.position.z+=0.15;
+            arrow.lookAt(this._rootMesh.position.subtract(dragAxis));
 
 
+            this._rootMesh.addChild(arrow)
+
+            var currentSnapDragDistance = 0;
+            var tmpVector = new Vector3();
+            var tmpSnapEvent = {snapDistance: 0};
             // Add drag behavior to handle events when the gizmo is dragged
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: new BABYLON.Vector3(0,0,1)});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
             this._dragBehavior.moveAttached = false;
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._rootMesh.addBehavior(this._dragBehavior);
             this._dragBehavior.onDragObservable.add((event)=>{
             this._dragBehavior.onDragObservable.add((event)=>{
-                if(!this.interactionsEnabled){
-                    return;
-                }
                 if(this.attachedMesh){
                 if(this.attachedMesh){
-                    this.attachedMesh.position.addInPlace(event.delta);
+                    // Snapping logic
+                    if(this.snapDistance == 0){
+                        this.attachedMesh.position.addInPlace(event.delta);
+                    }else{
+                        currentSnapDragDistance+=event.dragDistance
+                        if(Math.abs(currentSnapDragDistance)>this.snapDistance){
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance)/this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
+                            event.delta.normalizeToRef(tmpVector);
+                            tmpVector.scaleInPlace(this.snapDistance*dragSteps);
+                            this.attachedMesh.position.addInPlace(tmpVector);
+                            tmpSnapEvent.snapDistance = this.snapDistance*dragSteps;
+                            this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                        }
+                    }
                 }
                 }
             })
             })
+
+            this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo, eventState)=>{
+                if(pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1)){
+                    this._rootMesh.getChildMeshes().forEach((m)=>{
+                        m.material = hoverMaterial;
+                    });
+                }else{
+                    this._rootMesh.getChildMeshes().forEach((m)=>{
+                        m.material = coloredMaterial;
+                    });
+                }
+            });
         }
         }
-        protected _onInteractionsEnabledChanged(value:boolean){
-            this._dragBehavior.enabled = value;
+        protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
+            if(this._dragBehavior){
+                this._dragBehavior.enabled = value?true:false;
+            }
         }
         }
         /**
         /**
          * Disposes of the gizmo
          * Disposes of the gizmo
          */
          */
         public dispose(){
         public dispose(){
+            this.onSnapObservable.clear();
+            this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             this._dragBehavior.detach();
             super.dispose();
             super.dispose();
         } 
         } 

+ 82 - 15
src/Gizmos/babylon.axisScaleGizmo.ts

@@ -4,6 +4,16 @@ module BABYLON {
      */
      */
     export class AxisScaleGizmo extends Gizmo {
     export class AxisScaleGizmo extends Gizmo {
         private _dragBehavior:PointerDragBehavior;
         private _dragBehavior:PointerDragBehavior;
+        private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
+        /**
+         * Scale distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+         */
+        public snapDistance = 0;
+        /**
+         * Event that fires each time the gizmo snaps to a new location.
+         * * snapDistance is the the change in distance
+         */
+        public onSnapObservable = new Observable<{snapDistance:number}>();
         /**
         /**
          * Creates an AxisScaleGizmo
          * Creates an AxisScaleGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -12,18 +22,22 @@ module BABYLON {
          */
          */
         constructor(gizmoLayer:UtilityLayerRenderer, dragAxis:Vector3, color:Color3){
         constructor(gizmoLayer:UtilityLayerRenderer, dragAxis:Vector3, color:Color3){
             super(gizmoLayer);
             super(gizmoLayer);
-            this.updateGizmoRotationToMatchAttachedMesh=false;
             
             
             // Create Material
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
             coloredMaterial.disableLighting = true;
             coloredMaterial.emissiveColor = color;
             coloredMaterial.emissiveColor = color;
 
 
+            var hoverMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
+            hoverMaterial.disableLighting = true;
+            hoverMaterial.emissiveColor = color.add(new Color3(0.2,0.2,0.2));
+
             // Build mesh on root node
             // Build mesh on root node
-            var arrowMesh = BABYLON.MeshBuilder.CreateBox("yPosMesh", {size: 1}, gizmoLayer.utilityLayerScene);
-            var arrowTail = BABYLON.MeshBuilder.CreateCylinder("yPosMesh", {diameter:0.03, height: 0.2, tessellation: 96}, gizmoLayer.utilityLayerScene);
-            this._rootMesh.addChild(arrowMesh);
-            this._rootMesh.addChild(arrowTail);
+            var arrow = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene)
+            var arrowMesh = BABYLON.MeshBuilder.CreateBox("yPosMesh", {size: 0.5}, gizmoLayer.utilityLayerScene);
+            var arrowTail = BABYLON.MeshBuilder.CreateCylinder("yPosMesh", {diameter:0.015, height: 0.3, tessellation: 96}, gizmoLayer.utilityLayerScene);
+            arrow.addChild(arrowMesh);
+            arrow.addChild(arrowTail);
 
 
             // Position arrow pointing in its drag axis
             // Position arrow pointing in its drag axis
             arrowMesh.scaling.scaleInPlace(0.1);
             arrowMesh.scaling.scaleInPlace(0.1);
@@ -32,32 +46,85 @@ module BABYLON {
             arrowMesh.position.z+=0.3;
             arrowMesh.position.z+=0.3;
             arrowTail.rotation.x = Math.PI/2;
             arrowTail.rotation.x = Math.PI/2;
             arrowTail.material = coloredMaterial;
             arrowTail.material = coloredMaterial;
-            arrowTail.position.z+=0.2;
-            this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
+            arrowTail.position.z+=0.15;
+            arrow.lookAt(this._rootMesh.position.subtract(dragAxis));
+            this._rootMesh.addChild(arrow);
 
 
             // Add drag behavior to handle events when the gizmo is dragged
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: new BABYLON.Vector3(0,0,1)});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
             this._dragBehavior.moveAttached = false;
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._rootMesh.addBehavior(this._dragBehavior);
 
 
-            this._dragBehavior.onDragObservable.add((event)=>{
-                if(!this.interactionsEnabled){
-                    return;
-                }
+            var currentSnapDragDistance = 0;
+            var tmpVector = new Vector3();
+            var tmpSnapEvent = {snapDistance: 0};
+            this._dragBehavior.onDragObservable.add((event)=>{                
                 if(this.attachedMesh){
                 if(this.attachedMesh){
-                    this.attachedMesh.scaling.addInPlace(event.delta);
+                    // Snapping logic
+                    var snapped = false;
+                    var dragSteps = 0;
+                    if(this.snapDistance == 0){
+                        dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    }else{
+                        currentSnapDragDistance+=event.dragDistance;
+                        if(Math.abs(currentSnapDragDistance)>this.snapDistance){
+                            dragSteps = Math.floor(currentSnapDragDistance/this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
+                            dragAxis.scaleToRef(this.snapDistance*dragSteps, tmpVector);
+                            snapped = true;
+                        }else{
+                            tmpVector.scaleInPlace(0);
+                        }
+                    }
+                    
+                    var invertCount = 0;
+                    if(this.attachedMesh.scaling["x"] < 0){
+                        invertCount++;
+                    }
+                    if(this.attachedMesh.scaling["y"] < 0){
+                        invertCount++;
+                    }
+                    if(this.attachedMesh.scaling["z"] < 0){
+                        invertCount++;
+                    }
+                    if(invertCount % 2 == 0){
+                        this.attachedMesh.scaling.addInPlace(tmpVector);
+                    }else{
+                        this.attachedMesh.scaling.subtractInPlace(tmpVector);
+                    }
+
+                    if(snapped){
+                        tmpSnapEvent.snapDistance = this.snapDistance*dragSteps;
+                        this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
                 }
             })
             })
+
+            this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo, eventState)=>{
+                if(pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1)){
+                    this._rootMesh.getChildMeshes().forEach((m)=>{
+                        m.material = hoverMaterial;
+                    });
+                }else{
+                    this._rootMesh.getChildMeshes().forEach((m)=>{
+                        m.material = coloredMaterial;
+                    });
+                }
+            });
         }
         }
         
         
-        protected _onInteractionsEnabledChanged(value:boolean){
-            this._dragBehavior.enabled = value;
+        protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
+            if(this._dragBehavior){
+                this._dragBehavior.enabled = value ? true : false;
+            }
         }
         }
         
         
         /**
         /**
          * Disposes of the gizmo
          * Disposes of the gizmo
          */
          */
         public dispose(){
         public dispose(){
+            this.onSnapObservable.clear();
+            this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             this._dragBehavior.detach();
             super.dispose();
             super.dispose();
         } 
         } 

+ 44 - 9
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -11,6 +11,8 @@ module BABYLON {
         private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
         private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
         private _scaleDragSpeed = 0.2;
         private _scaleDragSpeed = 0.2;
 
 
+        private _tmpQuaternion = new Quaternion();
+        private _tmpVector = new Vector3(0,0,0);
         /**
         /**
          * Creates an BoundingBoxGizmo
          * Creates an BoundingBoxGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -65,25 +67,41 @@ module BABYLON {
                 // Drag behavior
                 // Drag behavior
                 var _dragBehavior = new PointerDragBehavior({});
                 var _dragBehavior = new PointerDragBehavior({});
                 _dragBehavior.moveAttached = false;
                 _dragBehavior.moveAttached = false;
+                _dragBehavior.updateDragPlane = false;
                 sphere.addBehavior(_dragBehavior);
                 sphere.addBehavior(_dragBehavior);
+                let startingTurnDirection = new Vector3(1,0,0);
+                let totalTurnAmountOfDrag = 0;
+                _dragBehavior.onDragStartObservable.add((event)=>{
+                    startingTurnDirection.copyFrom(sphere.forward);
+                    totalTurnAmountOfDrag = 0;
+                })
                 _dragBehavior.onDragObservable.add((event)=>{
                 _dragBehavior.onDragObservable.add((event)=>{
                     if(this.attachedMesh){
                     if(this.attachedMesh){
-                        var worldDragDirection = sphere.forward;
+                        var worldDragDirection = startingTurnDirection;
 
 
                         // Project the world right on to the drag plane
                         // Project the world right on to the drag plane
                         var toSub = event.dragPlaneNormal.scale(Vector3.Dot(event.dragPlaneNormal, worldDragDirection));
                         var toSub = event.dragPlaneNormal.scale(Vector3.Dot(event.dragPlaneNormal, worldDragDirection));
                         var dragAxis = worldDragDirection.subtract(toSub).normalizeToNew();
                         var dragAxis = worldDragDirection.subtract(toSub).normalizeToNew();
 
 
                         // project drag delta on to the resulting drag axis and rotate based on that
                         // project drag delta on to the resulting drag axis and rotate based on that
-                        var projectDist = Vector3.Dot(dragAxis, event.delta);
+                        var projectDist = -Vector3.Dot(dragAxis, event.delta);
 
 
                         // Rotate based on axis
                         // Rotate based on axis
-                        if(i>=8){
-                            this.attachedMesh.rotation.z -= projectDist;
-                        }else if(i>=4){
-                            this.attachedMesh.rotation.y -= projectDist;
-                        }else{
-                            this.attachedMesh.rotation.x -= projectDist;
+                        if(!this.attachedMesh.rotationQuaternion){
+                            this.attachedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.attachedMesh.rotation.y,this.attachedMesh.rotation.x,this.attachedMesh.rotation.z);
+                        }
+                       
+                        // Do not allow the object to turn more than a full circle
+                        totalTurnAmountOfDrag+=projectDist;
+                        if(Math.abs(totalTurnAmountOfDrag)<=2*Math.PI){
+                            if(i>=8){
+                                Quaternion.RotationYawPitchRollToRef(0,0,projectDist, this._tmpQuaternion);
+                            }else if(i>=4){
+                                Quaternion.RotationYawPitchRollToRef(projectDist,0,0, this._tmpQuaternion);
+                            }else{
+                                Quaternion.RotationYawPitchRollToRef(0,projectDist,0, this._tmpQuaternion);
+                            }
+                            this.attachedMesh.rotationQuaternion!.multiplyInPlace(this._tmpQuaternion);
                         }
                         }
                     }
                     }
                 });
                 });
@@ -129,7 +147,8 @@ module BABYLON {
                                 
                                 
                                 // Update scale and position
                                 // Update scale and position
                                 this.attachedMesh.scaling.addInPlace(deltaScale);
                                 this.attachedMesh.scaling.addInPlace(deltaScale);
-                                this.attachedMesh.position.addInPlace(worldMoveDirection);
+                                this.attachedMesh.getAbsolutePosition().addToRef(worldMoveDirection, this._tmpVector)
+                                this.attachedMesh.setAbsolutePosition(this._tmpVector);
                             }
                             }
                         })
                         })
 
 
@@ -229,6 +248,22 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
+         * Enables rotation on the specified axis and disables rotation on the others
+         * @param axis The list of axis that should be enabled (eg. "xy" or "xyz")
+         */
+        public setEnabledRotationAxis(axis:string){
+            this._rotateSpheresParent.getChildMeshes().forEach((m,i)=>{
+                if(i<4){
+                    m.setEnabled(axis.indexOf("x")!=-1);
+                }else if(i<8){
+                    m.setEnabled(axis.indexOf("y")!=-1);
+                }else{
+                    m.setEnabled(axis.indexOf("z")!=-1);
+                }
+            })
+        }
+
+        /**
          * Disposes of the gizmo
          * Disposes of the gizmo
          */
          */
         public dispose(){
         public dispose(){

+ 12 - 13
src/Gizmos/babylon.gizmo.ts

@@ -7,10 +7,19 @@ module BABYLON {
          * The root mesh of the gizmo
          * The root mesh of the gizmo
          */
          */
         protected _rootMesh:Mesh;
         protected _rootMesh:Mesh;
+        private _attachedMesh:Nullable<AbstractMesh>;
         /**
         /**
          * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
          * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
+         * * When set, interactions will be enabled
          */
          */
-        public attachedMesh:Nullable<AbstractMesh>;
+        public get attachedMesh(){
+            return this._attachedMesh;
+        }
+        public set attachedMesh(value){
+            this._attachedMesh = value;
+            this._rootMesh.setEnabled(value?true:false);
+            this._attachedMeshChanged(value);
+        }
         /**
         /**
          * If set the gizmo's rotation will be updated to match the attached mesh each frame (Default: true)
          * If set the gizmo's rotation will be updated to match the attached mesh each frame (Default: true)
          */
          */
@@ -24,18 +33,7 @@ module BABYLON {
          */
          */
         protected _updateScale = true;
         protected _updateScale = true;
         protected _interactionsEnabled = true;
         protected _interactionsEnabled = true;
-        protected _onInteractionsEnabledChanged(value:boolean){
-        }
-
-        /**
-         * If interactions are enabled with this gizmo. (eg. dragging/rotation)
-         */
-        public set interactionsEnabled(value:boolean){
-            this._interactionsEnabled = value;
-            this._onInteractionsEnabledChanged(value);
-        }
-        public get interactionsEnabled(){
-            return this._interactionsEnabled;
+        protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
         }
         }
 
 
         private _beforeRenderObserver:Nullable<Observer<Scene>>;
         private _beforeRenderObserver:Nullable<Observer<Scene>>;
@@ -63,6 +61,7 @@ module BABYLON {
                     }
                     }
                 }
                 }
             })
             })
+            this.attachedMesh = null;
         }
         }
         /**
         /**
          * Disposes of the gizmo
          * Disposes of the gizmo

+ 82 - 25
src/Gizmos/babylon.planeRotationGizmo.ts

@@ -4,6 +4,18 @@ module BABYLON {
      */
      */
     export class PlaneRotationGizmo extends Gizmo {
     export class PlaneRotationGizmo extends Gizmo {
         private _dragBehavior:PointerDragBehavior;
         private _dragBehavior:PointerDragBehavior;
+        private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
+        
+        /**
+         * Rotation distance in radians that the gizmo will snap to (Default: 0)
+         */
+        public snapDistance = 0;
+        /**
+         * Event that fires each time the gizmo snaps to a new location.
+         * * snapDistance is the the change in distance
+         */
+        public onSnapObservable = new Observable<{snapDistance:number}>();
+
         /**
         /**
          * Creates a PlaneRotationGizmo
          * Creates a PlaneRotationGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -12,41 +24,48 @@ module BABYLON {
          */
          */
         constructor(gizmoLayer:UtilityLayerRenderer, planeNormal:Vector3, color:Color3){
         constructor(gizmoLayer:UtilityLayerRenderer, planeNormal:Vector3, color:Color3){
             super(gizmoLayer);
             super(gizmoLayer);
-            this.updateGizmoRotationToMatchAttachedMesh=false;
             
             
             // Create Material
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
             coloredMaterial.disableLighting = true;
             coloredMaterial.emissiveColor = color;
             coloredMaterial.emissiveColor = color;
+            
+            var hoverMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
+            hoverMaterial.disableLighting = true;
+            hoverMaterial.emissiveColor = color.add(new Color3(0.2,0.2,0.2));
 
 
             // Build mesh on root node
             // Build mesh on root node
-            var rotationMesh = BABYLON.Mesh.CreateTorus("torus", 3, 0.3, 20, gizmoLayer.utilityLayerScene, false);
-            this._rootMesh.addChild(rotationMesh);
-
+            var parentMesh = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
+            var rotationMesh = BABYLON.Mesh.CreateTorus("torus", 3, 0.15, 20, gizmoLayer.utilityLayerScene, false);
+            
             // Position arrow pointing in its drag axis
             // Position arrow pointing in its drag axis
             rotationMesh.scaling.scaleInPlace(0.1);
             rotationMesh.scaling.scaleInPlace(0.1);
             rotationMesh.material = coloredMaterial;
             rotationMesh.material = coloredMaterial;
             rotationMesh.rotation.x = Math.PI/2;
             rotationMesh.rotation.x = Math.PI/2;
-            this._rootMesh.lookAt(this._rootMesh.position.subtract(planeNormal));
-
+            parentMesh.addChild(rotationMesh);
+            parentMesh.lookAt(this._rootMesh.position.subtract(planeNormal));
+            
+            this._rootMesh.addChild(parentMesh);
             // Add drag behavior to handle events when the gizmo is dragged
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: new Vector3(0,0,1)});
+            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal});
             this._dragBehavior.moveAttached = false;
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._rootMesh.addBehavior(this._dragBehavior);
 
 
             var lastDragPosition:Nullable<Vector3> = null;
             var lastDragPosition:Nullable<Vector3> = null;
 
 
             this._dragBehavior.onDragStartObservable.add((e)=>{
             this._dragBehavior.onDragStartObservable.add((e)=>{
-                if(!this.interactionsEnabled){
-                    return;
+                if(this.attachedMesh){
+                    lastDragPosition = e.dragPlanePoint;
                 }
                 }
-                lastDragPosition = e.dragPlanePoint;
             })
             })
 
 
+            var rotationMatrix = new Matrix();
+            var planeNormalTowardsCamera = new Vector3();
+            var localPlaneNormalTowardsCamera = new Vector3();
+
+            var tmpSnapEvent = {snapDistance: 0};
+            var currentSnapDragDistance = 0;
             this._dragBehavior.onDragObservable.add((event)=>{
             this._dragBehavior.onDragObservable.add((event)=>{
-                if(!this.interactionsEnabled){
-                    return;
-                }
                 if(this.attachedMesh && lastDragPosition){
                 if(this.attachedMesh && lastDragPosition){
                     if(!this.attachedMesh.rotationQuaternion){
                     if(!this.attachedMesh.rotationQuaternion){
                         this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
                         this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
@@ -57,38 +76,76 @@ module BABYLON {
                     var cross = Vector3.Cross(newVector,originalVector);
                     var cross = Vector3.Cross(newVector,originalVector);
                     var dot = Vector3.Dot(newVector,originalVector);
                     var dot = Vector3.Dot(newVector,originalVector);
                     var angle = Math.atan2(cross.length(), dot);
                     var angle = Math.atan2(cross.length(), dot);
-                    var up = planeNormal.clone();
+                    planeNormalTowardsCamera.copyFrom(planeNormal);
+                    localPlaneNormalTowardsCamera.copyFrom(planeNormal);
+                    if(this.updateGizmoRotationToMatchAttachedMesh){
+                        this.attachedMesh.rotationQuaternion.toRotationMatrix(rotationMatrix);
+                        localPlaneNormalTowardsCamera = Vector3.TransformCoordinates(planeNormalTowardsCamera, rotationMatrix);
+                    }
                     // Flip up vector depending on which side the camera is on
                     // Flip up vector depending on which side the camera is on
                     if(gizmoLayer.utilityLayerScene.activeCamera){
                     if(gizmoLayer.utilityLayerScene.activeCamera){
                         var camVec = gizmoLayer.utilityLayerScene.activeCamera.position.subtract(this.attachedMesh.position);
                         var camVec = gizmoLayer.utilityLayerScene.activeCamera.position.subtract(this.attachedMesh.position);
-                        if(Vector3.Dot(camVec, up) > 0){
-                            up.scaleInPlace(-1);
+                        if(Vector3.Dot(camVec, localPlaneNormalTowardsCamera) > 0){
+                            planeNormalTowardsCamera.scaleInPlace(-1);
+                            localPlaneNormalTowardsCamera.scaleInPlace(-1);
                         }
                         }
                     }
                     }
-                    var halfCircleSide = Vector3.Dot(up, cross) > 0.0;
+                    var halfCircleSide = Vector3.Dot(localPlaneNormalTowardsCamera, cross) > 0.0;
                     if (halfCircleSide) angle = -angle;
                     if (halfCircleSide) angle = -angle;
                     
                     
+                    // Snapping logic
+                    var snapped = false;
+                    if(this.snapDistance != 0){
+                        currentSnapDragDistance+=angle
+                        if(Math.abs(currentSnapDragDistance)>this.snapDistance){
+                            var dragSteps = Math.floor(currentSnapDragDistance/this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
+                            angle = this.snapDistance*dragSteps;
+                            snapped = true;
+                        }else{
+                            angle = 0;
+                        }
+                    }
+                     // Convert angle and axis to quaternion (http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm)
+                     var quaternionCoefficient = Math.sin(angle/2)
+                     var amountToRotate = new BABYLON.Quaternion(planeNormalTowardsCamera.x*quaternionCoefficient,planeNormalTowardsCamera.y*quaternionCoefficient,planeNormalTowardsCamera.z*quaternionCoefficient,Math.cos(angle/2));
 
 
-                    // Convert angle and axis to quaternion (http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm)
-                    var quaternionCoefficient = Math.sin(angle/2)
-                    var amountToRotate = new BABYLON.Quaternion(up.x*quaternionCoefficient,up.y*quaternionCoefficient,up.z*quaternionCoefficient,Math.cos(angle/2));
-
-                    // Rotate selected mesh quaternion over fixed axis
-                    amountToRotate.multiplyToRef(this.attachedMesh.rotationQuaternion,this.attachedMesh.rotationQuaternion);
+                     // Rotate selected mesh quaternion over fixed axis
+                     this.attachedMesh.rotationQuaternion.multiplyToRef(amountToRotate,this.attachedMesh.rotationQuaternion);
 
 
                     lastDragPosition = event.dragPlanePoint;
                     lastDragPosition = event.dragPlanePoint;
+                    if(snapped){
+                        tmpSnapEvent.snapDistance = angle;
+                        this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
                 }
             })
             })
+
+            this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo, eventState)=>{
+                if(pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1)){
+                    this._rootMesh.getChildMeshes().forEach((m)=>{
+                        m.material = hoverMaterial;
+                    });
+                }else{
+                    this._rootMesh.getChildMeshes().forEach((m)=>{
+                        m.material = coloredMaterial;
+                    });
+                }
+            });
         }
         }
 
 
-        protected _onInteractionsEnabledChanged(value:boolean){
-            this._dragBehavior.enabled = value;
+        protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
+            if(this._dragBehavior){
+                this._dragBehavior.enabled = value ? true : false;
+            }
         }
         }
 
 
         /**
         /**
          * Disposes of the gizmo
          * Disposes of the gizmo
          */
          */
         public dispose(){
         public dispose(){
+            this.onSnapObservable.clear();
+            this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             this._dragBehavior.detach();
             super.dispose();
             super.dispose();
         } 
         } 

+ 19 - 11
src/Gizmos/babylon.positionGizmo.ts

@@ -8,25 +8,33 @@ module BABYLON {
         private _zDrag:AxisDragGizmo;
         private _zDrag:AxisDragGizmo;
 
 
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
-            this._xDrag.attachedMesh = mesh;
-            this._yDrag.attachedMesh = mesh;
-            this._zDrag.attachedMesh = mesh;
+            if(this._xDrag){
+                this._xDrag.attachedMesh = mesh;
+                this._yDrag.attachedMesh = mesh;
+                this._zDrag.attachedMesh = mesh;
+            }
         }
         }
-        /**
+    /**
          * Creates a PositionGizmo
          * Creates a PositionGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
          * @param gizmoLayer The utility layer the gizmo will be added to
          */
          */
         constructor(gizmoLayer:UtilityLayerRenderer){
         constructor(gizmoLayer:UtilityLayerRenderer){
             super(gizmoLayer);
             super(gizmoLayer);
-            this._xDrag = new AxisDragGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.FromHexString("#00b894"));
-            this._yDrag = new AxisDragGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.FromHexString("#d63031"));
-            this._zDrag = new AxisDragGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.FromHexString("#0984e3"));
+            this._xDrag = new AxisDragGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5));
+            this._yDrag = new AxisDragGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5));
+            this._zDrag = new AxisDragGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5));
+            this.attachedMesh = null;
         }
         }
 
 
-        protected _onInteractionsEnabledChanged(value:boolean){
-            this._xDrag.interactionsEnabled = value
-            this._yDrag.interactionsEnabled = value
-            this._zDrag.interactionsEnabled = value
+        public set updateGizmoRotationToMatchAttachedMesh(value:boolean){
+            if(this._xDrag){
+                this._xDrag.updateGizmoRotationToMatchAttachedMesh = value;
+                this._yDrag.updateGizmoRotationToMatchAttachedMesh = value;
+                this._zDrag.updateGizmoRotationToMatchAttachedMesh = value;
+            }
+        }
+        public get updateGizmoRotationToMatchAttachedMesh(){
+            return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
         }
         }
         
         
         /**
         /**

+ 18 - 10
src/Gizmos/babylon.rotationGizmo.ts

@@ -8,9 +8,11 @@ module BABYLON {
         private _zDrag:PlaneRotationGizmo;
         private _zDrag:PlaneRotationGizmo;
 
 
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
-            this._xDrag.attachedMesh = mesh;
-            this._yDrag.attachedMesh = mesh;
-            this._zDrag.attachedMesh = mesh;
+            if(this._xDrag){
+                this._xDrag.attachedMesh = mesh;
+                this._yDrag.attachedMesh = mesh;
+                this._zDrag.attachedMesh = mesh;
+            }
         }
         }
         /**
         /**
          * Creates a RotationGizmo
          * Creates a RotationGizmo
@@ -18,15 +20,21 @@ module BABYLON {
          */
          */
         constructor(gizmoLayer:UtilityLayerRenderer){
         constructor(gizmoLayer:UtilityLayerRenderer){
             super(gizmoLayer);
             super(gizmoLayer);
-            this._xDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.FromHexString("#00b894"));
-            this._yDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.FromHexString("#d63031"));
-            this._zDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.FromHexString("#0984e3"));
+            this._xDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5));
+            this._yDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5));
+            this._zDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5));
+            this.attachedMesh = null;
         }
         }
 
 
-        protected _onInteractionsEnabledChanged(value:boolean){
-            this._xDrag.interactionsEnabled = value
-            this._yDrag.interactionsEnabled = value
-            this._zDrag.interactionsEnabled = value
+        public set updateGizmoRotationToMatchAttachedMesh(value:boolean){
+            if(this._xDrag){
+                this._xDrag.updateGizmoRotationToMatchAttachedMesh = value;
+                this._yDrag.updateGizmoRotationToMatchAttachedMesh = value;
+                this._zDrag.updateGizmoRotationToMatchAttachedMesh = value;
+            }
+        }
+        public get updateGizmoRotationToMatchAttachedMesh(){
+            return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
         }
         }
 
 
         /**
         /**

+ 18 - 10
src/Gizmos/babylon.scaleGizmo.ts

@@ -8,9 +8,11 @@ module BABYLON {
         private _zDrag:AxisScaleGizmo;
         private _zDrag:AxisScaleGizmo;
 
 
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
-            this._xDrag.attachedMesh = mesh;
-            this._yDrag.attachedMesh = mesh;
-            this._zDrag.attachedMesh = mesh;
+            if(this._xDrag){
+                this._xDrag.attachedMesh = mesh;
+                this._yDrag.attachedMesh = mesh;
+                this._zDrag.attachedMesh = mesh;
+            }
         }
         }
         /**
         /**
          * Creates a ScaleGizmo
          * Creates a ScaleGizmo
@@ -18,15 +20,21 @@ module BABYLON {
          */
          */
         constructor(gizmoLayer:UtilityLayerRenderer){
         constructor(gizmoLayer:UtilityLayerRenderer){
             super(gizmoLayer);
             super(gizmoLayer);
-            this._xDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.FromHexString("#00b894"));
-            this._yDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.FromHexString("#d63031"));
-            this._zDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.FromHexString("#0984e3"));
+            this._xDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5));
+            this._yDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5));
+            this._zDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5));
+            this.attachedMesh = null;
         }
         }
 
 
-        protected _onInteractionsEnabledChanged(value:boolean){
-            this._xDrag.interactionsEnabled = value
-            this._yDrag.interactionsEnabled = value
-            this._zDrag.interactionsEnabled = value
+        public set updateGizmoRotationToMatchAttachedMesh(value:boolean){
+            if(this._xDrag){
+                this._xDrag.updateGizmoRotationToMatchAttachedMesh = value;
+                this._yDrag.updateGizmoRotationToMatchAttachedMesh = value;
+                this._zDrag.updateGizmoRotationToMatchAttachedMesh = value;
+            }
+        }
+        public get updateGizmoRotationToMatchAttachedMesh(){
+            return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
         }
         }
 
 
         /**
         /**

+ 1 - 1
src/Helpers/babylon.environmentHelper.ts

@@ -82,7 +82,7 @@ module BABYLON {
          */
          */
         groundMirrorTextureType: number;
         groundMirrorTextureType: number;
         /**
         /**
-         * Specifies a bias applied to the ground vertical position to prevent z-fighyting with
+         * Specifies a bias applied to the ground vertical position to prevent z-fighting with
          * the shown objects.
          * the shown objects.
          */
          */
         groundYBias: number;
         groundYBias: number;

+ 32 - 13
src/Helpers/babylon.photoDome.ts

@@ -6,6 +6,7 @@ module BABYLON {
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
      */
     export class PhotoDome extends Node {
     export class PhotoDome extends Node {
+        private _useDirectMapping = false;
 
 
         /**
         /**
          * The texture being displayed on the sphere
          * The texture being displayed on the sphere
@@ -13,12 +14,29 @@ module BABYLON {
         protected _photoTexture: Texture;
         protected _photoTexture: Texture;
 
 
         /**
         /**
-         * Gets the texture being displayed on the sphere
+         * Gets or sets the texture being displayed on the sphere
          */
          */
         public get photoTexture(): Texture {
         public get photoTexture(): Texture {
             return this._photoTexture;
             return this._photoTexture;
         }        
         }        
 
 
+        public set photoTexture(value: Texture) {
+            if (this._photoTexture === value) {
+                return;
+            }
+            this._photoTexture = value;
+            if (this._useDirectMapping) {
+                this._photoTexture.wrapU = Texture.CLAMP_ADDRESSMODE;     
+                this._photoTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+                this._material.diffuseTexture = this._photoTexture;
+            } else {
+                this._photoTexture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
+                this._photoTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+                this._material.reflectionTexture = this._photoTexture;
+            }
+        }        
+
+
         /**
         /**
          * The skybox material
          * The skybox material
          */
          */
@@ -48,32 +66,33 @@ module BABYLON {
          */
          */
         constructor(name: string, urlOfPhoto: string, options: {
         constructor(name: string, urlOfPhoto: string, options: {
             resolution?: number,
             resolution?: number,
-            size?: number
+            size?: number,
+            useDirectMapping?: boolean
         }, scene: Scene) {
         }, scene: Scene) {
             super(name, scene);
             super(name, scene);
 
 
             // set defaults and manage values
             // set defaults and manage values
             name = name || "photoDome";
             name = name || "photoDome";
-            options.resolution = (Math.abs(options.resolution as any) | 0) || 12;
+            options.resolution = (Math.abs(options.resolution as any) | 0) || 32;
             options.size = Math.abs(options.size as any) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
             options.size = Math.abs(options.size as any) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
 
 
+            if (options.useDirectMapping === undefined) {
+                this._useDirectMapping = true;    
+            } else {
+                this._useDirectMapping = options.useDirectMapping;            
+            }
+
             // create
             // create
             let material = this._material = new BackgroundMaterial(name + "_material", scene);
             let material = this._material = new BackgroundMaterial(name + "_material", scene);
-            let texture = this._photoTexture = new Texture(urlOfPhoto, scene);
-            this._mesh = MeshBuilder.CreateIcoSphere(name + "_mesh", {
-                flat: false, // saves on vertex data
-                radius: options.size,
-                subdivisions: options.resolution,
-                sideOrientation: Mesh.BACKSIDE // needs to be inside out
-            }, scene);
+            this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
 
 
             // configure material
             // configure material
-            texture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
-            texture.wrapV = Texture.CLAMP_ADDRESSMODE; // always clamp the up/down
-            material.reflectionTexture = this._photoTexture;
+            material.opacityFresnel = false;
             material.useEquirectangularFOV = true;
             material.useEquirectangularFOV = true;
             material.fovMultiplier = 1.0;
             material.fovMultiplier = 1.0;
 
 
+            this.photoTexture = new Texture(urlOfPhoto, scene, true, !this._useDirectMapping);
+           
             // configure mesh
             // configure mesh
             this._mesh.material = material;
             this._mesh.material = material;
             this._mesh.parent = this;
             this._mesh.parent = this;

+ 23 - 12
src/Helpers/babylon.videoDome.ts

@@ -6,6 +6,7 @@ module BABYLON {
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
      */
     export class VideoDome extends Node {
     export class VideoDome extends Node {
+        private _useDirectMapping = false;
 
 
         /**
         /**
          * The video texture being displayed on the sphere
          * The video texture being displayed on the sphere
@@ -52,35 +53,45 @@ module BABYLON {
             autoPlay?: boolean,
             autoPlay?: boolean,
             loop?: boolean,
             loop?: boolean,
             size?: number,
             size?: number,
-            poster?: string
+            poster?: string,            
+            useDirectMapping?: boolean
         }, scene: Scene) {
         }, scene: Scene) {
             super(name, scene);
             super(name, scene);
 
 
             // set defaults and manage values
             // set defaults and manage values
             name = name || "videoDome";
             name = name || "videoDome";
-            options.resolution = (Math.abs(options.resolution as any) | 0) || 12;
+            options.resolution = (Math.abs(options.resolution as any) | 0) || 32;
             options.clickToPlay = Boolean(options.clickToPlay);
             options.clickToPlay = Boolean(options.clickToPlay);
             options.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
             options.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
             options.loop = options.loop === undefined ? true : Boolean(options.loop);
             options.loop = options.loop === undefined ? true : Boolean(options.loop);
             options.size = Math.abs(options.size as any) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
             options.size = Math.abs(options.size as any) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
 
 
+            if (options.useDirectMapping === undefined) {
+                this._useDirectMapping = true;    
+            } else {
+                this._useDirectMapping = options.useDirectMapping;            
+            }
+
             // create
             // create
             let tempOptions: VideoTextureSettings = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
             let tempOptions: VideoTextureSettings = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
             let material = this._material = new BackgroundMaterial(name + "_material", scene);
             let material = this._material = new BackgroundMaterial(name + "_material", scene);
-            let texture = this._videoTexture = new VideoTexture(name + "_texture", urlsOrVideo, scene, false, false, Texture.TRILINEAR_SAMPLINGMODE, tempOptions);
-            this._mesh = MeshBuilder.CreateIcoSphere(name + "_mesh", {
-                flat: false, // saves on vertex data
-                radius: options.size,
-                subdivisions: options.resolution,
-                sideOrientation: Mesh.BACKSIDE // needs to be inside out
-            }, scene);
+            let texture = this._videoTexture = new VideoTexture(name + "_texture", urlsOrVideo, scene, false, this._useDirectMapping, Texture.TRILINEAR_SAMPLINGMODE, tempOptions);
+            this._mesh = BABYLON.Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, BABYLON.Mesh.BACKSIDE);
 
 
             // configure material
             // configure material
-            texture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
-            texture.wrapV = Texture.CLAMP_ADDRESSMODE; // always clamp the up/down
-            material.reflectionTexture = this._videoTexture;
             material.useEquirectangularFOV = true;
             material.useEquirectangularFOV = true;
             material.fovMultiplier = 1.0;
             material.fovMultiplier = 1.0;
+            material.opacityFresnel = false;
+
+            if (this._useDirectMapping) {
+                texture.wrapU = Texture.CLAMP_ADDRESSMODE;     
+                texture.wrapV = Texture.CLAMP_ADDRESSMODE;
+                material.diffuseTexture = texture;
+            } else {
+                texture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
+                texture.wrapV = Texture.CLAMP_ADDRESSMODE;
+                material.reflectionTexture =texture;
+            }            
 
 
             // configure mesh
             // configure mesh
             this._mesh.material = material;
             this._mesh.material = material;

+ 4 - 0
src/Materials/Textures/babylon.rawTexture.ts

@@ -34,5 +34,9 @@
         public static CreateRGBATexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, type: number = Engine.TEXTURETYPE_UNSIGNED_INT): RawTexture {
         public static CreateRGBATexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, type: number = Engine.TEXTURETYPE_UNSIGNED_INT): RawTexture {
             return new RawTexture(data, width, height, Engine.TEXTUREFORMAT_RGBA, scene, generateMipMaps, invertY, samplingMode, type);
             return new RawTexture(data, width, height, Engine.TEXTUREFORMAT_RGBA, scene, generateMipMaps, invertY, samplingMode, type);
         }
         }
+
+        public static CreateRTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, type: number = Engine.TEXTURETYPE_FLOAT): RawTexture {
+            return new RawTexture(data, width, height, Engine.TEXTUREFORMAT_R, scene, generateMipMaps, invertY, samplingMode, type);
+        }        
     }
     }
 }
 }

+ 3 - 1
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -172,8 +172,9 @@
          * @param generateDepthBuffer True to generate a depth buffer
          * @param generateDepthBuffer True to generate a depth buffer
          * @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...)
          */
          */
-        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) {
+        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) {
             super(null, scene, !generateMipMaps);
             super(null, scene, !generateMipMaps);
             scene = this.getScene();
             scene = this.getScene();
 
 
@@ -204,6 +205,7 @@
             this._renderTargetOptions = {
             this._renderTargetOptions = {
                 generateMipMaps: generateMipMaps,
                 generateMipMaps: generateMipMaps,
                 type: type,
                 type: type,
+                format: format,
                 samplingMode: samplingMode,
                 samplingMode: samplingMode,
                 generateDepthBuffer: generateDepthBuffer,
                 generateDepthBuffer: generateDepthBuffer,
                 generateStencilBuffer: generateStencilBuffer
                 generateStencilBuffer: generateStencilBuffer

+ 4 - 0
src/Materials/Textures/babylon.videoTexture.ts

@@ -293,6 +293,10 @@
             }
             }
         ): void {
         ): void {
             var video = document.createElement("video");
             var video = document.createElement("video");
+            video.setAttribute('autoplay', '');
+            video.setAttribute('muted', '');
+            video.setAttribute('playsinline', '');
+
             var constraintsDeviceId;
             var constraintsDeviceId;
             if (constraints && constraints.deviceId) {
             if (constraints && constraints.deviceId) {
                 constraintsDeviceId = {
                 constraintsDeviceId = {

+ 6 - 5
src/Math/babylon.math.ts

@@ -869,9 +869,9 @@
          */
          */
         constructor(
         constructor(
             /** defines the first coordinate */
             /** defines the first coordinate */
-            public x: number, 
+            public x: number = 0, 
             /** defines the second coordinate */
             /** defines the second coordinate */
-            public y: number) {
+            public y: number = 0) {
         }
         }
 
 
         /**
         /**
@@ -1496,15 +1496,15 @@
             /**
             /**
              * Defines the first coordinates (on X axis)
              * Defines the first coordinates (on X axis)
              */
              */
-            public x: number,
+            public x: number = 0,
             /**
             /**
              * Defines the second coordinates (on Y axis)
              * Defines the second coordinates (on Y axis)
              */
              */
-            public y: number,
+            public y: number = 0,
             /**
             /**
              * Defines the third coordinates (on Z axis)
              * Defines the third coordinates (on Z axis)
              */
              */
-            public z: number
+            public z: number = 0
         ) {
         ) {
         }
         }
 
 
@@ -6663,6 +6663,7 @@
     // There's a Tmp array per object type : int, float, Vector2, Vector3, Vector4, Quaternion, Matrix
     // There's a Tmp array per object type : int, float, Vector2, Vector3, Vector4, Quaternion, Matrix
     export class Tmp {
     export class Tmp {
         public static Color3: Color3[] = [Color3.Black(), Color3.Black(), Color3.Black()];
         public static Color3: Color3[] = [Color3.Black(), Color3.Black(), Color3.Black()];
+        public static Color4: Color4[] = [new Color4(0, 0, 0, 0), new Color4(0, 0, 0, 0)];
         public static Vector2: Vector2[] = [Vector2.Zero(), Vector2.Zero(), Vector2.Zero()];  // 3 temp Vector2 at once should be enough
         public static Vector2: Vector2[] = [Vector2.Zero(), Vector2.Zero(), Vector2.Zero()];  // 3 temp Vector2 at once should be enough
         public static Vector3: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),
         public static Vector3: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),
         Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];    // 9 temp Vector3 at once should be enough
         Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];    // 9 temp Vector3 at once should be enough

+ 9 - 0
src/Mesh/babylon.mesh.vertexData.ts

@@ -326,6 +326,7 @@
          * @returns the VertexData 
          * @returns the VertexData 
          */
          */
         public transform(matrix: Matrix): VertexData {
         public transform(matrix: Matrix): VertexData {
+            var flip = matrix.m[0] * matrix.m[5] * matrix.m[10] < 0;
             var transformed = Vector3.Zero();
             var transformed = Vector3.Zero();
             var index: number;
             var index: number;
             if (this.positions) {
             if (this.positions) {
@@ -369,6 +370,14 @@
                 }
                 }
             }
             }
 
 
+            if (flip && this.indices) {
+                for (index = 0; index < this.indices!.length; index += 3) {
+                    let tmp = this.indices[index + 1];
+                    this.indices[index + 1] = this.indices[index + 2];
+                    this.indices[index + 2] = tmp;
+                }
+            }
+
             return this;
             return this;
         }
         }
 
 

+ 4 - 2
src/Mesh/babylon.transformNode.ts

@@ -536,8 +536,10 @@ module BABYLON {
          * Returns the TransformNode.
          * Returns the TransformNode.
          */
          */
         public setParent(node: Nullable<Node>): TransformNode {
         public setParent(node: Nullable<Node>): TransformNode {
-
-            if (node === null) {
+            if (!node && !this.parent) {
+                return this;
+            }
+            if (!node) {
                 var rotation = Tmp.Quaternion[0];
                 var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
                 var position = Tmp.Vector3[0];
                 var scale = Tmp.Vector3[1];
                 var scale = Tmp.Vector3[1];

+ 4 - 4
src/Particles/EmitterTypes/babylon.boxParticleEmitter.ts

@@ -119,10 +119,10 @@ module BABYLON {
          * @param serializationObject defines the JSON object
          * @param serializationObject defines the JSON object
          */
          */
         public parse(serializationObject: any): void {
         public parse(serializationObject: any): void {
-            this.direction1.copyFrom(serializationObject.direction1);
-            this.direction2.copyFrom(serializationObject.direction2);
-            this.minEmitBox.copyFrom(serializationObject.minEmitBox);
-            this.maxEmitBox.copyFrom(serializationObject.maxEmitBox);
+            Vector3.FromArrayToRef(serializationObject.direction1, 0, this.direction1);
+            Vector3.FromArrayToRef(serializationObject.direction2, 0, this.direction2);
+            Vector3.FromArrayToRef(serializationObject.minEmitBox, 0, this.minEmitBox);
+            Vector3.FromArrayToRef(serializationObject.maxEmitBox, 0, this.maxEmitBox);
         }
         }
     }
     }
 }
 }

+ 66 - 4
src/Particles/babylon.IParticleSystem.ts

@@ -6,6 +6,10 @@ module BABYLON {
      */
      */
     export interface IParticleSystem {
     export interface IParticleSystem {
         /**
         /**
+         * List of animations used by the particle system.
+         */
+        animations: Animation[];
+        /**
          * The id of the Particle system.
          * The id of the Particle system.
          */
          */
         id: string;
         id: string;
@@ -114,12 +118,35 @@ module BABYLON {
          * Maximum power of emitting particles.
          * Maximum power of emitting particles.
          */
          */
         maxEmitPower: number;        
         maxEmitPower: number;        
-
+        /**
+         * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
+         */
+        minAngularSpeed: number;
+        /**
+         * Maximum angular speed of emitting particles (Z-axis rotation for each particle).
+         */
+        maxAngularSpeed: number;
+        /**
+         * Gets or sets the minimal initial rotation in radians.         
+         */
+        minInitialRotation: number;
+        /**
+         * Gets or sets the maximal initial rotation in radians.         
+         */
+        maxInitialRotation: number;         
         /**
         /**
          * The particle emitter type defines the emitter used by the particle system.
          * The particle emitter type defines the emitter used by the particle system.
          * It can be for example box, sphere, or cone...
          * It can be for example box, sphere, or cone...
          */
          */
-        particleEmitterType: Nullable<IParticleEmitterType>;        
+        particleEmitterType: Nullable<IParticleEmitterType>;  
+        /** 
+         * Gets or sets a value indicating how many cycles (or frames) must be executed before first rendering (this value has to be set before starting the system). Default is 0 
+         */
+        preWarmCycles: number;   
+        /** 
+         * Gets or sets a value indicating the time step multiplier to use in pre-warm mode (default is 1) 
+         */
+        preWarmStepOffset: number;               
 
 
         /**
         /**
          * Gets the maximum number of particles active at the same time.
          * Gets the maximum number of particles active at the same time.
@@ -188,6 +215,41 @@ module BABYLON {
          * Is this system ready to be used/rendered
          * Is this system ready to be used/rendered
          * @return true if the system is ready
          * @return true if the system is ready
          */
          */
-        isReady(): boolean;        
-    }
+        isReady(): boolean; 
+        /**
+         * Adds a new color gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param color defines the color to affect to the specified gradient
+         * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         */
+        addColorGradient(gradient: number, color1: Color4, color2?: Color4): IParticleSystem;   
+        /**
+         * Remove a specific color gradient
+         * @param gradient defines the gradient to remove
+         */
+        removeColorGradient(gradient: number): IParticleSystem;
+        /**
+         * Adds a new size gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient
+         */
+        addSizeGradient(gradient: number, factor: number): IParticleSystem;
+        /**
+         * Remove a specific size gradient
+         * @param gradient defines the gradient to remove
+         */
+        removeSizeGradient(gradient: number): IParticleSystem;
+        /**
+         * Gets the current list of color gradients.
+         * You must use addColorGradient and removeColorGradient to udpate this list
+         * @returns the list of color gradients
+         */
+        getColorGradients(): Nullable<Array<ColorGradient>>;
+        /**
+         * Gets the current list of size gradients.
+         * You must use addSizeGradient and removeSizeGradient to udpate this list
+         * @returns the list of size gradients
+         */
+        getSizeGradients(): Nullable<Array<FactorGradient>>;
+    }  
 }
 }

+ 420 - 185
src/Particles/babylon.gpuParticleSystem.ts

@@ -33,6 +33,7 @@
         private _capacity: number;
         private _capacity: number;
         private _activeCount: number;
         private _activeCount: number;
         private _currentActiveCount: number;
         private _currentActiveCount: number;
+        private _accumulatedCount = 0;
         private _renderEffect: Effect;
         private _renderEffect: Effect;
         private _updateEffect: Effect;
         private _updateEffect: Effect;
 
 
@@ -56,8 +57,9 @@
         private _timeDelta = 0;
         private _timeDelta = 0;
 
 
         private _randomTexture: RawTexture;
         private _randomTexture: RawTexture;
+        private _randomTexture2: RawTexture;
 
 
-        private _attributesStrideSize = 18;
+        private _attributesStrideSize = 21;
         private _updateEffectOptions: EffectCreationOptions;
         private _updateEffectOptions: EffectCreationOptions;
 
 
         private _randomTextureSize: number;
         private _randomTextureSize: number;
@@ -284,6 +286,23 @@
             this._activeCount = Math.min(value, this._capacity);
             this._activeCount = Math.min(value, this._capacity);
         }
         }
 
 
+        private _preWarmDone = false;
+
+        /** Gets or sets a value indicating how many cycles (or frames) must be executed before first rendering (this value has to be set before starting the system). Default is 0 */
+        public preWarmCycles = 0;
+
+        /** Gets or sets a value indicating the time step multiplier to use in pre-warm mode (default is 1) */
+        public preWarmStepOffset = 1;        
+
+        /**
+         * Gets or sets the minimal initial rotation in radians.         
+         */
+        public minInitialRotation = 0;
+        /**
+         * Gets or sets the maximal initial rotation in radians.         
+         */
+        public maxInitialRotation = 0;            
+
         /**
         /**
          * Is this system ready to be used/rendered
          * Is this system ready to be used/rendered
          * @return true if the system is ready
          * @return true if the system is ready
@@ -317,6 +336,7 @@
         public start(): void {
         public start(): void {
             this._started = true;
             this._started = true;
             this._stopped = false;
             this._stopped = false;
+            this._preWarmDone = false;
         }
         }
 
 
         /**
         /**
@@ -361,7 +381,158 @@
             this._isBillboardBased = value;
             this._isBillboardBased = value;
 
 
             this._releaseBuffers();
             this._releaseBuffers();
-        }          
+        }        
+        
+        private _colorGradients: Nullable<Array<ColorGradient>> = null;
+        private _colorGradientsTexture: RawTexture;
+
+        /**
+         * Gets the current list of color gradients.
+         * You must use addColorGradient and removeColorGradient to udpate this list
+         * @returns the list of color gradients
+         */
+        public getColorGradients(): Nullable<Array<ColorGradient>> {
+            return this._colorGradients;
+        }
+
+        /**
+         * Gets the current list of size gradients.
+         * You must use addSizeGradient and removeSizeGradient to udpate this list
+         * @returns the list of size gradients
+         */
+        public getSizeGradients(): Nullable<Array<FactorGradient>> {
+            return this._sizeGradients;
+        }               
+        
+        /**
+         * Adds a new color gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param color defines the color to affect to the specified gradient
+         * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         */
+        public addColorGradient(gradient: number, color1: Color4, color2?: Color4): GPUParticleSystem {
+            if (!this._colorGradients) {
+                this._colorGradients = [];
+            }
+
+            let colorGradient = new ColorGradient();
+            colorGradient.gradient = gradient;
+            colorGradient.color1 = color1;
+            this._colorGradients.push(colorGradient);
+
+            this._colorGradients.sort((a, b) => {
+                if (a.gradient < b.gradient) {
+                    return -1;
+                } else if (a.gradient > b.gradient) {
+                    return 1;
+                }
+
+                return 0;
+            });
+
+            if (this._colorGradientsTexture) {
+                this._colorGradientsTexture.dispose();
+                (<any>this._colorGradientsTexture) = null;
+            }
+
+            this._releaseBuffers();            
+
+            return this;
+        }
+
+        /**
+         * Remove a specific color gradient
+         * @param gradient defines the gradient to remove
+         */
+        public removeColorGradient(gradient: number): GPUParticleSystem {
+            if (!this._colorGradients) {
+                return this;
+            }
+
+            let index = 0;
+            for (var colorGradient of this._colorGradients) {
+                if (colorGradient.gradient === gradient) {
+                    this._colorGradients.splice(index, 1);
+                    break;
+                }
+                index++;
+            }
+
+            if (this._colorGradientsTexture) {
+                this._colorGradientsTexture.dispose();
+                (<any>this._colorGradientsTexture) = null;
+            }            
+
+            this._releaseBuffers();
+
+            return this;
+        }    
+        
+        private _sizeGradients: Nullable<Array<FactorGradient>> = null;
+        private _sizeGradientsTexture: RawTexture;        
+        
+        /**
+         * Adds a new size gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient
+         */
+        public addSizeGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._sizeGradients) {
+                this._sizeGradients = [];
+            }
+
+            let sizeGradient = new FactorGradient();
+            sizeGradient.gradient = gradient;
+            sizeGradient.factor = factor;
+            this._sizeGradients.push(sizeGradient);
+
+            this._sizeGradients.sort((a, b) => {
+                if (a.gradient < b.gradient) {
+                    return -1;
+                } else if (a.gradient > b.gradient) {
+                    return 1;
+                }
+
+                return 0;
+            });
+
+            if (this._sizeGradientsTexture) {
+                this._sizeGradientsTexture.dispose();
+                (<any>this._sizeGradientsTexture) = null;
+            }
+
+            this._releaseBuffers();                 
+
+            return this;
+        }
+
+        /**
+         * Remove a specific size gradient
+         * @param gradient defines the gradient to remove
+         */
+        public removeSizeGradient(gradient: number): GPUParticleSystem {
+            if (!this._sizeGradients) {
+                return this;
+            }
+
+            let index = 0;
+            for (var sizeGradient of this._sizeGradients) {
+                if (sizeGradient.gradient === gradient) {
+                    this._sizeGradients.splice(index, 1);
+                    break;
+                }
+                index++;
+            }
+
+            if (this._sizeGradientsTexture) {
+                this._sizeGradientsTexture.dispose();
+                (<any>this._sizeGradientsTexture) = null;
+            }
+
+            this._releaseBuffers();               
+
+            return this;
+        }            
 
 
         /**
         /**
          * Instantiates a GPU particle system.
          * Instantiates a GPU particle system.
@@ -385,6 +556,11 @@
                 ...options
                 ...options
             };
             };
 
 
+            var optionsAsNumber = <number>options;
+            if (isFinite(optionsAsNumber)) {
+                fullOptions.capacity = optionsAsNumber;
+            }
+
             this._capacity = fullOptions.capacity;
             this._capacity = fullOptions.capacity;
             this._activeCount = fullOptions.capacity;
             this._activeCount = fullOptions.capacity;
             this._currentActiveCount = 0;
             this._currentActiveCount = 0;
@@ -392,12 +568,12 @@
             this._scene.particleSystems.push(this);
             this._scene.particleSystems.push(this);
 
 
             this._updateEffectOptions = {
             this._updateEffectOptions = {
-                attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle"],
-                uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
+                attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "initialSize"],
+                uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "angleRange", "radiusRange"],
                                 "angleRange", "radiusRange"],
                 uniformBuffersNames: [],
                 uniformBuffersNames: [],
-                samplers:["randomSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler"],
                 defines: "",
                 defines: "",
                 fallbacks: null,  
                 fallbacks: null,  
                 onCompiled: null,
                 onCompiled: null,
@@ -407,6 +583,8 @@
                 transformFeedbackVaryings: []
                 transformFeedbackVaryings: []
             };
             };
 
 
+            this.particleEmitterType = new BoxParticleEmitter();
+
             // Random data
             // Random data
             var maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
             var maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
             var d = [];
             var d = [];
@@ -416,12 +594,22 @@
                 d.push(Math.random());
                 d.push(Math.random());
                 d.push(Math.random());
                 d.push(Math.random());
             }
             }
-            this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGBA, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT)
+            this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGBA, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT);
             this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
             this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
             this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
             this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
 
 
+            d = [];
+            for (var i = 0; i < maxTextureSize; ++i) {
+                d.push(Math.random());
+                d.push(Math.random());
+                d.push(Math.random());
+                d.push(Math.random());
+            }
+            this._randomTexture2 = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGBA, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT);
+            this._randomTexture2.wrapU = Texture.WRAP_ADDRESSMODE;
+            this._randomTexture2.wrapV = Texture.WRAP_ADDRESSMODE;
+
             this._randomTextureSize = maxTextureSize;
             this._randomTextureSize = maxTextureSize;
-            this.particleEmitterType = new BoxParticleEmitter();
         }
         }
 
 
         private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {            
         private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {            
@@ -429,12 +617,22 @@
             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);
-            updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 1);
-            updateVertexBuffers["size"] = source.createVertexBuffer("size", 6, 3);
-            updateVertexBuffers["color"] = source.createVertexBuffer("color", 9, 4);
-            updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 13, 3);
+            updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 4);
+            updateVertexBuffers["size"] = source.createVertexBuffer("size", 9, 3);
+            let offset = 12;
+            if (this._sizeGradientsTexture) {
+                updateVertexBuffers["initialSize"] = source.createVertexBuffer("initialSize", offset, 3);
+                offset += 3;
+            }
+
+            if (!this._colorGradientsTexture) {
+                updateVertexBuffers["color"] = source.createVertexBuffer("color", offset, 4);
+                offset += 4;
+            }
+
+            updateVertexBuffers["direction"] = source.createVertexBuffer("direction", offset, 3);
+            offset += 3
 
 
-            let offset = 16;
             if (!this._isBillboardBased) {
             if (!this._isBillboardBased) {
                 updateVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3);
                 updateVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3);
                 offset += 3;
                 offset += 3;
@@ -453,10 +651,20 @@
             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);
-            renderVertexBuffers["size"] = source.createVertexBuffer("size", 6, 3, this._attributesStrideSize, true);           
-            renderVertexBuffers["color"] = source.createVertexBuffer("color", 9, 4, this._attributesStrideSize, true);
+            renderVertexBuffers["size"] = source.createVertexBuffer("size", 9, 3, this._attributesStrideSize, true);      
+            
+            let offset = 12;
+            if (this._sizeGradientsTexture) {
+                offset += 3;
+            }
+
+            if (!this._colorGradientsTexture) {
+                renderVertexBuffers["color"] = source.createVertexBuffer("color", offset, 4, this._attributesStrideSize, true);
+                offset += 4;
+            }
+            
+            offset += 3; // Direction
 
 
-            let offset = 16;
             if (!this._isBillboardBased) {
             if (!this._isBillboardBased) {
                 renderVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
                 renderVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
                 offset += 3;
                 offset += 3;
@@ -481,7 +689,15 @@
             var data = new Array<float>();
             var data = new Array<float>();
 
 
             if (!this.isBillboardBased) {
             if (!this.isBillboardBased) {
-                this._attributesStrideSize = 21;
+                this._attributesStrideSize += 3;
+            }
+
+            if (this._colorGradientsTexture) {
+                this._attributesStrideSize -= 4;
+            }
+
+            if (this._sizeGradientsTexture) {
+                this._attributesStrideSize += 3;
             }
             }
 
 
             for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
             for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
@@ -496,17 +712,28 @@
 
 
                 // Seed
                 // Seed
                 data.push(Math.random());
                 data.push(Math.random());
+                data.push(Math.random());
+                data.push(Math.random());
+                data.push(Math.random());
 
 
                 // Size
                 // Size
                 data.push(0.0);
                 data.push(0.0);
                 data.push(0.0);
                 data.push(0.0);
                 data.push(0.0);
                 data.push(0.0);
 
 
-                // color
-                data.push(0.0);
-                data.push(0.0);
-                data.push(0.0);                     
-                data.push(0.0); 
+                if (this._sizeGradientsTexture) {
+                    data.push(0.0);
+                    data.push(0.0);
+                    data.push(0.0);  
+                }                
+
+                if (!this._colorGradientsTexture) {
+                    // color
+                    data.push(0.0);
+                    data.push(0.0);
+                    data.push(0.0);                     
+                    data.push(0.0); 
+                }
 
 
                 // direction
                 // direction
                 data.push(0.0);
                 data.push(0.0);
@@ -560,11 +787,29 @@
                 defines += "\n#define BILLBOARD";
                 defines += "\n#define BILLBOARD";
             }   
             }   
 
 
+            if (this._colorGradientsTexture) {
+                defines += "\n#define COLORGRADIENTS";
+            }        
+            
+            if (this._sizeGradientsTexture) {
+                defines += "\n#define SIZEGRADIENTS";
+            }                 
+
             if (this._updateEffect && this._updateEffectOptions.defines === defines) {
             if (this._updateEffect && this._updateEffectOptions.defines === defines) {
                 return;
                 return;
             }
             }
 
 
-            this._updateEffectOptions.transformFeedbackVaryings = ["outPosition", "outAge", "outLife", "outSeed", "outSize", "outColor", "outDirection"];           
+            this._updateEffectOptions.transformFeedbackVaryings = ["outPosition", "outAge", "outLife", "outSeed", "outSize"];           
+
+            if (this._sizeGradientsTexture) {
+                this._updateEffectOptions.transformFeedbackVaryings.push("outInitialSize");
+            }
+
+            if (!this._colorGradientsTexture) {
+                this._updateEffectOptions.transformFeedbackVaryings.push("outColor");
+            }
+
+            this._updateEffectOptions.transformFeedbackVaryings.push("outDirection");
 
 
             if (!this._isBillboardBased) {
             if (!this._isBillboardBased) {
                 this._updateEffectOptions.transformFeedbackVaryings.push("outInitialDirection");
                 this._updateEffectOptions.transformFeedbackVaryings.push("outInitialDirection");
@@ -585,7 +830,11 @@
 
 
             if (this._isBillboardBased) {
             if (this._isBillboardBased) {
                 defines += "\n#define BILLBOARD";
                 defines += "\n#define BILLBOARD";
-            }            
+            }         
+            
+            if (this._colorGradientsTexture) {
+                defines += "\n#define COLORGRADIENTS";
+            }   
 
 
             if (this._renderEffect && this._renderEffect.defines === defines) {
             if (this._renderEffect && this._renderEffect.defines === defines) {
                 return;
                 return;
@@ -594,14 +843,15 @@
             this._renderEffect = new Effect("gpuRenderParticles", 
             this._renderEffect = new Effect("gpuRenderParticles", 
                                             ["position", "age", "life", "size", "color", "offset", "uv", "initialDirection", "angle"], 
                                             ["position", "age", "life", "size", "color", "offset", "uv", "initialDirection", "angle"], 
                                             ["view", "projection", "colorDead", "invView", "vClipPlane"], 
                                             ["view", "projection", "colorDead", "invView", "vClipPlane"], 
-                                            ["textureSampler"], this._scene.getEngine(), defines);
+                                            ["textureSampler", "colorGradientSampler"], this._scene.getEngine(), defines);
         }        
         }        
 
 
         /**
         /**
          * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
          * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
+         * @param preWarm defines if we are in the pre-warmimg phase
          */
          */
-        public animate(): void {           
-            this._timeDelta = this.updateSpeed * this._scene.getAnimationRatio();   
+        public animate(preWarm = false): void {           
+            this._timeDelta = this.updateSpeed * (preWarm ? this.preWarmStepOffset : this._scene.getAnimationRatio());
             this._actualFrame += this._timeDelta;
             this._actualFrame += this._timeDelta;
 
 
             if (!this._stopped) {
             if (!this._stopped) {
@@ -609,17 +859,66 @@
                     this.stop();
                     this.stop();
                 }
                 }
             }             
             }             
+        }    
+
+        private _createSizeGradientTexture() {
+            if (!this._sizeGradients || !this._sizeGradients.length || this._sizeGradientsTexture) {
+                return;
+            }
+
+            let textureWidth = 256;
+            let data = new Float32Array(textureWidth);
+
+            for (var x = 0; x < textureWidth; x++) {
+                var ratio = x / textureWidth;
+
+                Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
+                    data[x] = Scalar.Lerp((<FactorGradient>currentGradient).factor, (<FactorGradient>nextGradient).factor, scale);
+                });
+            }
+
+            this._sizeGradientsTexture = RawTexture.CreateRTexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
         }        
         }        
+            
+        private _createColorGradientTexture() {
+            if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
+                return;
+            }
+
+            let textureWidth = 256;
+            let data = new Uint8Array(textureWidth * 4);
+            let tmpColor = Tmp.Color4[0];
+
+            for (var x = 0; x < textureWidth; x++) {
+                var ratio = x / textureWidth;
+
+                Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
+
+                    Color4.LerpToRef((<ColorGradient>currentGradient).color1, (<ColorGradient>nextGradient).color1, scale, tmpColor);
+                    data[x * 4] = tmpColor.r * 255;
+                    data[x * 4 + 1] = tmpColor.g * 255;
+                    data[x * 4 + 2] = tmpColor.b * 255;
+                    data[x * 4 + 3] = tmpColor.a * 255;
+                });
+
+            }
+
+            this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        }
 
 
         /**
         /**
-         * Renders the particle system in its current state.
+         * Renders the particle system in its current state
+         * @param preWarm defines if the system should only update the particles but not render them
          * @returns the current number of particles
          * @returns the current number of particles
          */
          */
-        public render(): number {
+        public render(preWarm = false): number {
             if (!this._started) {
             if (!this._started) {
                 return 0;
                 return 0;
             }
             }
 
 
+            this._createColorGradientTexture();
+            this._createSizeGradientTexture();
+
             this._recreateUpdateEffect();
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
             this._recreateRenderEffect();
 
 
@@ -627,36 +926,61 @@
                 return 0;
                 return 0;
             }
             }
 
 
-            if (this._currentRenderId === this._scene.getRenderId()) {
-                return 0;
-            }
+            if (!preWarm) {
+                if (!this._preWarmDone && this.preWarmCycles) {                
+                    for (var index = 0; index < this.preWarmCycles; index++) {
+                        this.animate(true);
+                        this.render(true);
+                    }
+
+                    this._preWarmDone = true;
+                }
+
+                if (this._currentRenderId === this._scene.getRenderId()) {
+                    return 0;
+                }
 
 
-            this._currentRenderId = this._scene.getRenderId();      
+                this._currentRenderId = this._scene.getRenderId();      
+            }
             
             
             // Get everything ready to render
             // Get everything ready to render
             this._initialize();
             this._initialize();
 
 
-            this._currentActiveCount = Math.min(this._activeCount, this._currentActiveCount + (this.emitRate * this._timeDelta) | 0);
+            this._accumulatedCount += this.emitRate * this._timeDelta;
+            if (this._accumulatedCount > 1) {
+                var intPart = this._accumulatedCount | 0;
+                this._accumulatedCount -= intPart;
+                this._currentActiveCount = Math.min(this._activeCount, this._currentActiveCount + intPart);
+            }
+
+            if (!this._currentActiveCount) {
+                return 0;
+            }
             
             
             // Enable update effect
             // Enable update effect
-
             this._engine.enableEffect(this._updateEffect);
             this._engine.enableEffect(this._updateEffect);
             this._engine.setState(false);    
             this._engine.setState(false);    
             
             
             this._updateEffect.setFloat("currentCount", this._currentActiveCount);
             this._updateEffect.setFloat("currentCount", this._currentActiveCount);
             this._updateEffect.setFloat("timeDelta", this._timeDelta);
             this._updateEffect.setFloat("timeDelta", this._timeDelta);
             this._updateEffect.setFloat("stopFactor", this._stopped ? 0 : 1);
             this._updateEffect.setFloat("stopFactor", this._stopped ? 0 : 1);
-            this._updateEffect.setFloat3("generalRandoms", Math.random(), Math.random(), Math.random());
             this._updateEffect.setTexture("randomSampler", this._randomTexture);
             this._updateEffect.setTexture("randomSampler", this._randomTexture);
+            this._updateEffect.setTexture("randomSampler2", this._randomTexture2);
             this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
             this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
             this._updateEffect.setFloat2("emitPower", this.minEmitPower, this.maxEmitPower);
             this._updateEffect.setFloat2("emitPower", this.minEmitPower, this.maxEmitPower);
-            this._updateEffect.setDirectColor4("color1", this.color1);
-            this._updateEffect.setDirectColor4("color2", this.color2);
+            if (!this._colorGradientsTexture) {            
+                this._updateEffect.setDirectColor4("color1", this.color1);
+                this._updateEffect.setDirectColor4("color2", this.color2);
+            }
             this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
             this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
             this._updateEffect.setFloat4("scaleRange", this.minScaleX, this.maxScaleX, this.minScaleY, this.maxScaleY);
             this._updateEffect.setFloat4("scaleRange", this.minScaleX, this.maxScaleX, this.minScaleY, this.maxScaleY);
-            this._updateEffect.setFloat2("angleRange", this.minAngularSpeed, this.maxAngularSpeed);
+            this._updateEffect.setFloat4("angleRange", this.minAngularSpeed, this.maxAngularSpeed, this.minInitialRotation, this.maxInitialRotation);
             this._updateEffect.setVector3("gravity", this.gravity);
             this._updateEffect.setVector3("gravity", this.gravity);
 
 
+            if (this._sizeGradientsTexture) {      
+                this._updateEffect.setTexture("sizeGradientSampler", this._sizeGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
             }
@@ -683,48 +1007,53 @@
             this._engine.setRasterizerState(true);
             this._engine.setRasterizerState(true);
             this._engine.bindTransformFeedbackBuffer(null);
             this._engine.bindTransformFeedbackBuffer(null);
 
 
-            // Enable render effect
-            this._engine.enableEffect(this._renderEffect);
-            let viewMatrix = this._scene.getViewMatrix();
-            this._renderEffect.setMatrix("view", viewMatrix);
-            this._renderEffect.setMatrix("projection", this._scene.getProjectionMatrix());
-            this._renderEffect.setTexture("textureSampler", this.particleTexture);
-            this._renderEffect.setDirectColor4("colorDead", this.colorDead);
+            if (!preWarm) {
+                // Enable render effect
+                this._engine.enableEffect(this._renderEffect);
+                let viewMatrix = this._scene.getViewMatrix();
+                this._renderEffect.setMatrix("view", viewMatrix);
+                this._renderEffect.setMatrix("projection", this._scene.getProjectionMatrix());
+                this._renderEffect.setTexture("textureSampler", this.particleTexture);
+                if (this._colorGradientsTexture) {
+                    this._renderEffect.setTexture("colorGradientSampler", this._colorGradientsTexture);
+                } else {
+                    this._renderEffect.setDirectColor4("colorDead", this.colorDead);
+                }
 
 
 
 
-            if (this._scene.clipPlane) {
-                var clipPlane = this._scene.clipPlane;
-                var invView = viewMatrix.clone();
-                invView.invert();
-                this._renderEffect.setMatrix("invView", invView);
-                this._renderEffect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
-            }            
+                if (this._scene.clipPlane) {
+                    var clipPlane = this._scene.clipPlane;
+                    var invView = viewMatrix.clone();
+                    invView.invert();
+                    this._renderEffect.setMatrix("invView", invView);
+                    this._renderEffect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
+                }            
 
 
-            // Draw order
-            switch(this.blendMode)
-            {
-                case ParticleSystem.BLENDMODE_ADD:
-                    this._engine.setAlphaMode(Engine.ALPHA_ADD);
-                    break;
-                case ParticleSystem.BLENDMODE_ONEONE:
-                    this._engine.setAlphaMode(Engine.ALPHA_ONEONE);
-                    break;
-                case ParticleSystem.BLENDMODE_STANDARD:
-                    this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
-                    break;
-            }      
-
-            if (this.forceDepthWrite) {
-                this._engine.setDepthWrite(true);
-            }
+                // Draw order
+                switch(this.blendMode)
+                {
+                    case ParticleSystem.BLENDMODE_ADD:
+                        this._engine.setAlphaMode(Engine.ALPHA_ADD);
+                        break;
+                    case ParticleSystem.BLENDMODE_ONEONE:
+                        this._engine.setAlphaMode(Engine.ALPHA_ONEONE);
+                        break;
+                    case ParticleSystem.BLENDMODE_STANDARD:
+                        this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
+                        break;
+                }      
 
 
-            // Bind source VAO
-            this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
+                if (this.forceDepthWrite) {
+                    this._engine.setDepthWrite(true);
+                }
 
 
-            // Render
-            this._engine.drawArraysType(Material.TriangleFanDrawMode, 0, 4, this._currentActiveCount);   
-            this._engine.setAlphaMode(Engine.ALPHA_DISABLE);         
+                // Bind source VAO
+                this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
 
 
+                // Render
+                this._engine.drawArraysType(Material.TriangleFanDrawMode, 0, 4, this._currentActiveCount);   
+                this._engine.setAlphaMode(Engine.ALPHA_DISABLE);         
+            }
             // Switch VAOs
             // Switch VAOs
             this._targetIndex++;
             this._targetIndex++;
             if (this._targetIndex === 2) {
             if (this._targetIndex === 2) {
@@ -789,13 +1118,27 @@
 
 
             this._releaseBuffers();
             this._releaseBuffers();
             this._releaseVAOs();
             this._releaseVAOs();
-         
 
 
+            if (this._colorGradientsTexture) {
+                this._colorGradientsTexture.dispose();
+                (<any>this._colorGradientsTexture) = null;
+            }
+
+            if (this._sizeGradientsTexture) {
+                this._sizeGradientsTexture.dispose();
+                (<any>this._sizeGradientsTexture) = null;
+            }            
+         
             if (this._randomTexture) {
             if (this._randomTexture) {
                 this._randomTexture.dispose();
                 this._randomTexture.dispose();
                 (<any>this._randomTexture) = null;
                 (<any>this._randomTexture) = null;
             }
             }
 
 
+            if (this._randomTexture2) {
+                this._randomTexture2.dispose();
+                (<any>this._randomTexture2) = null;
+            }            
+
             if (disposeTexture && this.particleTexture) {
             if (disposeTexture && this.particleTexture) {
                 this.particleTexture.dispose();
                 this.particleTexture.dispose();
                 this.particleTexture = null;
                 this.particleTexture = null;
@@ -835,51 +1178,7 @@
         public serialize(): any {
         public serialize(): any {
             var serializationObject: any = {};
             var serializationObject: any = {};
 
 
-            serializationObject.name = this.name;
-            serializationObject.id = this.id;
-
-            // Emitter
-            if ((<AbstractMesh>this.emitter).position) {
-                var emitterMesh = (<AbstractMesh>this.emitter);
-                serializationObject.emitterId = emitterMesh.id;
-            } else {
-                var emitterPosition = (<Vector3>this.emitter);
-                serializationObject.emitter = emitterPosition.asArray();
-            }
-
-            serializationObject.capacity = this.getCapacity();
-
-            if (this.particleTexture) {
-                serializationObject.textureName = this.particleTexture.name;
-            }
-
-            // Animations
-            Animation.AppendSerializedAnimations(this, serializationObject);
-
-            // Particle system
-            serializationObject.activeParticleCount = this.activeParticleCount;
-            serializationObject.randomTextureSize = this._randomTextureSize;
-            serializationObject.minSize = this.minSize;
-            serializationObject.maxSize = this.maxSize;
-            serializationObject.minEmitPower = this.minEmitPower;
-            serializationObject.maxEmitPower = this.maxEmitPower;
-            serializationObject.minLifeTime = this.minLifeTime;
-            serializationObject.maxLifeTime = this.maxLifeTime;            
-            serializationObject.minAngularSpeed = this.minAngularSpeed;
-            serializationObject.maxAngularSpeed = this.maxAngularSpeed;
-            serializationObject.emitRate = this.emitRate;
-            serializationObject.gravity = this.gravity.asArray();
-            serializationObject.color1 = this.color1.asArray();
-            serializationObject.color2 = this.color2.asArray();
-            serializationObject.colorDead = this.colorDead.asArray();
-            serializationObject.updateSpeed = this.updateSpeed;
-            serializationObject.targetStopDuration = this.targetStopDuration;
-            serializationObject.blendMode = this.blendMode;
-
-            // Emitter
-            if (this.particleEmitterType) {
-                serializationObject.particleEmitterType = this.particleEmitterType.serialize();
-            }
+            ParticleSystem._Serialize(serializationObject, this);
 
 
             return serializationObject;            
             return serializationObject;            
         }
         }
@@ -895,72 +1194,8 @@
             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.id) {
-                particleSystem.id = parsedParticleSystem.id;
-            }
-
-            // Texture
-            if (parsedParticleSystem.textureName) {
-                particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene);
-                particleSystem.particleTexture.name = parsedParticleSystem.textureName;
-            }
-
-            // Emitter
-            if (parsedParticleSystem.emitterId) {
-                particleSystem.emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
-            } else {
-                particleSystem.emitter = Vector3.FromArray(parsedParticleSystem.emitter);
-            }
-
-            // Animations
-            if (parsedParticleSystem.animations) {
-                for (var animationIndex = 0; animationIndex < parsedParticleSystem.animations.length; animationIndex++) {
-                    var parsedAnimation = parsedParticleSystem.animations[animationIndex];
-                    particleSystem.animations.push(Animation.Parse(parsedAnimation));
-                }
-            }
-
-            // Particle system
             particleSystem.activeParticleCount = parsedParticleSystem.activeParticleCount;
             particleSystem.activeParticleCount = parsedParticleSystem.activeParticleCount;
-            particleSystem.minSize = parsedParticleSystem.minSize;
-            particleSystem.maxSize = parsedParticleSystem.maxSize;
-            particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
-            particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
-            particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
-            particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
-            particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
-            particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
-            particleSystem.emitRate = parsedParticleSystem.emitRate;
-            particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
-            particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
-            particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
-            particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
-            particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
-            particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
-            particleSystem.blendMode = parsedParticleSystem.blendMode;
-
-            // Emitter
-            if (parsedParticleSystem.particleEmitterType) {
-                let emitterType: IParticleEmitterType;
-                switch (parsedParticleSystem.particleEmitterType.type) {
-                    case "SphereEmitter":
-                        emitterType = new SphereParticleEmitter();
-                        break;
-                    case "SphereDirectedParticleEmitter":
-                        emitterType = new SphereDirectedParticleEmitter();
-                        break;
-                    case "ConeEmitter":
-                        emitterType = new ConeParticleEmitter();
-                        break;
-                    case "BoxEmitter":
-                    default:
-                        emitterType = new BoxParticleEmitter();
-                        break;                                                
-                }
-
-                emitterType.parse(parsedParticleSystem.particleEmitterType);
-                particleSystem.particleEmitterType = emitterType;
-            }
+            ParticleSystem._Parse(parsedParticleSystem, particleSystem, scene, rootUrl);
 
 
             return particleSystem;
             return particleSystem;
         }        
         }        

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

@@ -65,6 +65,9 @@
         /** @hidden */
         /** @hidden */
         public _initialDirection: Nullable<Vector3>;
         public _initialDirection: Nullable<Vector3>;
 
 
+        /** @hidden */
+        public _initialSize: number;
+
         /**
         /**
          * Creates a new instance Particle
          * Creates a new instance Particle
          * @param particleSystem the particle system the particle belongs to
          * @param particleSystem the particle system the particle belongs to

+ 337 - 131
src/Particles/babylon.particleSystem.ts

@@ -1,11 +1,5 @@
 module BABYLON {
 module BABYLON {
 
 
-    /** @hidden */
-    class ColorGradient {
-        public gradient: number;
-        public color: Color4;
-    }
-
     /**
     /**
      * This represents a particle system in Babylon.
      * This represents a particle system in Babylon.
      * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
      * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
@@ -120,7 +114,16 @@
         /**
         /**
          * Maximum scale of emitting particles on Y axis.
          * Maximum scale of emitting particles on Y axis.
          */
          */
-        public maxScaleY = 1;           
+        public maxScaleY = 1;          
+        
+        /**
+         * Gets or sets the minimal initial rotation in radians.         
+         */
+        public minInitialRotation = 0;
+        /**
+         * Gets or sets the maximal initial rotation in radians.         
+         */
+        public maxInitialRotation = 0;             
 
 
         /**
         /**
          * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
          * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
@@ -182,8 +185,27 @@
         public gravity = Vector3.Zero();
         public gravity = Vector3.Zero();
 
 
         private _colorGradients: Nullable<Array<ColorGradient>> = null;
         private _colorGradients: Nullable<Array<ColorGradient>> = null;
+        private _sizeGradients: Nullable<Array<FactorGradient>> = null;
+
+        /**
+         * Gets the current list of color gradients.
+         * You must use addColorGradient and removeColorGradient to udpate this list
+         * @returns the list of color gradients
+         */
+        public getColorGradients(): Nullable<Array<ColorGradient>> {
+            return this._colorGradients;
+        }
+
+        /**
+         * Gets the current list of size gradients.
+         * You must use addSizeGradient and removeSizeGradient to udpate this list
+         * @returns the list of size gradients
+         */
+        public getSizeGradients(): Nullable<Array<FactorGradient>> {
+            return this._sizeGradients;
+        }        
 
 
-       /**
+        /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
          * This only works when particleEmitterTyps is a BoxParticleEmitter
          * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
          */
@@ -315,6 +337,12 @@
          */
          */
         public spriteCellHeight = 0;
         public spriteCellHeight = 0;
 
 
+        /** Gets or sets a value indicating how many cycles (or frames) must be executed before first rendering (this value has to be set before starting the system). Default is 0 */
+        public preWarmCycles = 0;
+
+        /** Gets or sets a value indicating the time step multiplier to use in pre-warm mode (default is 1) */
+        public preWarmStepOffset = 1;
+
         /**
         /**
         * An event triggered when the system is disposed
         * An event triggered when the system is disposed
         */
         */
@@ -459,19 +487,18 @@
                         continue;
                         continue;
                     }
                     }
                     else {
                     else {
-                        if (this._colorGradients && this._colorGradients.length > 0) {
-                            let ratio = particle.age / particle.lifeTime;
-
-                            for (var gradientIndex = 0; gradientIndex < this._colorGradients.length - 1; gradientIndex++) {
-                                let currentGradient = this._colorGradients[gradientIndex];
-                                let nextGradient = this._colorGradients[gradientIndex + 1];
+                        let ratio = particle.age / particle.lifeTime;
 
 
-                                if (ratio >= currentGradient.gradient && ratio <= nextGradient.gradient) {
-                                    let scale = (ratio - currentGradient.gradient) / (nextGradient.gradient - currentGradient.gradient);
-                                    Color4.LerpToRef(currentGradient.color, nextGradient.color, scale, particle.color);
-                                    break;
-                               }
-                            }
+                        // Color
+                        if (this._colorGradients && this._colorGradients.length > 0) {
+                            var color1 = Tmp.Color4[0];
+                            var color2 = Tmp.Color4[1];
+
+                            Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
+                                (<ColorGradient>currentGradient).getColorToRef(color1);
+                                (<ColorGradient>nextGradient).getColorToRef(color2);
+                                Color4.LerpToRef(color1, color2, scale, particle.color);
+                            });
                         }
                         }
                         else {
                         else {
                             particle.colorStep.scaleToRef(this._scaledUpdateSpeed, this._scaledColorStep);
                             particle.colorStep.scaleToRef(this._scaledUpdateSpeed, this._scaledColorStep);
@@ -489,6 +516,13 @@
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
                         particle.direction.addInPlace(this._scaledGravity);
                         particle.direction.addInPlace(this._scaledGravity);
 
 
+                        // Gradient
+                        if (this._sizeGradients && this._sizeGradients.length > 0) {
+                            Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
+                                particle.size = particle._initialSize * Scalar.Lerp((<FactorGradient>currentGradient).factor, (<FactorGradient>nextGradient).factor, scale);
+                            });
+                        }
+
                         if (this._isAnimationSheetEnabled) {
                         if (this._isAnimationSheetEnabled) {
                             particle.updateCellIndex(this._scaledUpdateSpeed);
                             particle.updateCellIndex(this._scaledUpdateSpeed);
                         }
                         }
@@ -498,18 +532,69 @@
         }
         }
 
 
         /**
         /**
+         * Adds a new size gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient
+         */
+        public addSizeGradient(gradient: number, factor: number): ParticleSystem {
+            if (!this._sizeGradients) {
+                this._sizeGradients = [];
+            }
+
+            let sizeGradient = new FactorGradient();
+            sizeGradient.gradient = gradient;
+            sizeGradient.factor = factor;
+            this._sizeGradients.push(sizeGradient);
+
+            this._sizeGradients.sort((a, b) => {
+                if (a.gradient < b.gradient) {
+                    return -1;
+                } else if (a.gradient > b.gradient) {
+                    return 1;
+                }
+
+                return 0;
+            });
+
+            return this;
+        }
+
+        /**
+         * Remove a specific size gradient
+         * @param gradient defines the gradient to remove
+         */
+        public removeSizeGradient(gradient: number): ParticleSystem {
+            if (!this._sizeGradients) {
+                return this;
+            }
+
+            let index = 0;
+            for (var sizeGradient of this._sizeGradients) {
+                if (sizeGradient.gradient === gradient) {
+                    this._sizeGradients.splice(index, 1);
+                    break;
+                }
+                index++;
+            }
+
+            return this;
+        }        
+
+        /**
          * Adds a new color gradient
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
          * @param color defines the color to affect to the specified gradient
+         * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
          */
          */
-        public addColorGradient(gradient: number, color: Color4): ParticleSystem {
+        public addColorGradient(gradient: number, color: Color4, color2?: Color4): ParticleSystem {
             if (!this._colorGradients) {
             if (!this._colorGradients) {
                 this._colorGradients = [];
                 this._colorGradients = [];
             }
             }
 
 
             let colorGradient = new ColorGradient();
             let colorGradient = new ColorGradient();
             colorGradient.gradient = gradient;
             colorGradient.gradient = gradient;
-            colorGradient.color = color;
+            colorGradient.color1 = color;
+            colorGradient.color2 = color2;
             this._colorGradients.push(colorGradient);
             this._colorGradients.push(colorGradient);
 
 
             this._colorGradients.sort((a, b) => {
             this._colorGradients.sort((a, b) => {
@@ -520,7 +605,7 @@
                 }
                 }
 
 
                 return 0;
                 return 0;
-            })
+            });
 
 
             return this;
             return this;
         }
         }
@@ -668,6 +753,12 @@
             if (this.subEmitters && this.subEmitters.length != 0) {
             if (this.subEmitters && this.subEmitters.length != 0) {
                 this.activeSubSystems = new Array<ParticleSystem>();
                 this.activeSubSystems = new Array<ParticleSystem>();
             }
             }
+
+            if (this.preWarmCycles) {
+                for (var index = 0; index < this.preWarmCycles; index++) {
+                    this.animate(true);
+                }
+            }
         }
         }
 
 
         /**
         /**
@@ -864,9 +955,12 @@
                 particle.lifeTime = Scalar.RandomRange(this.minLifeTime, this.maxLifeTime);
                 particle.lifeTime = Scalar.RandomRange(this.minLifeTime, this.maxLifeTime);
 
 
                 particle.size = Scalar.RandomRange(this.minSize, this.maxSize);
                 particle.size = Scalar.RandomRange(this.minSize, this.maxSize);
+                particle._initialSize = particle.size;
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
                 particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
                 particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
 
 
+                particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
+
                 if (!this._colorGradients || this._colorGradients.length === 0) {
                 if (!this._colorGradients || this._colorGradients.length === 0) {
                     var step = Scalar.RandomRange(0, 1.0);
                     var step = Scalar.RandomRange(0, 1.0);
 
 
@@ -875,11 +969,36 @@
                     this.colorDead.subtractToRef(particle.color, this._colorDiff);
                     this.colorDead.subtractToRef(particle.color, this._colorDiff);
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                 } else {
                 } else {
-                    particle.color.copyFrom(this._colorGradients[0].color);
+                    this._colorGradients[0].getColorToRef(particle.color);
                 }
                 }
             }
             }
         }
         }
 
 
+        /** @hidden */
+        public static _GetAttributeNamesOrOptions(isAnimationSheetEnabled = false, isBillboardBased = false): string[] {
+            var attributeNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "angle", "offset", "size"];
+
+            if (isAnimationSheetEnabled) {
+                attributeNamesOrOptions.push("cellIndex");
+            }
+
+            if (!isBillboardBased) {
+                attributeNamesOrOptions.push("direction");
+            }            
+
+            return attributeNamesOrOptions;
+        }
+
+        public static _GetEffectCreationOptions(isAnimationSheetEnabled = false): string[] {
+            var effectCreationOption = ["invView", "view", "projection", "vClipPlane", "textureMask"];
+
+            if (isAnimationSheetEnabled) {
+                effectCreationOption.push("particlesInfos")
+            }
+
+            return effectCreationOption;
+        }
+
         private _getEffect(): Effect {
         private _getEffect(): Effect {
             if (this._customEffect) {
             if (this._customEffect) {
                 return this._customEffect;
                 return this._customEffect;
@@ -904,17 +1023,8 @@
             if (this._cachedDefines !== join) {
             if (this._cachedDefines !== join) {
                 this._cachedDefines = join;
                 this._cachedDefines = join;
 
 
-                var attributesNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "angle", "offset", "size"];
-                var effectCreationOption = ["invView", "view", "projection", "vClipPlane", "textureMask"];
-
-                if (this._isAnimationSheetEnabled) {
-                    attributesNamesOrOptions.push("cellIndex");
-                    effectCreationOption.push("particlesInfos")
-                }
-
-                if (!this._isBillboardBased) {
-                    attributesNamesOrOptions.push("direction");
-                }
+                var attributesNamesOrOptions = ParticleSystem._GetAttributeNamesOrOptions(this._isAnimationSheetEnabled, this._isBillboardBased);
+                var effectCreationOption = ParticleSystem._GetEffectCreationOptions(this._isAnimationSheetEnabled);
 
 
                 this._effect = this._scene.getEngine().createEffect(
                 this._effect = this._scene.getEngine().createEffect(
                     "particles",
                     "particles",
@@ -928,24 +1038,26 @@
 
 
         /**
         /**
          * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
          * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
+         * @param preWarmOnly will prevent the system from updating the vertex buffer (default is false)
          */
          */
-        public animate(): void {
+        public animate(preWarmOnly = false): void {
             if (!this._started)
             if (!this._started)
                 return;
                 return;
 
 
-            var effect = this._getEffect();
+            if (!preWarmOnly) {
+                var effect = this._getEffect();
 
 
-            // Check
-            if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady())
-                return;
+                // Check
+                if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady())
+                    return;
 
 
-            if (this._currentRenderId === this._scene.getRenderId()) {
-                return;
+                if (this._currentRenderId === this._scene.getRenderId()) {
+                    return;
+                }
+                this._currentRenderId = this._scene.getRenderId();
             }
             }
 
 
-            this._currentRenderId = this._scene.getRenderId();
-
-            this._scaledUpdateSpeed = this.updateSpeed * this._scene.getAnimationRatio();
+            this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene.getAnimationRatio());
 
 
             // determine the number of particles we need to create
             // determine the number of particles we need to create
             var newParticles;
             var newParticles;
@@ -989,16 +1101,18 @@
                 }
                 }
             }
             }
 
 
-            // Update VBO
-            var offset = 0;
-            for (var index = 0; index < this._particles.length; index++) {
-                var particle = this._particles[index];
-                this._appendParticleVertices(offset, particle);                
-                offset += this._useInstancing ? 1 : 4;
-            }
+            if (!preWarmOnly) {
+                // Update VBO
+                var offset = 0;
+                for (var index = 0; index < this._particles.length; index++) {
+                    var particle = this._particles[index];
+                    this._appendParticleVertices(offset, particle);                
+                    offset += this._useInstancing ? 1 : 4;
+                }
 
 
-            if (this._vertexBuffer) {
-                this._vertexBuffer.update(this._vertexData);
+                if (this._vertexBuffer) {
+                    this._vertexBuffer.update(this._vertexData);
+                }
             }
             }
 
 
             if (this.manualEmitCount === 0 && this.disposeOnStop) {
             if (this.manualEmitCount === 0 && this.disposeOnStop) {
@@ -1215,6 +1329,8 @@
                 program = this.customShader;
                 program = this.customShader;
                 var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
                 var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
                 custom = this._scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
                 custom = this._scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
+            } else if (this._customEffect) {
+                custom = this._customEffect;
             }
             }
             var result = new ParticleSystem(name, this._capacity, this._scene, custom);
             var result = new ParticleSystem(name, this._capacity, this._scene, custom);
             result.customShader = program;
             result.customShader = program;
@@ -1244,53 +1360,9 @@
         public serialize(): any {
         public serialize(): any {
             var serializationObject: any = {};
             var serializationObject: any = {};
 
 
-            serializationObject.name = this.name;
-            serializationObject.id = this.id;
+            ParticleSystem._Serialize(serializationObject, this);
 
 
-            // Emitter
-            if ((<AbstractMesh>this.emitter).position) {
-                var emitterMesh = (<AbstractMesh>this.emitter);
-                serializationObject.emitterId = emitterMesh.id;
-            } else {
-                var emitterPosition = (<Vector3>this.emitter);
-                serializationObject.emitter = emitterPosition.asArray();
-            }
-
-            serializationObject.capacity = this.getCapacity();
-
-            if (this.particleTexture) {
-                serializationObject.textureName = this.particleTexture.name;
-            }
-
-            // Animations
-            Animation.AppendSerializedAnimations(this, serializationObject);
-
-            // Particle system
-            serializationObject.minAngularSpeed = this.minAngularSpeed;
-            serializationObject.maxAngularSpeed = this.maxAngularSpeed;
-            serializationObject.minSize = this.minSize;
-            serializationObject.maxSize = this.maxSize;
-            serializationObject.minScaleX = this.minScaleX;
-            serializationObject.maxScaleX = this.maxScaleX;
-            serializationObject.minScaleY = this.minScaleY;
-            serializationObject.maxScaleY = this.maxScaleY;            
-            serializationObject.minEmitPower = this.minEmitPower;
-            serializationObject.maxEmitPower = this.maxEmitPower;
-            serializationObject.minLifeTime = this.minLifeTime;
-            serializationObject.maxLifeTime = this.maxLifeTime;
-            serializationObject.emitRate = this.emitRate;
-            serializationObject.minEmitBox = this.minEmitBox.asArray();
-            serializationObject.maxEmitBox = this.maxEmitBox.asArray();
-            serializationObject.gravity = this.gravity.asArray();
-            serializationObject.direction1 = this.direction1.asArray();
-            serializationObject.direction2 = this.direction2.asArray();
-            serializationObject.color1 = this.color1.asArray();
-            serializationObject.color2 = this.color2.asArray();
-            serializationObject.colorDead = this.colorDead.asArray();
-            serializationObject.updateSpeed = this.updateSpeed;
-            serializationObject.targetStopDuration = this.targetStopDuration;
             serializationObject.textureMask = this.textureMask.asArray();
             serializationObject.textureMask = this.textureMask.asArray();
-            serializationObject.blendMode = this.blendMode;
             serializationObject.customShader = this.customShader;
             serializationObject.customShader = this.customShader;
             serializationObject.preventAutoStart = this.preventAutoStart;
             serializationObject.preventAutoStart = this.preventAutoStart;
 
 
@@ -1303,42 +1375,95 @@
 
 
             serializationObject.isAnimationSheetEnabled = this._isAnimationSheetEnabled;
             serializationObject.isAnimationSheetEnabled = this._isAnimationSheetEnabled;
 
 
-            // Emitter
-            if (this.particleEmitterType) {
-                serializationObject.particleEmitterType = this.particleEmitterType.serialize();
-            }            
-
             return serializationObject;
             return serializationObject;
         }
         }
 
 
-        /**
-         * Parses a JSON object to create a particle system.
-         * @param parsedParticleSystem The JSON object to parse
-         * @param scene The scene to create the particle system in
-         * @param rootUrl The root url to use to load external dependencies like texture
-         * @returns the Parsed particle system
-         */
-        public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): ParticleSystem {
-            var name = parsedParticleSystem.name;
-            var custom: Nullable<Effect> = null;
-            var program: any = null;
-            if (parsedParticleSystem.customShader) {
-                program = parsedParticleSystem.customShader;
-                var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
-                custom = scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
+        /** @hidden */
+        public static _Serialize(serializationObject: any, particleSystem: IParticleSystem) {
+            serializationObject.name = particleSystem.name;
+            serializationObject.id = particleSystem.id;
+
+            serializationObject.capacity = particleSystem.getCapacity();
+
+            // Emitter
+            if ((<AbstractMesh>particleSystem.emitter).position) {
+                var emitterMesh = (<AbstractMesh>particleSystem.emitter);
+                serializationObject.emitterId = emitterMesh.id;
+            } else {
+                var emitterPosition = (<Vector3>particleSystem.emitter);
+                serializationObject.emitter = emitterPosition.asArray();
             }
             }
-            var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, scene, custom, parsedParticleSystem.isAnimationSheetEnabled);
-            particleSystem.customShader = program;
 
 
-            if (parsedParticleSystem.id) {
-                particleSystem.id = parsedParticleSystem.id;
+            // Emitter
+            if (particleSystem.particleEmitterType) {
+                serializationObject.particleEmitterType = particleSystem.particleEmitterType.serialize();
+            }           
+            
+            if (particleSystem.particleTexture) {
+                serializationObject.textureName = particleSystem.particleTexture.name;
             }
             }
+            
+            // Animations
+            Animation.AppendSerializedAnimations(particleSystem, serializationObject);
 
 
-            // Auto start
-            if (parsedParticleSystem.preventAutoStart) {
-                particleSystem.preventAutoStart = parsedParticleSystem.preventAutoStart;
+            // Particle system
+            serializationObject.minAngularSpeed = particleSystem.minAngularSpeed;
+            serializationObject.maxAngularSpeed = particleSystem.maxAngularSpeed;
+            serializationObject.minSize = particleSystem.minSize;
+            serializationObject.maxSize = particleSystem.maxSize;
+            serializationObject.minScaleX = particleSystem.minScaleX;
+            serializationObject.maxScaleX = particleSystem.maxScaleX;
+            serializationObject.minScaleY = particleSystem.minScaleY;
+            serializationObject.maxScaleY = particleSystem.maxScaleY;            
+            serializationObject.minEmitPower = particleSystem.minEmitPower;
+            serializationObject.maxEmitPower = particleSystem.maxEmitPower;
+            serializationObject.minLifeTime = particleSystem.minLifeTime;
+            serializationObject.maxLifeTime = particleSystem.maxLifeTime;
+            serializationObject.emitRate = particleSystem.emitRate;
+            serializationObject.gravity = particleSystem.gravity.asArray();
+            serializationObject.color1 = particleSystem.color1.asArray();
+            serializationObject.color2 = particleSystem.color2.asArray();
+            serializationObject.colorDead = particleSystem.colorDead.asArray();
+            serializationObject.updateSpeed = particleSystem.updateSpeed;
+            serializationObject.targetStopDuration = particleSystem.targetStopDuration;
+            serializationObject.blendMode = particleSystem.blendMode;
+            serializationObject.preWarmCycles = particleSystem.preWarmCycles;
+            serializationObject.preWarmStepOffset = particleSystem.preWarmStepOffset;
+            serializationObject.minInitialRotation = particleSystem.minInitialRotation;
+            serializationObject.maxInitialRotation = particleSystem.maxInitialRotation;
+
+            let colorGradients = particleSystem.getColorGradients();
+            if (colorGradients) {
+                serializationObject.colorGradients = [];
+                for (var colorGradient of colorGradients) {
+                    var serializedGradient: any = {
+                        gradient: colorGradient.gradient,
+                        color1: colorGradient.color1.asArray()
+                    };
+
+                    if (colorGradient.color2) {
+                        serializedGradient.color2 = colorGradient.color2.asArray();
+                    }
+
+                    serializationObject.colorGradients.push(serializedGradient);
+                }
             }
             }
 
 
+            let sizeGradients = particleSystem.getSizeGradients();
+            if (sizeGradients) {
+                serializationObject.sizeGradients = [];
+                for (var sizeGradient of sizeGradients) {
+                    serializationObject.sizeGradients.push({
+                        gradient: sizeGradient.gradient,
+                        factor: sizeGradient.factor
+                    })
+                }
+            }            
+
+        }
+
+        /** @hidden */
+        public static _Parse(parsedParticleSystem: any, particleSystem: IParticleSystem, scene: Scene, rootUrl: string) {
             // Texture
             // Texture
             if (parsedParticleSystem.textureName) {
             if (parsedParticleSystem.textureName) {
                 particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene);
                 particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene);
@@ -1377,24 +1502,105 @@
                 particleSystem.maxScaleY = parsedParticleSystem.maxScaleY;                
                 particleSystem.maxScaleY = parsedParticleSystem.maxScaleY;                
             }
             }
 
 
+            if (parsedParticleSystem.preWarmCycles !== undefined) {
+                particleSystem.preWarmCycles = parsedParticleSystem.preWarmCycles;
+                particleSystem.preWarmStepOffset = parsedParticleSystem.preWarmStepOffset;
+            }   
+
+            if (parsedParticleSystem.minInitialRotation !== undefined) {
+                particleSystem.minInitialRotation = parsedParticleSystem.minInitialRotation;
+                particleSystem.maxInitialRotation = parsedParticleSystem.maxInitialRotation;
+            }
+
             particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
             particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
             particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
             particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
             particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
             particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
             particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
             particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
             particleSystem.emitRate = parsedParticleSystem.emitRate;
             particleSystem.emitRate = parsedParticleSystem.emitRate;
-            particleSystem.minEmitBox = Vector3.FromArray(parsedParticleSystem.minEmitBox);
-            particleSystem.maxEmitBox = Vector3.FromArray(parsedParticleSystem.maxEmitBox);
             particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
             particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
-            particleSystem.direction1 = Vector3.FromArray(parsedParticleSystem.direction1);
-            particleSystem.direction2 = Vector3.FromArray(parsedParticleSystem.direction2);
             particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
             particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
             particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
             particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
             particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
             particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
             particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
             particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
             particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
             particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
-            particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);
             particleSystem.blendMode = parsedParticleSystem.blendMode;
             particleSystem.blendMode = parsedParticleSystem.blendMode;
 
 
+
+            if (parsedParticleSystem.colorGradients) {
+                for (var colorGradient of parsedParticleSystem.colorGradients) {
+                    particleSystem.addColorGradient(colorGradient.gradient, Color4.FromArray(colorGradient.color1), colorGradient.color2 ? Color4.FromArray(colorGradient.color2) : undefined);
+                }
+            }
+
+            if (parsedParticleSystem.sizeGradients) {
+                for (var sizeGradient of parsedParticleSystem.sizeGradients) {
+                    particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor);
+                }
+            }       
+            
+            // Emitter
+            let emitterType: IParticleEmitterType;
+            if (parsedParticleSystem.particleEmitterType) {
+                switch (parsedParticleSystem.particleEmitterType.type) {
+                    case "SphereEmitter":
+                        emitterType = new SphereParticleEmitter();
+                        break;
+                    case "SphereDirectedParticleEmitter":
+                        emitterType = new SphereDirectedParticleEmitter();
+                        break;
+                    case "ConeEmitter":
+                        emitterType = new ConeParticleEmitter();
+                        break;
+                    case "BoxEmitter":
+                    default:
+                        emitterType = new BoxParticleEmitter();
+                        break;                                                
+                }
+
+                emitterType.parse(parsedParticleSystem.particleEmitterType);
+            } else {
+                emitterType = new BoxParticleEmitter();
+                emitterType.parse(parsedParticleSystem);
+            }
+            particleSystem.particleEmitterType = emitterType;
+        }
+
+        /**
+         * Parses a JSON object to create a particle system.
+         * @param parsedParticleSystem The JSON object to parse
+         * @param scene The scene to create the particle system in
+         * @param rootUrl The root url to use to load external dependencies like texture
+         * @returns the Parsed particle system
+         */
+        public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): ParticleSystem {
+            var name = parsedParticleSystem.name;
+            var custom: Nullable<Effect> = null;
+            var program: any = null;
+            if (parsedParticleSystem.customShader) {
+                program = parsedParticleSystem.customShader;
+                var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
+                custom = scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
+            }
+            var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, scene, custom, parsedParticleSystem.isAnimationSheetEnabled);
+            particleSystem.customShader = program;
+
+            if (parsedParticleSystem.id) {
+                particleSystem.id = parsedParticleSystem.id;
+            }
+
+            // Auto start
+            if (parsedParticleSystem.preventAutoStart) {
+                particleSystem.preventAutoStart = parsedParticleSystem.preventAutoStart;
+            }
+
+            particleSystem.minEmitBox = Vector3.FromArray(parsedParticleSystem.minEmitBox);
+            particleSystem.maxEmitBox = Vector3.FromArray(parsedParticleSystem.maxEmitBox);
+            particleSystem.direction1 = Vector3.FromArray(parsedParticleSystem.direction1);
+            particleSystem.direction2 = Vector3.FromArray(parsedParticleSystem.direction2);
+
+            ParticleSystem._Parse(parsedParticleSystem, particleSystem, scene, rootUrl);
+
+            particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);
             particleSystem.startSpriteCellID = parsedParticleSystem.startSpriteCellID;
             particleSystem.startSpriteCellID = parsedParticleSystem.startSpriteCellID;
             particleSystem.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
             particleSystem.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
             particleSystem.spriteCellLoop = parsedParticleSystem.spriteCellLoop;
             particleSystem.spriteCellLoop = parsedParticleSystem.spriteCellLoop;

+ 12 - 7
src/Shaders/ShadersInclude/reflectionFunction.fx

@@ -19,10 +19,12 @@ vec3 parallaxCorrectNormal( vec3 vertexPos, vec3 origVec, vec3 cubeSize, vec3 cu
 vec3 computeReflectionCoords(vec4 worldPos, vec3 worldNormal)
 vec3 computeReflectionCoords(vec4 worldPos, vec3 worldNormal)
 {
 {
 #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED)
 #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED)
-	vec3 direction = vDirectionW;
-
-	float t = clamp(direction.y * -0.5 + 0.5, 0., 1.0);
-	float s = atan(direction.z, direction.x) * RECIPROCAL_PI2 + 0.5;
+	vec3 direction = normalize(vDirectionW);
+	float lon = atan(direction.z, direction.x);
+	float lat = acos(direction.y);
+	vec2 sphereCoords = vec2(lon, lat) * RECIPROCAL_PI2 * 2.0;
+	float s = sphereCoords.x * 0.5 + 0.5;
+	float t = sphereCoords.y;	
 
 
  	#ifdef REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED
  	#ifdef REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED
 		return vec3(1.0 - s, t, 0);
 		return vec3(1.0 - s, t, 0);
@@ -34,9 +36,12 @@ vec3 computeReflectionCoords(vec4 worldPos, vec3 worldNormal)
 #ifdef REFLECTIONMAP_EQUIRECTANGULAR
 #ifdef REFLECTIONMAP_EQUIRECTANGULAR
 
 
 	vec3 cameraToVertex = normalize(worldPos.xyz - vEyePosition.xyz);
 	vec3 cameraToVertex = normalize(worldPos.xyz - vEyePosition.xyz);
-	vec3 r = reflect(cameraToVertex, worldNormal);
-	float t = clamp(r.y * -0.5 + 0.5, 0., 1.0);
-	float s = atan(r.z, r.x) * RECIPROCAL_PI2 + 0.5;
+	vec3 r = normalize(reflect(cameraToVertex, worldNormal));
+	float lon = atan(r.z, r.x);
+	float lat = acos(r.y);
+	vec2 sphereCoords = vec2(lon, lat) * RECIPROCAL_PI2 * 2.0;
+	float s = sphereCoords.x * 0.5 + 0.5;
+	float t = sphereCoords.y;		
 
 
 	return vec3(s, t, 0);
 	return vec3(s, t, 0);
 #endif
 #endif

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

@@ -1,6 +1,6 @@
 #version 300 es
 #version 300 es
 
 
-uniform vec4 colorDead;
+
 uniform mat4 view;
 uniform mat4 view;
 uniform mat4 projection;
 uniform mat4 projection;
 
 
@@ -9,7 +9,6 @@ in vec3 position;
 in float age;
 in float age;
 in float life;
 in float life;
 in vec3 size;
 in vec3 size;
-in vec4 color;
 #ifndef BILLBOARD
 #ifndef BILLBOARD
 in vec3 initialDirection;
 in vec3 initialDirection;
 #endif
 #endif
@@ -26,11 +25,22 @@ uniform mat4 invView;
 out float fClipDistance;
 out float fClipDistance;
 #endif
 #endif
 
 
+#ifdef COLORGRADIENTS
+uniform sampler2D colorGradientSampler;
+#else
+uniform vec4 colorDead;
+in vec4 color;
+#endif
 
 
 void main() {
 void main() {
   vUV = uv;
   vUV = uv;
   float ratio = age / life;
   float ratio = age / life;
-  vColor = color * vec4(1.0 - ratio) + colorDead * vec4(ratio);
+#ifdef COLORGRADIENTS
+	vColor = texture(colorGradientSampler, vec2(ratio, 0));
+#else
+	vColor = color * vec4(1.0 - ratio) + colorDead * vec4(ratio);
+#endif
+  
 
 
   vec2 cornerPos = offset * size.yz * size.x;
   vec2 cornerPos = offset * size.yz * size.x;
 
 

+ 46 - 16
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -5,17 +5,19 @@
 uniform float currentCount;
 uniform float currentCount;
 uniform float timeDelta;
 uniform float timeDelta;
 uniform float stopFactor;
 uniform float stopFactor;
-uniform vec3 generalRandoms;
 uniform mat4 emitterWM;
 uniform mat4 emitterWM;
 uniform vec2 lifeTime;
 uniform vec2 lifeTime;
 uniform vec2 emitPower;
 uniform vec2 emitPower;
 uniform vec2 sizeRange;
 uniform vec2 sizeRange;
 uniform vec4 scaleRange;
 uniform vec4 scaleRange;
+#ifndef COLORGRADIENTS
 uniform vec4 color1;
 uniform vec4 color1;
 uniform vec4 color2;
 uniform vec4 color2;
+#endif
 uniform vec3 gravity;
 uniform vec3 gravity;
 uniform sampler2D randomSampler;
 uniform sampler2D randomSampler;
-uniform vec2 angleRange;
+uniform sampler2D randomSampler2;
+uniform vec4 angleRange;
 
 
 #ifdef BOXEMITTER
 #ifdef BOXEMITTER
 uniform vec3 direction1;
 uniform vec3 direction1;
@@ -46,9 +48,11 @@ uniform float directionRandomizer;
 in vec3 position;
 in vec3 position;
 in float age;
 in float age;
 in float life;
 in float life;
-in float seed;
+in vec4 seed;
 in vec3 size;
 in vec3 size;
+#ifndef COLORGRADIENTS
 in vec4 color;
 in vec4 color;
+#endif
 in vec3 direction;
 in vec3 direction;
 #ifndef BILLBOARD
 #ifndef BILLBOARD
 in vec3 initialDirection;
 in vec3 initialDirection;
@@ -59,24 +63,33 @@ in vec2 angle;
 out vec3 outPosition;
 out vec3 outPosition;
 out float outAge;
 out float outAge;
 out float outLife;
 out float outLife;
-out float outSeed;
+out vec4 outSeed;
 out vec3 outSize;
 out vec3 outSize;
+#ifdef SIZEGRADIENTS
+out vec3 outInitialSize;
+#endif
+#ifndef COLORGRADIENTS
 out vec4 outColor;
 out vec4 outColor;
+#endif
 out vec3 outDirection;
 out vec3 outDirection;
 #ifndef BILLBOARD
 #ifndef BILLBOARD
 out vec3 outInitialDirection;
 out vec3 outInitialDirection;
 #endif
 #endif
 out vec2 outAngle;
 out vec2 outAngle;
 
 
+#ifdef SIZEGRADIENTS
+uniform sampler2D sizeGradientSampler;
+in vec3 initialSize;
+#endif 
+
 vec3 getRandomVec3(float offset) {
 vec3 getRandomVec3(float offset) {
-  return texture(randomSampler, vec2(float(gl_VertexID) * offset / currentCount, 0)).rgb;
+  return texture(randomSampler2, vec2(float(gl_VertexID) * offset / currentCount, 0)).rgb;
 }
 }
 
 
 vec4 getRandomVec4(float offset) {
 vec4 getRandomVec4(float offset) {
   return texture(randomSampler, vec2(float(gl_VertexID) * offset / currentCount, 0));
   return texture(randomSampler, vec2(float(gl_VertexID) * offset / currentCount, 0));
 }
 }
 
 
-
 void main() {
 void main() {
   if (age >= life) {
   if (age >= life) {
     if (stopFactor == 0.) {
     if (stopFactor == 0.) {
@@ -84,7 +97,9 @@ void main() {
       outAge = life;
       outAge = life;
       outLife = life;
       outLife = life;
       outSeed = seed;
       outSeed = seed;
+#ifndef COLORGRADIENTS      
       outColor = vec4(0.,0.,0.,0.);
       outColor = vec4(0.,0.,0.,0.);
+#endif
       outSize = vec3(0., 0., 0.);
       outSize = vec3(0., 0., 0.);
 #ifndef BILLBOARD        
 #ifndef BILLBOARD        
       outInitialDirection = initialDirection;
       outInitialDirection = initialDirection;
@@ -97,7 +112,7 @@ void main() {
     vec3 direction;
     vec3 direction;
 
 
     // Let's get some random values
     // Let's get some random values
-    vec4 randoms = getRandomVec4(generalRandoms.x);
+    vec4 randoms = getRandomVec4(seed.x);
 
 
     // Age and life
     // Age and life
     outAge = 0.0;
     outAge = 0.0;
@@ -109,26 +124,32 @@ void main() {
     // Size
     // Size
     outSize.x = sizeRange.x + (sizeRange.y - sizeRange.x) * randoms.g;
     outSize.x = sizeRange.x + (sizeRange.y - sizeRange.x) * randoms.g;
     outSize.y = scaleRange.x + (scaleRange.y - scaleRange.x) * randoms.b;
     outSize.y = scaleRange.x + (scaleRange.y - scaleRange.x) * randoms.b;
-    outSize.z = scaleRange.z + (scaleRange.w - scaleRange.z) * randoms.a;
+    outSize.z = scaleRange.z + (scaleRange.w - scaleRange.z) * randoms.a; 
 
 
+#ifdef SIZEGRADIENTS
+    outInitialSize = outSize;
+#endif    
+
+#ifndef COLORGRADIENTS
     // Color
     // Color
     outColor = color1 + (color2 - color1) * randoms.b;
     outColor = color1 + (color2 - color1) * randoms.b;
+#endif
 
 
     // Angular speed
     // Angular speed
     outAngle.y = angleRange.x + (angleRange.y - angleRange.x) * randoms.a;
     outAngle.y = angleRange.x + (angleRange.y - angleRange.x) * randoms.a;
-    outAngle.x = 0.;
+    outAngle.x = angleRange.z + (angleRange.w - angleRange.z) * randoms.r;
 
 
     // Position / Direction (based on emitter type)
     // Position / Direction (based on emitter type)
 #ifdef BOXEMITTER
 #ifdef BOXEMITTER
-    vec3 randoms2 = getRandomVec3(generalRandoms.y);
-    vec3 randoms3 = getRandomVec3(generalRandoms.z);
+    vec3 randoms2 = getRandomVec3(seed.y);
+    vec3 randoms3 = getRandomVec3(seed.z);
 
 
     position = minEmitBox + (maxEmitBox - minEmitBox) * randoms2;
     position = minEmitBox + (maxEmitBox - minEmitBox) * randoms2;
 
 
     direction = direction1 + (direction2 - direction1) * randoms3;
     direction = direction1 + (direction2 - direction1) * randoms3;
 #elif defined(SPHEREEMITTER)
 #elif defined(SPHEREEMITTER)
-    vec3 randoms2 = getRandomVec3(generalRandoms.y);
-    vec3 randoms3 = getRandomVec3(generalRandoms.z);
+    vec3 randoms2 = getRandomVec3(seed.y);
+    vec3 randoms3 = getRandomVec3(seed.z);
 
 
     // Position on the sphere surface
     // Position on the sphere surface
     float phi = 2.0 * PI * randoms2.x;
     float phi = 2.0 * PI * randoms2.x;
@@ -146,7 +167,7 @@ void main() {
       direction = position + directionRandomizer * randoms3;
       direction = position + directionRandomizer * randoms3;
     #endif
     #endif
 #elif defined(CONEEMITTER)
 #elif defined(CONEEMITTER)
-    vec3 randoms2 = getRandomVec3(generalRandoms.y);
+    vec3 randoms2 = getRandomVec3(seed.y);
 
 
     float s = 2.0 * PI * randoms2.x;
     float s = 2.0 * PI * randoms2.x;
     float h = randoms2.y;
     float h = randoms2.y;
@@ -166,7 +187,7 @@ void main() {
     if (coneAngle == 0.) {
     if (coneAngle == 0.) {
         direction = vec3(0., 1.0, 0.);
         direction = vec3(0., 1.0, 0.);
     } else {
     } else {
-        vec3 randoms3 = getRandomVec3(generalRandoms.z);
+        vec3 randoms3 = getRandomVec3(seed.z);
         direction = position + directionRandomizer * randoms3;
         direction = position + directionRandomizer * randoms3;
     }
     }
 #else    
 #else    
@@ -174,7 +195,7 @@ void main() {
     position = vec3(0., 0., 0.);
     position = vec3(0., 0., 0.);
 
 
     // Spread in all directions
     // Spread in all directions
-    direction = 2.0 * (getRandomVec3(seed) - vec3(0.5, 0.5, 0.5));
+    direction = 2.0 * (getRandomVec3(seed.w) - vec3(0.5, 0.5, 0.5));
 #endif
 #endif
 
 
     float power = emitPower.x + (emitPower.y - emitPower.x) * randoms.a;
     float power = emitPower.x + (emitPower.y - emitPower.x) * randoms.a;
@@ -191,8 +212,17 @@ void main() {
     outAge = age + timeDelta;
     outAge = age + timeDelta;
     outLife = life;
     outLife = life;
     outSeed = seed;
     outSeed = seed;
+#ifndef COLORGRADIENTS    
     outColor = color;
     outColor = color;
+#endif
+
+#ifdef SIZEGRADIENTS
+    outInitialSize = initialSize;
+	outSize = initialSize * texture(sizeGradientSampler, vec2(age / life, 0)).r;
+#else
     outSize = size;
     outSize = size;
+#endif 
+
 #ifndef BILLBOARD    
 #ifndef BILLBOARD    
     outInitialDirection = initialDirection;
     outInitialDirection = initialDirection;
 #endif
 #endif

+ 3 - 3
src/Tools/babylon.khronosTextureContainer.ts

@@ -125,13 +125,13 @@ module BABYLON {
             var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
             var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
             for (var level = 0; level < mipmapCount; level++) {
             for (var level = 0; level < mipmapCount; level++) {
                 var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
                 var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
-
+                dataOffset += 4;//image data starts from next multiple of 4 offset. Each face refers to same imagesize field above.
                 for (var face = 0; face < this.numberOfFaces; face++) {
                 for (var face = 0; face < this.numberOfFaces; face++) {
                     var sampler = this.numberOfFaces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
                     var sampler = this.numberOfFaces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
-                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset + 4, imageSize);
+                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset, imageSize);
                     gl.compressedTexImage2D(sampler, level, this.glInternalFormat, width, height, 0, byteArray);
                     gl.compressedTexImage2D(sampler, level, this.glInternalFormat, width, height, 0, byteArray);
 
 
-                    dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field
+                    dataOffset += imageSize; // add size of the image for the next face/mipmap
                     dataOffset += 3 - ((imageSize + 3) % 4); // add padding for odd sized image
                     dataOffset += 3 - ((imageSize + 3) % 4); // add padding for odd sized image
                 }
                 }
                 width = Math.max(1.0, width * 0.5);
                 width = Math.max(1.0, width * 0.5);

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

@@ -3,6 +3,57 @@
         animations: Array<Animation>;
         animations: Array<Animation>;
     }
     }
 
 
+    
+    /** Interface used by value gradients (color, factor, ...) */
+    export interface IValueGradient {
+        /**
+         * Gets or sets the gradient value (between 0 and 1)
+         */        
+        gradient: number;
+    }
+
+    /** Class used to store color gradient */
+    export class ColorGradient implements IValueGradient {
+        /**
+         * Gets or sets the gradient value (between 0 and 1)
+         */
+        public gradient: number;
+        /**
+         * Gets or sets first associated color
+         */
+        public color1: Color4;
+        /**
+         * Gets or sets second associated color
+         */
+        public color2?: Color4;
+
+        /** 
+         * Will get a color picked randomly between color1 and color2.
+         * If color2 is undefined then color1 will be used
+         * @param result defines the target Color4 to store the result in
+         */
+        public getColorToRef(result: Color4) {
+            if (!this.color2) {
+                result.copyFrom(this.color1);
+                return;
+            }
+
+            Color4.LerpToRef(this.color1, this.color2, Math.random(), result);
+        }
+    }
+
+    /** Class used to store factor gradient */
+    export class FactorGradient implements IValueGradient {
+        /**
+         * Gets or sets the gradient value (between 0 and 1)
+         */
+        public gradient: number;
+        /**
+         * Gets or sets associated factor
+         */        
+        public factor: number;
+    }  
+
     // See https://stackoverflow.com/questions/12915412/how-do-i-extend-a-host-object-e-g-error-in-typescript
     // See https://stackoverflow.com/questions/12915412/how-do-i-extend-a-host-object-e-g-error-in-typescript
     // and https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
     // and https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
     export class LoadFileError extends Error {
     export class LoadFileError extends Error {
@@ -1607,6 +1658,25 @@
                 }, delay);
                 }, delay);
             });
             });
         }
         }
+
+
+        /**
+         * Gets the current gradient from an array of IValueGradient
+         * @param ratio defines the current ratio to get
+         * @param gradients defines the array of IValueGradient
+         * @param updateFunc defines the callback function used to get the final value from the selected gradients
+         */
+        public static GetCurrentGradient(ratio: number, gradients: IValueGradient[], updateFunc: (current: IValueGradient, next: IValueGradient, scale: number) => void) {
+            for (var gradientIndex = 0; gradientIndex < gradients.length - 1; gradientIndex++) {
+                let currentGradient = gradients[gradientIndex];
+                let nextGradient = gradients[gradientIndex + 1];
+
+                if (ratio >= currentGradient.gradient && ratio <= nextGradient.gradient) {
+                    let scale =  (ratio - currentGradient.gradient) / (nextGradient.gradient - currentGradient.gradient);
+                    updateFunc(currentGradient, nextGradient, scale);
+               }
+            }
+        }
     }
     }
 
 
     /**
     /**

+ 4 - 56
tests/unit/babylon/serializers/babylon.glTFSerializer.tests.ts

@@ -35,29 +35,6 @@ describe('Babylon glTF Serializer', () => {
      * This tests the glTF serializer help functions 
      * This tests the glTF serializer help functions 
      */
      */
     describe('#GLTF', () => {
     describe('#GLTF', () => {
-        // it('should get alpha mode from Babylon metallic roughness', () => {
-        //     let alphaMode: string;
-
-        //     const scene = new BABYLON.Scene(subject);
-        //     const babylonMaterial = new BABYLON.PBRMetallicRoughnessMaterial("metallicroughness", scene);
-        //     babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_OPAQUE;
-
-        //     alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
-        //     alphaMode.should.be.equal('OPAQUE');
-
-        //     babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
-        //     alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
-        //     alphaMode.should.be.equal('BLEND');
-
-        //     babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND;
-        //     alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
-        //     alphaMode.should.be.equal('BLEND');
-
-        //     babylonMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHATEST;
-        //     alphaMode = BABYLON.GLTF2._GLTFMaterial._GetAlphaMode(babylonMaterial);
-        //     alphaMode.should.be.equal('MASK');
-        // });
-
         it('should convert Babylon standard material to metallic roughness', () => {
         it('should convert Babylon standard material to metallic roughness', () => {
             const scene = new BABYLON.Scene(subject);
             const scene = new BABYLON.Scene(subject);
             const babylonStandardMaterial = new BABYLON.StandardMaterial("specGloss", scene);
             const babylonStandardMaterial = new BABYLON.StandardMaterial("specGloss", scene);
@@ -65,8 +42,9 @@ describe('Babylon glTF Serializer', () => {
             babylonStandardMaterial.specularColor = BABYLON.Color3.Black();
             babylonStandardMaterial.specularColor = BABYLON.Color3.Black();
             babylonStandardMaterial.specularPower = 64;
             babylonStandardMaterial.specularPower = 64;
             babylonStandardMaterial.alpha = 1;
             babylonStandardMaterial.alpha = 1;
+            const materialExporter = new BABYLON.GLTF2._GLTFMaterialExporter(new BABYLON.GLTF2._Exporter(scene));
 
 
-            const metalRough = BABYLON.GLTF2._GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+            const metalRough = materialExporter._convertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
 
 
             metalRough.baseColorFactor.should.deep.equal([0.5, 0.5, 0.5, 1]);
             metalRough.baseColorFactor.should.deep.equal([0.5, 0.5, 0.5, 1]);
 
 
@@ -74,12 +52,10 @@ describe('Babylon glTF Serializer', () => {
 
 
             metalRough.roughnessFactor.should.be.approximately(0.328809, 1e-6);
             metalRough.roughnessFactor.should.be.approximately(0.328809, 1e-6);
         });
         });
-
         it('should solve for metallic', () => {
         it('should solve for metallic', () => {
-            BABYLON.GLTF2._GLTFMaterial._SolveMetallic(1.0, 0.0, 1.0).should.be.equal(0);
-            BABYLON.GLTF2._GLTFMaterial._SolveMetallic(0.0, 1.0, 1.0).should.be.approximately(1, 1e-6);
+            BABYLON.GLTF2._GLTFMaterialExporter._SolveMetallic(1.0, 0.0, 1.0).should.be.equal(0);
+            BABYLON.GLTF2._GLTFMaterialExporter._SolveMetallic(0.0, 1.0, 1.0).should.be.approximately(1, 1e-6);
         });
         });
-
         it('should serialize empty Babylon scene to glTF with only asset property', () => {
         it('should serialize empty Babylon scene to glTF with only asset property', () => {
             const scene = new BABYLON.Scene(subject);
             const scene = new BABYLON.Scene(subject);
 
 
@@ -92,7 +68,6 @@ describe('Babylon glTF Serializer', () => {
                 jsonData.asset.generator.should.be.equal("BabylonJS");
                 jsonData.asset.generator.should.be.equal("BabylonJS");
             });
             });
         });
         });
-
         it('should serialize sphere geometry in scene to glTF', () => {
         it('should serialize sphere geometry in scene to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             const scene = new BABYLON.Scene(subject);
             BABYLON.Mesh.CreateSphere('sphere', 16, 2, scene);
             BABYLON.Mesh.CreateSphere('sphere', 16, 2, scene);
@@ -125,33 +100,6 @@ describe('Babylon glTF Serializer', () => {
                     jsonData.scene.should.be.equal(0);
                     jsonData.scene.should.be.equal(0);
                 });
                 });
         });
         });
-
-        // it('should serialize alpha mode and cutoff', () => {
-        //     const scene = new BABYLON.Scene(subject);
-
-        //     const plane = BABYLON.Mesh.CreatePlane('plane', 120, scene);
-        //     const babylonPBRMetalRoughMaterial = new BABYLON.PBRMetallicRoughnessMaterial('metalRoughMat', scene);
-        //     babylonPBRMetalRoughMaterial.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHATEST;
-        //     const alphaCutoff = 0.8;
-        //     babylonPBRMetalRoughMaterial.alphaCutOff = alphaCutoff;
-
-        //     plane.material = babylonPBRMetalRoughMaterial;
-
-
-        //     return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
-        //         const jsonString = glTFData.glTFFiles['test.gltf'] as string;
-        //         const jsonData = JSON.parse(jsonString);
-
-        //         // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials
-        //         Object.keys(jsonData).length.should.be.equal(9);
-
-        //         jsonData.materials.length.should.be.equal(2);
-
-        //         jsonData.materials[0].alphaMode.should.be.equal('MASK');
-
-        //         jsonData.materials[0].alphaCutoff.should.be.equal(alphaCutoff);
-        //     });
-        // });
         it('should serialize single component translation animation to glTF', () => {
         it('should serialize single component translation animation to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             const scene = new BABYLON.Scene(subject);
             const box = BABYLON.Mesh.CreateBox('box', 1, scene);
             const box = BABYLON.Mesh.CreateBox('box', 1, scene);

+ 1 - 1
tests/unit/babylon/src/Animations/babylon.animationGroup.tests.ts

@@ -46,7 +46,7 @@ describe('Babylon Animation Group', function () {
 
 
             const length = 10;
             const length = 10;
             for (let i = 0; i < length; i++) {
             for (let i = 0; i < length; i++) {
-                const animation = new BABYLON.Animation(`animation${i}`, "", 1, BABYLON.Animation.ANIMATIONTYPE_VECTOR3);
+                const animation = new BABYLON.Animation(`animation${i}`, "position", 1, BABYLON.Animation.ANIMATIONTYPE_VECTOR3);
                 animation.setKeys([
                 animation.setKeys([
                     {
                     {
                         frame: 0,
                         frame: 0,