Parcourir la source

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

Raanan Weber il y a 7 ans
Parent
commit
dabdec916c
76 fichiers modifiés avec 29323 ajouts et 24562 suppressions
  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,
         "assets": true
     },
+    "editor.tabSize": 4,
     "typescript.tsdk": "./Tools/Gulp/node_modules/typescript/lib"
 }

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


+ 6 - 8
Playground/index.html

@@ -42,21 +42,19 @@
         <!-- Babylon.js -->
         <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/materialsLibrary/babylonjs.materials.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/loaders/babylonjs.loaders.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://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" />
     </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.glTFExporter.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.glTFUtilities.ts"
                 ],
-                "shaderFiles": [
-                    "../../serializers/src/glTF/2.0/shaders/setAlphaToOne.fragment.fx"
-                ],
                 "output": "babylon.glTF2Serializer.js"
             }
         ],

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

@@ -139,6 +139,26 @@
         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 {
         font-size: 16px;
         content: "\EF4E";

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

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

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

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

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


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


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


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


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


Fichier diff supprimé car celui-ci est trop grand
+ 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 _maximum;
         private _value;
+        private _isVertical;
         private _background;
         private _borderColor;
         private _barOffset;
@@ -1220,6 +1221,8 @@ declare module BABYLON.GUI {
         maximum: number;
         /** Gets or sets current value */
         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 */
         isThumbCircle: boolean;
         /** 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);
         protected _getTypeName(): string;
+        protected _getThumbThickness(type: string, backgroundLength: number): number;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         private _pointerIsDown;
         private _updateValueFromPointer(x, y);

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

@@ -3738,10 +3738,11 @@ var BABYLON;
             function Slider(name) {
                 var _this = _super.call(this, name) || this;
                 _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._maximum = 100;
                 _this._value = 50;
+                _this._isVertical = false;
                 _this._background = "black";
                 _this._borderColor = "white";
                 _this._barOffset = new GUI.ValueAndUnit(5, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
@@ -3881,6 +3882,21 @@ var BABYLON;
                 enumerable: 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", {
                 /** Gets or sets a boolean indicating if the thumb should be round or square */
                 get: function () {
@@ -3914,24 +3930,80 @@ var BABYLON;
             Slider.prototype._getTypeName = function () {
                 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) {
                 context.save();
                 this._applyStates(context);
                 if (this._processMeasures(parentMeasure, context)) {
                     // 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 {
-                        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 {
-                        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) {
                         context.shadowColor = this.shadowColor;
@@ -3939,15 +4011,39 @@ var BABYLON;
                         context.shadowOffsetX = this.shadowOffsetX;
                         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;
-                    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);
+                        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) {
                         context.shadowBlur = 0;
@@ -3955,11 +4051,37 @@ var BABYLON;
                         context.shadowOffsetY = 0;
                     }
                     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);
+                        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) {
                         context.shadowColor = this.shadowColor;
@@ -3969,7 +4091,12 @@ var BABYLON;
                     }
                     if (this._isThumbCircle) {
                         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();
                         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                             context.shadowBlur = 0;
@@ -3980,14 +4107,24 @@ var BABYLON;
                         context.stroke();
                     }
                     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) {
                             context.shadowBlur = 0;
                             context.shadowOffsetX = 0;
                             context.shadowOffsetY = 0;
                         }
                         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();
@@ -3996,8 +4133,14 @@ var BABYLON;
                 if (this.rotation != 0) {
                     this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
                     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) {
                 if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {

Fichier diff supprimé car celui-ci est trop grand
+ 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 _maximum;
         private _value;
+        private _isVertical;
         private _background;
         private _borderColor;
         private _barOffset;
@@ -1225,6 +1226,8 @@ declare module BABYLON.GUI {
         maximum: number;
         /** Gets or sets current value */
         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 */
         isThumbCircle: boolean;
         /** 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);
         protected _getTypeName(): string;
+        protected _getThumbThickness(type: string, backgroundLength: number): number;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         private _pointerIsDown;
         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);
     }
-    class OBJFileLoader implements ISceneLoaderPlugin {
+    class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
         static INVERT_Y: boolean;
         name: string;
@@ -57,9 +57,14 @@ declare module BABYLON {
          * @private
          */
         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.
          * 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.
             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
-            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
-            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.
@@ -283,6 +287,7 @@ var BABYLON;
          * @private
          */
         OBJFileLoader.prototype._parseSolid = function (meshesNames, scene, data, rootUrl) {
+            var _this = this;
             var positions = []; //values for the positions of vertices
             var normals = []; //Values for the normals
             var uvs = []; //Values for the textures
@@ -752,42 +757,53 @@ var BABYLON;
                 //Push the mesh into an array
                 babylonMeshesArray.push(babylonMesh);
             }
+            var mtlPromises = [];
             //load the materials
             //Check if we have a file to load
             if (fileToLoad !== "") {
                 //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 babylonMeshesArray;
+            return Promise.all(mtlPromises).then(function () {
+                return babylonMeshesArray;
+            });
         };
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
         OBJFileLoader.INVERT_Y = false;

Fichier diff supprimé car celui-ci est trop grand
+ 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);
     }
-    class OBJFileLoader implements ISceneLoaderPlugin {
+    class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
         static INVERT_Y: boolean;
         name: string;
@@ -75,9 +75,14 @@ declare module BABYLON {
          * @private
          */
         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.
          * 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.
             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
-            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
-            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.
@@ -482,6 +486,7 @@ var BABYLON;
          * @private
          */
         OBJFileLoader.prototype._parseSolid = function (meshesNames, scene, data, rootUrl) {
+            var _this = this;
             var positions = []; //values for the positions of vertices
             var normals = []; //Values for the normals
             var uvs = []; //Values for the textures
@@ -951,42 +956,53 @@ var BABYLON;
                 //Push the mesh into an array
                 babylonMeshesArray.push(babylonMesh);
             }
+            var mtlPromises = [];
             //load the materials
             //Check if we have a file to load
             if (fileToLoad !== "") {
                 //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 babylonMeshesArray;
+            return Promise.all(mtlPromises).then(function () {
+                return babylonMeshesArray;
+            });
         };
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
         OBJFileLoader.INVERT_Y = false;

Fichier diff supprimé car celui-ci est trop grand
+ 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);
     }
-    class OBJFileLoader implements ISceneLoaderPlugin {
+    class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
         static INVERT_Y: boolean;
         name: string;
@@ -82,9 +82,14 @@ declare module BABYLON {
          * @private
          */
         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.
          * 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
          */
-        private bufferViews;
+        private _bufferViews;
         /**
          * 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
          */
-        private nodes;
+        private _nodes;
         /**
          * 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
          */
-        private scenes;
+        private _scenes;
         /**
          * 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
          */
-        private materials;
-        private materialMap;
+        _materials: IMaterial[];
+        _materialMap: {
+            [materialID: number]: number;
+        };
         /**
          * 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
          */
-        private images;
+        _images: IImage[];
         /**
          * Stores all the texture samplers
          */
-        private samplers;
+        _samplers: ISampler[];
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
         /**
          * Stores the animations for glTF models
          */
-        private animations;
+        private _animations;
         /**
          * 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
          */
-        private babylonScene;
+        private _babylonScene;
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * 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
          */
-        private nodeMap;
+        private _nodeMap;
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
-        private convertToRightHandedSystem;
+        private _convertToRightHandedSystem;
         /**
          * Baked animation sample rate
          */
-        private animationSampleRate;
+        private _animationSampleRate;
         /**
          * 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
          * @param babylonScene Babylon scene object
          * @param options Options to modify the behavior of the exporter
          */
         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);
         /**
          * 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
      * @hidden
      */
-    class _GLTFMaterial {
+    class _GLTFMaterialExporter {
         /**
          * 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
          */
-        private static _maxSpecularPower;
+        private static readonly _MaxSpecularPower;
+        /**
+         * Mapping to store textures
+         */
+        private _textureMap;
         /**
          * 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
          * @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 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
          * @param originalMaterial original glTF material
          * @returns glTF material without texture parameters
          */
-        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        _stripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
          * Specifies if the material has any texture parameters present
          * @param material glTF Material
          * @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
          * @param babylonStandardMaterial
          * @returns glTF Metallic Roughness Material representation
          */
-        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        _convertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
          * Computes the metallic factor
          * @param diffuse diffused value
@@ -457,7 +472,7 @@ declare module BABYLON.GLTF2 {
          * @param babylonMaterial Babylon Material
          * @returns The Babylon alpha mode value
          */
-        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        _getAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
          * Converts a Babylon Standard Material to a glTF Material
          * @param babylonStandardMaterial BJS Standard Material
@@ -468,21 +483,7 @@ declare module BABYLON.GLTF2 {
          * @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
          */
-        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
          * @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 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
          * @param buffer typed array buffer
@@ -509,7 +503,7 @@ declare module BABYLON.GLTF2 {
          * @param mimeType mimetype of the image
          * @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
          * @param width width of the texture in pixels
@@ -517,7 +511,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @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
          * @param texture1 first texture to resize
@@ -525,7 +519,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @returns resized textures or null
          */
-        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        private _resizeTexturesToSameDimensions(texture1, texture2, scene);
         /**
          * Convert Specular Glossiness Textures to Metallic Roughness
          * 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
          * @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
          * @param specularGlossiness interface with specular glossiness material properties
          * @returns interface with metallic roughness material properties
          */
-        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        private _convertSpecularGlossinessToMetallicRoughness(specularGlossiness);
         /**
          * Calculates the surface reflectance, independent of lighting conditions
          * @param color Color source to calculate brightness from
          * @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
          * @param color
          * @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
          * @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
          * @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
          * @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
          * @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
          * @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 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
          * @param babylonTexture Babylon texture to extract
@@ -611,7 +598,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name and data
          * @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
          * @param base64Texture base64 texture string
@@ -622,7 +609,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image data
          * @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);
     }
 }
 

Fichier diff supprimé car celui-ci est trop grand
+ 350 - 315
dist/preview release/serializers/babylon.glTF2Serializer.js


Fichier diff supprimé car celui-ci est trop grand
+ 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
          */
-        private bufferViews;
+        private _bufferViews;
         /**
          * 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
          */
-        private nodes;
+        private _nodes;
         /**
          * 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
          */
-        private scenes;
+        private _scenes;
         /**
          * 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
          */
-        private materials;
-        private materialMap;
+        _materials: IMaterial[];
+        _materialMap: {
+            [materialID: number]: number;
+        };
         /**
          * 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
          */
-        private images;
+        _images: IImage[];
         /**
          * Stores all the texture samplers
          */
-        private samplers;
+        _samplers: ISampler[];
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
         /**
          * Stores the animations for glTF models
          */
-        private animations;
+        private _animations;
         /**
          * 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
          */
-        private babylonScene;
+        private _babylonScene;
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * 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
          */
-        private nodeMap;
+        private _nodeMap;
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
-        private convertToRightHandedSystem;
+        private _convertToRightHandedSystem;
         /**
          * Baked animation sample rate
          */
-        private animationSampleRate;
+        private _animationSampleRate;
         /**
          * 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
          * @param babylonScene Babylon scene object
          * @param options Options to modify the behavior of the exporter
          */
         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);
         /**
          * 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
      * @hidden
      */
-    class _GLTFMaterial {
+    class _GLTFMaterialExporter {
         /**
          * 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
          */
-        private static _maxSpecularPower;
+        private static readonly _MaxSpecularPower;
+        /**
+         * Mapping to store textures
+         */
+        private _textureMap;
         /**
          * 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
          * @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 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
          * @param originalMaterial original glTF material
          * @returns glTF material without texture parameters
          */
-        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        _stripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
          * Specifies if the material has any texture parameters present
          * @param material glTF Material
          * @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
          * @param babylonStandardMaterial
          * @returns glTF Metallic Roughness Material representation
          */
-        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        _convertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
          * Computes the metallic factor
          * @param diffuse diffused value
@@ -465,7 +480,7 @@ declare module BABYLON.GLTF2 {
          * @param babylonMaterial Babylon Material
          * @returns The Babylon alpha mode value
          */
-        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        _getAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
          * Converts a Babylon Standard Material to a glTF Material
          * @param babylonStandardMaterial BJS Standard Material
@@ -476,21 +491,7 @@ declare module BABYLON.GLTF2 {
          * @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
          */
-        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
          * @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 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
          * @param buffer typed array buffer
@@ -517,7 +511,7 @@ declare module BABYLON.GLTF2 {
          * @param mimeType mimetype of the image
          * @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
          * @param width width of the texture in pixels
@@ -525,7 +519,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @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
          * @param texture1 first texture to resize
@@ -533,7 +527,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @returns resized textures or null
          */
-        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        private _resizeTexturesToSameDimensions(texture1, texture2, scene);
         /**
          * Convert Specular Glossiness Textures to Metallic Roughness
          * 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
          * @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
          * @param specularGlossiness interface with specular glossiness material properties
          * @returns interface with metallic roughness material properties
          */
-        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        private _convertSpecularGlossinessToMetallicRoughness(specularGlossiness);
         /**
          * Calculates the surface reflectance, independent of lighting conditions
          * @param color Color source to calculate brightness from
          * @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
          * @param color
          * @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
          * @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
          * @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
          * @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
          * @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
          * @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 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
          * @param babylonTexture Babylon texture to extract
@@ -619,7 +606,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name and data
          * @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
          * @param base64Texture base64 texture string
@@ -630,7 +617,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image data
          * @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);
     }
 }
 

Fichier diff supprimé car celui-ci est trop grand
+ 350 - 315
dist/preview release/serializers/babylonjs.serializers.js


Fichier diff supprimé car celui-ci est trop grand
+ 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
          */
-        private bufferViews;
+        private _bufferViews;
         /**
          * 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
          */
-        private nodes;
+        private _nodes;
         /**
          * 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
          */
-        private scenes;
+        private _scenes;
         /**
          * 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
          */
-        private materials;
-        private materialMap;
+        _materials: IMaterial[];
+        _materialMap: {
+            [materialID: number]: number;
+        };
         /**
          * 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
          */
-        private images;
+        _images: IImage[];
         /**
          * Stores all the texture samplers
          */
-        private samplers;
+        _samplers: ISampler[];
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
         /**
          * Stores the animations for glTF models
          */
-        private animations;
+        private _animations;
         /**
          * 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
          */
-        private babylonScene;
+        private _babylonScene;
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * 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
          */
-        private nodeMap;
+        private _nodeMap;
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
-        private convertToRightHandedSystem;
+        private _convertToRightHandedSystem;
         /**
          * Baked animation sample rate
          */
-        private animationSampleRate;
+        private _animationSampleRate;
         /**
          * 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
          * @param babylonScene Babylon scene object
          * @param options Options to modify the behavior of the exporter
          */
         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);
         /**
          * 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
      * @hidden
      */
-    class _GLTFMaterial {
+    class _GLTFMaterialExporter {
         /**
          * 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
          */
-        private static _maxSpecularPower;
+        private static readonly _MaxSpecularPower;
+        /**
+         * Mapping to store textures
+         */
+        private _textureMap;
         /**
          * 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
          * @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 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
          * @param originalMaterial original glTF material
          * @returns glTF material without texture parameters
          */
-        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        _stripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
          * Specifies if the material has any texture parameters present
          * @param material glTF Material
          * @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
          * @param babylonStandardMaterial
          * @returns glTF Metallic Roughness Material representation
          */
-        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        _convertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
          * Computes the metallic factor
          * @param diffuse diffused value
@@ -472,7 +487,7 @@ declare module BABYLON.GLTF2 {
          * @param babylonMaterial Babylon Material
          * @returns The Babylon alpha mode value
          */
-        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        _getAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
          * Converts a Babylon Standard Material to a glTF Material
          * @param babylonStandardMaterial BJS Standard Material
@@ -483,21 +498,7 @@ declare module BABYLON.GLTF2 {
          * @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
          */
-        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
          * @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 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
          * @param buffer typed array buffer
@@ -524,7 +518,7 @@ declare module BABYLON.GLTF2 {
          * @param mimeType mimetype of the image
          * @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
          * @param width width of the texture in pixels
@@ -532,7 +526,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @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
          * @param texture1 first texture to resize
@@ -540,7 +534,7 @@ declare module BABYLON.GLTF2 {
          * @param scene babylonjs scene
          * @returns resized textures or null
          */
-        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        private _resizeTexturesToSameDimensions(texture1, texture2, scene);
         /**
          * Convert Specular Glossiness Textures to Metallic Roughness
          * 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
          * @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
          * @param specularGlossiness interface with specular glossiness material properties
          * @returns interface with metallic roughness material properties
          */
-        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        private _convertSpecularGlossinessToMetallicRoughness(specularGlossiness);
         /**
          * Calculates the surface reflectance, independent of lighting conditions
          * @param color Color source to calculate brightness from
          * @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
          * @param color
          * @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
          * @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
          * @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
          * @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
          * @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
          * @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 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
          * @param babylonTexture Babylon texture to extract
@@ -626,7 +613,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image file name and data
          * @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
          * @param base64Texture base64 texture string
@@ -637,7 +624,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData map of image data
          * @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);
     }
 }
 

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


Fichier diff supprimé car celui-ci est trop grand
+ 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))
 - 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 `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))
   - Improved CPU particles rendering performance (up to x2 on low end devices)
   - 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 `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 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
 
@@ -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))
 - 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))
-- 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))
 - 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))
 - 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))
 - 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 ```lodGenerationScale``` and ```lodGenerationOffset``` to force rebind ([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))
+- `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
 
@@ -79,6 +86,8 @@
 - 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))
 - 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
 
@@ -106,10 +115,12 @@
 - 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))
 - 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
 
 - 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
 

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

@@ -5,10 +5,11 @@ module BABYLON.GUI {
      * Class used to create slider controls
      */
     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 _maximum = 100;
         private _value = 50;
+        private _isVertical = false;
         private _background = "black";
         private _borderColor = "white";
         private _barOffset = new ValueAndUnit(5, ValueAndUnit.UNITMODE_PIXEL, false);
@@ -135,6 +136,20 @@ module BABYLON.GUI {
             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 */
         public get isThumbCircle(): boolean {
             return this._isThumbCircle;
@@ -177,27 +192,94 @@ module BABYLON.GUI {
             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 {
             context.save();
 
             this._applyStates(context);
             if (this._processMeasures(parentMeasure, context)) {
                 // 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 {
-                    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 {
-                    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) {
@@ -207,16 +289,40 @@ module BABYLON.GUI {
                     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;
-                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) {
@@ -224,47 +330,84 @@ module BABYLON.GUI {
                     context.shadowOffsetX = 0;
                     context.shadowOffsetY = 0;
                 }
-
+                
                 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) {
                     context.shadowColor = this.shadowColor;
                     context.shadowBlur = this.shadowBlur;
                     context.shadowOffsetX = this.shadowOffsetX;
                     context.shadowOffsetY = this.shadowOffsetY;
                 }
-
                 if (this._isThumbCircle) {
                     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();
-
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowBlur = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetY = 0;
                     }
-
                     context.strokeStyle = this._borderColor;
                     context.stroke();
                 }
                 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) {
                         context.shadowBlur = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetY = 0;
                     }
-
                     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();
@@ -278,8 +421,15 @@ module BABYLON.GUI {
             if (this.rotation != 0) {
                 this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
                 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 {

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

@@ -24,7 +24,7 @@ module BABYLON {
             if (data instanceof ArrayBuffer) {
                 return;
             }
-            
+
             //Split the lines from the file
             var lines = data.split('\n');
             //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 INVERT_Y = false;
@@ -252,28 +252,32 @@ module BABYLON {
                 () => { 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
-            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
-            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>
          * @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 normals: Array<BABYLON.Vector3> = [];      //Values for the normals
@@ -702,15 +706,15 @@ module BABYLON {
                         uvs?: Array<number>;
                         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();
 
                     //Push the last mesh created with only the name
@@ -739,15 +743,15 @@ module BABYLON {
                             uvs?: Array<number>;
                             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++;
                         //If meshes are already defined
                         meshesFromObj.push(objMesh);
@@ -847,49 +851,61 @@ module BABYLON {
                 //Set the data from the VertexBuffer to the current BABYLON.Mesh
                 vertexData.applyToMesh(babylonMesh);
                 if (OBJFileLoader.INVERT_Y) {
-                    babylonMesh.scaling.y *=-1;
+                    babylonMesh.scaling.y *= -1;
                 }
 
                 //Push the mesh into an array
                 babylonMeshesArray.push(babylonMesh);
             }
+
+            let mtlPromises: Array<Promise<any>> = [];
             //load the materials
             //Check if we have a file to load
             if (fileToLoad !== "") {
                 //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 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
          */
-        private bufferViews: IBufferView[];
+        private _bufferViews: IBufferView[];
         /**
          * 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
          */
-        private nodes: INode[];
+        private _nodes: INode[];
         /**
          * 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
          */
-        private scenes: IScene[];
+        private _scenes: IScene[];
         /**
          * 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
          */
-        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
          */
-        private textures: ITexture[];
+        public _textures: ITexture[];
         /**
          * Stores all the generated image information, which is referenced by glTF textures
          */
-        private images: IImage[];
+        public _images: IImage[];
 
         /**
          * Stores all the texture samplers
          */
-        private samplers: ISampler[];
+        public _samplers: ISampler[];
         /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
         /**
          * Stores the animations for glTF models
          */
-        private animations: IAnimation[];
+        private _animations: IAnimation[];
         /**
          * 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
          */
-        private babylonScene: Scene;
+        private _babylonScene: Scene;
         /**
          * Stores a map of the image data, where the key is the file name and the value
          * 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
          */
-        private nodeMap: { [key: number]: number };
+        private _nodeMap: { [key: number]: number };
 
         /**
          * Specifies if the Babylon scene should be converted to right-handed on export
          */
-        private convertToRightHandedSystem: boolean;
+        private _convertToRightHandedSystem: boolean;
 
         /**
          * Baked animation sample rate
          */
-        private animationSampleRate: number;
+        private _animationSampleRate: number;
 
         /**
          * 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
@@ -119,24 +123,43 @@ module BABYLON.GLTF2 {
          * @param options Options to modify the behavior of the exporter
          */
         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 || {};
-            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) {
@@ -182,7 +205,7 @@ module BABYLON.GLTF2 {
          * @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 {
-            if (this.convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
+            if (this._convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
                 switch (primitiveMode) {
                     case Material.TriangleFillMode: {
                         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) {
             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) {
                         (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) {
                         index = k * stride;
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
-                        if (this.convertToRightHandedSystem) {
+                        if (this._convertToRightHandedSystem) {
                             _GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
                         }
                         vertexAttributes.push(vertexData.asArray());
@@ -456,7 +479,7 @@ module BABYLON.GLTF2 {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                         index = k * stride;
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
-                        if (this.convertToRightHandedSystem) {
+                        if (this._convertToRightHandedSystem) {
                             _GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
                         }
                         vertexAttributes.push(vertexData.asArray());
@@ -467,7 +490,7 @@ module BABYLON.GLTF2 {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                         index = k * stride;
                         const vertexData = Vector4.FromArray(meshAttributeArray, index);
-                        if (this.convertToRightHandedSystem) {
+                        if (this._convertToRightHandedSystem) {
                             _GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
                         }
                         vertexAttributes.push(vertexData.asArray());
@@ -486,7 +509,7 @@ module BABYLON.GLTF2 {
                 case VertexBuffer.UV2Kind: {
                     for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                         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;
                 }
@@ -510,61 +533,61 @@ module BABYLON.GLTF2 {
          * @returns json data as 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 imageData: { data: Uint8Array, mimeType: ImageMimeType };
             let bufferView: IBufferView;
-            let byteOffset: number = this.totalByteLength;
+            let byteOffset: number = this._totalByteLength;
 
             let glTF: IGLTF = {
-                asset: this.asset
+                asset: this._asset
             };
             if (buffer.byteLength) {
                 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;
             }
-            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) {
-                    glTF.images = this.images;
+                    glTF.images = this._images;
                 }
                 else {
                     glTF.images = [];
 
-                    this.images.forEach((image) => {
+                    this._images.forEach((image) => {
                         if (image.uri) {
-                            imageData = this.imageData[image.uri];
+                            imageData = this._imageData[image.uri];
                             imageName = image.uri.split('.')[0] + " image";
                             bufferView = _GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                             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.mimeType = imageData.mimeType;
                             image.uri = undefined;
@@ -606,9 +629,9 @@ module BABYLON.GLTF2 {
                 container.glTFFiles[glTFFileName] = jsonText;
                 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> {
             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();
             });
         }
@@ -655,8 +681,8 @@ module BABYLON.GLTF2 {
                 const jsonLength = jsonText.length;
                 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 binPadding = this._getPadding(binaryBuffer.byteLength);
@@ -711,8 +737,8 @@ module BABYLON.GLTF2 {
                 const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
 
                 // 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);
 
@@ -723,6 +749,8 @@ module BABYLON.GLTF2 {
                 const container = new GLTFData();
                 container.glTFFiles[glbFileName] = glbFile;
 
+                this._localEngine.dispose();
+
                 return container;
             });
         }
@@ -734,7 +762,7 @@ module BABYLON.GLTF2 {
          */
         private setNodeTransformation(node: INode, babylonTransformNode: TransformNode): void {
             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)) {
@@ -746,7 +774,7 @@ module BABYLON.GLTF2 {
                 rotationQuaternion.multiplyInPlace(babylonTransformNode.rotationQuaternion);
             }
             if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
-                if (this.convertToRightHandedSystem) {
+                if (this._convertToRightHandedSystem) {
                     _GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
 
                 }
@@ -781,7 +809,7 @@ module BABYLON.GLTF2 {
                 if (vertexData) {
                     const byteLength = vertexData.length * 4;
                     const bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
-                    this.bufferViews.push(bufferView);
+                    this._bufferViews.push(bufferView);
 
                     this.writeAttributeData(
                         kind,
@@ -854,27 +882,27 @@ module BABYLON.GLTF2 {
         private setAttributeKind(meshPrimitive: IMeshPrimitive, attributeKind: string): void {
             switch (attributeKind) {
                 case VertexBuffer.PositionKind: {
-                    meshPrimitive.attributes.POSITION = this.accessors.length - 1;
+                    meshPrimitive.attributes.POSITION = this._accessors.length - 1;
                     break;
                 }
                 case VertexBuffer.NormalKind: {
-                    meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                    meshPrimitive.attributes.NORMAL = this._accessors.length - 1;
                     break;
                 }
                 case VertexBuffer.ColorKind: {
-                    meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
+                    meshPrimitive.attributes.COLOR_0 = this._accessors.length - 1;
                     break;
                 }
                 case VertexBuffer.TangentKind: {
-                    meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
+                    meshPrimitive.attributes.TANGENT = this._accessors.length - 1;
                     break;
                 }
                 case VertexBuffer.UVKind: {
-                    meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                    meshPrimitive.attributes.TEXCOORD_0 = this._accessors.length - 1;
                     break;
                 }
                 case VertexBuffer.UV2Kind: {
-                    meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                    meshPrimitive.attributes.TEXCOORD_1 = this._accessors.length - 1;
                     break;
                 }
                 default: {
@@ -926,7 +954,7 @@ module BABYLON.GLTF2 {
                         }
 
                         this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride);
-                        attribute.bufferViewIndex = this.bufferViews.length - 1;
+                        attribute.bufferViewIndex = this._bufferViews.length - 1;
                         vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                     }
                 }
@@ -936,8 +964,8 @@ module BABYLON.GLTF2 {
                     if (indices) {
                         const byteLength = indices.length * 4;
                         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) {
                             binaryWriter.setUInt32(indices[k]);
@@ -963,21 +991,21 @@ module BABYLON.GLTF2 {
                                         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) {
                                 babylonMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
                                 if (babylonMaterial) {
-                                    materialIndex = this.materialMap[babylonMaterial.uniqueId];
+                                    materialIndex = this._materialMap[babylonMaterial.uniqueId];
                                 }
                             }
                             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: {} };
                         this.setPrimitiveMode(meshPrimitive, primitiveMode);
@@ -985,7 +1013,7 @@ module BABYLON.GLTF2 {
                         for (const attribute of attributeData) {
                             const attributeKind = attribute.kind;
                             if (attributeKind === VertexBuffer.UVKind || attributeKind === VertexBuffer.UV2Kind) {
-                                if (glTFMaterial && !_GLTFMaterial._HasTexturesPresent(glTFMaterial)) {
+                                if (glTFMaterial && !this._glTFMaterialExporter._hasTexturesPresent(glTFMaterial)) {
                                     continue;
                                 }
                             }
@@ -998,10 +1026,10 @@ module BABYLON.GLTF2 {
                                     if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                         minMax = { min: null, max: null };
                                         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);
-                                        this.accessors.push(accessor);
+                                        this._accessors.push(accessor);
                                         this.setAttributeKind(meshPrimitive, attributeKind);
                                         if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
                                             uvCoordsPresent = true;
@@ -1013,15 +1041,15 @@ module BABYLON.GLTF2 {
                         if (indexBufferViewIndex) {
                             // Create accessor
                             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) {
-                            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
-                                let byteOffset = indexBufferViewIndex != null ? this.bufferViews[indexBufferViewIndex].byteOffset : null;
+                                let byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
                                 if (byteOffset == null) { byteOffset = 0; }
                                 let babylonIndices: Nullable<IndicesArray> = null;
                                 if (indexBufferViewIndex != null) {
@@ -1034,7 +1062,7 @@ module BABYLON.GLTF2 {
                                     for (let attribute of attributeData) {
                                         let vertexData = bufferMesh.getVerticesData(attribute.kind);
                                         if (vertexData) {
-                                            let byteOffset = this.bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset;
+                                            let byteOffset = this._bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset;
                                             if (!byteOffset) {
                                                 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;
@@ -1072,23 +1100,23 @@ module BABYLON.GLTF2 {
             let directDescendents: Node[];
             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.
                 for (let babylonTransformNode of nodes) {
-                    glTFNodeIndex = this.nodeMap[babylonTransformNode.uniqueId];
+                    glTFNodeIndex = this._nodeMap[babylonTransformNode.uniqueId];
                     if (glTFNodeIndex != null) {
-                        glTFNode = this.nodes[glTFNodeIndex];
+                        glTFNode = this._nodes[glTFNodeIndex];
                         if (!babylonTransformNode.parent) {
-                            if (!this.shouldExportTransformNode(babylonTransformNode)) {
+                            if (!this._shouldExportTransformNode(babylonTransformNode)) {
                                 Tools.Log("Omitting " + babylonTransformNode.name + " from scene.");
                             }
                             else {
-                                if (this.convertToRightHandedSystem) {
+                                if (this._convertToRightHandedSystem) {
                                     if (glTFNode.translation) {
                                         glTFNode.translation[2] *= -1;
                                         glTFNode.translation[0] *= -1;
@@ -1104,15 +1132,15 @@ module BABYLON.GLTF2 {
                         if (!glTFNode.children && directDescendents && directDescendents.length) {
                             glTFNode.children = [];
                             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) {
-                    this.scenes.push(scene);
+                    this._scenes.push(scene);
                 }
             });
         }
@@ -1140,12 +1168,12 @@ module BABYLON.GLTF2 {
                 if (shouldExportTransformNode(babylonTransformNode)) {
                     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;
 
                     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 {
@@ -1154,16 +1182,16 @@ module BABYLON.GLTF2 {
             };
 
             if (runtimeGLTFAnimation.channels.length && runtimeGLTFAnimation.samplers.length) {
-                this.animations.push(runtimeGLTFAnimation);
+                this._animations.push(runtimeGLTFAnimation);
             }
             idleGLTFAnimations.forEach((idleGLTFAnimation) => {
                 if (idleGLTFAnimation.channels.length && idleGLTFAnimation.samplers.length) {
-                    this.animations.push(idleGLTFAnimation);
+                    this._animations.push(idleGLTFAnimation);
                 }
             });
 
             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;
@@ -1190,8 +1218,8 @@ module BABYLON.GLTF2 {
             this.setPrimitiveAttributes(mesh, babylonTransformNode, binaryWriter);
 
             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;

Fichier diff supprimé car celui-ci est trop grand
+ 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;
                 return false;
             }
-            var returnValue = true;
+
             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
             if (keys[0].frame !== 0) {
                 var newKey = { frame: 0, value: keys[0].value };
@@ -424,7 +432,7 @@
                     to++;
                 }
             }
-            
+
             // Compute ratio
             var range = to - from;
             var offsetValue;

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

@@ -42,12 +42,12 @@ module BABYLON {
         private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
         private _isOutputConnected = false;
         private _htmlAudioElement: HTMLAudioElement;
-        private _urlType: string = "Unknown";
+        private _urlType: 'Unknown' | 'String' | 'Array' | 'ArrayBuffer' | 'MediaStream' = "Unknown";
 
         /**
         * Create a sound and attach it to a scene
         * @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 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 (urlOrArrayBuffer) {
                     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 codecSupportedFound = false;
 
                         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":
                                 if ((<ArrayBuffer>urlOrArrayBuffer).byteLength > 0) {
                                     codecSupportedFound = true;
@@ -131,8 +150,8 @@ module BABYLON {
                                     if (codecSupportedFound) {
                                         // Loading sound using XHR2
                                         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) => {
                                                 if (exception) {
                                                     Tools.Error("XHR " + exception.status + " error on: " + url + ".");
@@ -210,7 +229,7 @@ module BABYLON {
         }
 
         public dispose() {
-            if (Engine.audioEngine.canUseWebAudio) { 
+            if (Engine.audioEngine.canUseWebAudio) {
                 if (this.isPlaying) {
                     this.stop();
                 }
@@ -241,6 +260,10 @@ module BABYLON {
                     document.body.removeChild(this._htmlAudioElement);
                 }
 
+                if (this._streamingSource) {
+                    this._streamingSource.disconnect();
+                }
+
                 if (this._connectedMesh && this._registerFunc) {
                     this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                     this._connectedMesh = null;
@@ -282,7 +305,7 @@ module BABYLON {
                 this._playbackRate = options.playbackRate || this._playbackRate;
                 this._updateSpatialParameters();
                 if (this.isPlaying) {
-                    if (this._streaming) {
+                    if (this._streaming && this._htmlAudioElement) {
                         this._htmlAudioElement.playbackRate = this._playbackRate;
                     }
                     else {
@@ -449,7 +472,9 @@ module BABYLON {
                         }
                         this._streamingSource.disconnect();
                         this._streamingSource.connect(this._inputAudioNode);
-                        this._htmlAudioElement.play();
+                        if (this._htmlAudioElement) {
+                            this._htmlAudioElement.play();
+                        }
                     }
                     else {
                         this._soundSource = Engine.audioEngine.audioContext.createBufferSource();
@@ -486,10 +511,14 @@ module BABYLON {
         public stop(time?: number) {
             if (this.isPlaying) {
                 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) {
@@ -508,7 +537,11 @@ module BABYLON {
             if (this.isPlaying) {
                 this.isPaused = true;
                 if (this._streaming) {
-                    this._htmlAudioElement.pause();
+                    if (this._htmlAudioElement) {
+                        this._htmlAudioElement.pause();
+                    } else {
+                        this._streamingSource.disconnect();
+                    }
                 }
                 else if (Engine.audioEngine.audioContext) {
                     this.stop(0);
@@ -534,7 +567,7 @@ module BABYLON {
         public setPlaybackRate(newPlaybackRate: number) {
             this._playbackRate = newPlaybackRate;
             if (this.isPlaying) {
-                if (this._streaming) {
+                if (this._streaming && this._htmlAudioElement) {
                     this._htmlAudioElement.playbackRate = this._playbackRate;
                 }
                 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)
          */
         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
         private _debugMode = false;
         private _moving = false;
@@ -149,8 +153,10 @@ module BABYLON {
                         var pickedPoint = this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
                         
                         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
                             if(this.options.dragAxis){
                                 // Convert local drag axis to world
@@ -209,7 +215,7 @@ module BABYLON {
         private _lineA = new Vector3(0,0,0);
         private _lineB = 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
         private _updateDragPlanePosition(ray:Ray, dragPlanePosition:Vector3){
             this._pointA.copyFrom(dragPlanePosition);

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

@@ -11,6 +11,8 @@ module BABYLON {
         private _virtualOriginMesh:AbstractMesh;
         private _virtualDragMesh:AbstractMesh;
         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)
          */
@@ -83,18 +85,23 @@ module BABYLON {
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh);
                         this._virtualDragMesh.position.copyFrom(pickedMesh.absolutePosition);
                         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);
+                        pickedMesh.setParent(oldParent);
                         this._virtualOriginMesh.addChild(this._virtualDragMesh);
-
+                        
                         // Update state
+                        this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
                         this.dragging = true;
                         this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
                         this.dragging = false;
+                        this._moving = false;
                         this.currentDraggingPointerID = -1;
                         pickedMesh = null;
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh);
@@ -105,7 +112,7 @@ module BABYLON {
                         var originDragDifference = pointerInfo.pickInfo.ray.origin.subtract(lastSixDofOriginPosition);
                         lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
                         var localOriginDragDifference = Vector3.TransformCoordinates(originDragDifference, Matrix.Invert(this._virtualOriginMesh.getWorldMatrix().getRotationMatrix()));
-                        
+
                         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
                         var zDragDistance = Vector3.Dot(localOriginDragDifference, this._virtualOriginMesh.position.normalizeToNew());
@@ -113,26 +120,47 @@ module BABYLON {
                         if(this._virtualDragMesh.position.z < 0){
                             this._virtualDragMesh.position.z = 0;
                         }
-                        
+
                         // Update the controller position
                         this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
                         this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.subtract(pointerInfo.pickInfo.ray.direction));
                         this._virtualOriginMesh.removeChild(this._virtualDragMesh)
-                    
+                        
                         // Move the virtualObjectsPosition into the picked mesh's space if needed
                         this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
                         if(pickedMesh.parent){
                             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
             this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(()=>{
-                if(this.dragging && pickedMesh){
+                if(this.dragging && this._moving && pickedMesh){
                     // Slowly move mesh to avoid jitter
                     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("webkitfullscreenchange", this._onFullscreenChange, false);
             document.addEventListener("msfullscreenchange", this._onFullscreenChange, false);
+            (<any>document).onmsfullscreenchange = this._onFullscreenChange;
 
             // Display vr button when headset is connected
             if (webVROptions.createFallbackVRDeviceOrientationFreeCamera) {
@@ -724,6 +725,8 @@ module BABYLON {
                 this._fullscreenVRpresenting = document.webkitIsFullScreen;
             } else if (document.msIsFullScreen !== undefined) {
                 this._fullscreenVRpresenting = document.msIsFullScreen;
+            } else if ((<any>document).msFullscreenElement !== undefined) {
+                this._fullscreenVRpresenting = (<any>document).msFullscreenElement;
             }
             if (!this._fullscreenVRpresenting && this._canvas) {
                 this.exitVR();
@@ -1796,6 +1799,7 @@ module BABYLON {
             document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("msfullscreenchange", this._onFullscreenChange);
+            (<any>document).onmsfullscreenchange = null;
 
             this._scene.getEngine().onVRDisplayChangedObservable.removeCallback(this._onVRDisplayChanged);
             this._scene.getEngine().onVRRequestPresentStart.removeCallback(this._onVRRequestPresentStart);

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

@@ -92,10 +92,24 @@
             }
 
             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 uniformsNames defines a list of attribute names 
          * @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,
             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(
                 {
                     vertex: "particles",
                     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;
                         }
 
-                        // 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;
                     }, samplingMode);
@@ -5545,12 +5577,13 @@
          * @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 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
          */
-        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 texture = new InternalTexture(this, InternalTexture.DATASOURCE_CUBE);
+            var texture = fallback ? fallback : new InternalTexture(this, InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
             texture.url = rootUrl;
             texture.generateMipMaps = !noMipmap;
@@ -5567,8 +5600,7 @@
             var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             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;
                 isKTX = true;
             } else {
@@ -5577,6 +5609,11 @@
             }
 
             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) {
                     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,
             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;
             if (!gl) {

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

@@ -4,6 +4,16 @@ module BABYLON {
      */
     export class AxisDragGizmo extends Gizmo {
         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
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -12,49 +22,85 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer, dragAxis:Vector3, color:Color3){
             super(gizmoLayer);
-            this.updateGizmoRotationToMatchAttachedMesh = false;
             
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
             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
+            var arrow = new BABYLON.AbstractMesh("", 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
-            arrowMesh.scaling.scaleInPlace(0.1);
+            arrowMesh.scaling.scaleInPlace(0.05);
             arrowMesh.material = coloredMaterial;
             arrowMesh.rotation.x = Math.PI/2;
             arrowMesh.position.z+=0.3;
             arrowTail.rotation.x = Math.PI/2;
             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
-            this._dragBehavior = new PointerDragBehavior({dragAxis: new BABYLON.Vector3(0,0,1)});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._dragBehavior.onDragObservable.add((event)=>{
-                if(!this.interactionsEnabled){
-                    return;
-                }
                 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
          */
         public dispose(){
+            this.onSnapObservable.clear();
+            this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             super.dispose();
         } 

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

@@ -4,6 +4,16 @@ module BABYLON {
      */
     export class AxisScaleGizmo extends Gizmo {
         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
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -12,18 +22,22 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer, dragAxis:Vector3, color:Color3){
             super(gizmoLayer);
-            this.updateGizmoRotationToMatchAttachedMesh=false;
             
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
             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
-            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
             arrowMesh.scaling.scaleInPlace(0.1);
@@ -32,32 +46,85 @@ module BABYLON {
             arrowMesh.position.z+=0.3;
             arrowTail.rotation.x = Math.PI/2;
             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
-            this._dragBehavior = new PointerDragBehavior({dragAxis: new BABYLON.Vector3(0,0,1)});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
             this._dragBehavior.moveAttached = false;
             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){
-                    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
          */
         public dispose(){
+            this.onSnapObservable.clear();
+            this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             super.dispose();
         } 

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

@@ -11,6 +11,8 @@ module BABYLON {
         private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
         private _scaleDragSpeed = 0.2;
 
+        private _tmpQuaternion = new Quaternion();
+        private _tmpVector = new Vector3(0,0,0);
         /**
          * Creates an BoundingBoxGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -65,25 +67,41 @@ module BABYLON {
                 // Drag behavior
                 var _dragBehavior = new PointerDragBehavior({});
                 _dragBehavior.moveAttached = false;
+                _dragBehavior.updateDragPlane = false;
                 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)=>{
                     if(this.attachedMesh){
-                        var worldDragDirection = sphere.forward;
+                        var worldDragDirection = startingTurnDirection;
 
                         // Project the world right on to the drag plane
                         var toSub = event.dragPlaneNormal.scale(Vector3.Dot(event.dragPlaneNormal, worldDragDirection));
                         var dragAxis = worldDragDirection.subtract(toSub).normalizeToNew();
 
                         // 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
-                        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
                                 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
          */
         public dispose(){

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

@@ -7,10 +7,19 @@ module BABYLON {
          * The root mesh of the gizmo
          */
         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)
+         * * 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)
          */
@@ -24,18 +33,7 @@ module BABYLON {
          */
         protected _updateScale = 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>>;
@@ -63,6 +61,7 @@ module BABYLON {
                     }
                 }
             })
+            this.attachedMesh = null;
         }
         /**
          * Disposes of the gizmo

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

@@ -4,6 +4,18 @@ module BABYLON {
      */
     export class PlaneRotationGizmo extends Gizmo {
         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
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -12,41 +24,48 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer, planeNormal:Vector3, color:Color3){
             super(gizmoLayer);
-            this.updateGizmoRotationToMatchAttachedMesh=false;
             
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
             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
-            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
             rotationMesh.scaling.scaleInPlace(0.1);
             rotationMesh.material = coloredMaterial;
             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
-            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: new Vector3(0,0,1)});
+            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal});
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
 
             var lastDragPosition:Nullable<Vector3> = null;
 
             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)=>{
-                if(!this.interactionsEnabled){
-                    return;
-                }
                 if(this.attachedMesh && lastDragPosition){
                     if(!this.attachedMesh.rotationQuaternion){
                         this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
@@ -57,38 +76,76 @@ module BABYLON {
                     var cross = Vector3.Cross(newVector,originalVector);
                     var dot = Vector3.Dot(newVector,originalVector);
                     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
                     if(gizmoLayer.utilityLayerScene.activeCamera){
                         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;
                     
+                    // 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;
+                    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
          */
         public dispose(){
+            this.onSnapObservable.clear();
+            this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             super.dispose();
         } 

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

@@ -8,25 +8,33 @@ module BABYLON {
         private _zDrag:AxisDragGizmo;
 
         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
          * @param gizmoLayer The utility layer the gizmo will be added to
          */
         constructor(gizmoLayer:UtilityLayerRenderer){
             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;
 
         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
@@ -18,15 +20,21 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer){
             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;
 
         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
@@ -18,15 +20,21 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer){
             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;
         /**
-         * 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.
          */
         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.
      */
     export class PhotoDome extends Node {
+        private _useDirectMapping = false;
 
         /**
          * The texture being displayed on the sphere
@@ -13,12 +14,29 @@ module BABYLON {
         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 {
             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
          */
@@ -48,32 +66,33 @@ module BABYLON {
          */
         constructor(name: string, urlOfPhoto: string, options: {
             resolution?: number,
-            size?: number
+            size?: number,
+            useDirectMapping?: boolean
         }, scene: Scene) {
             super(name, scene);
 
             // set defaults and manage values
             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);
 
+            if (options.useDirectMapping === undefined) {
+                this._useDirectMapping = true;    
+            } else {
+                this._useDirectMapping = options.useDirectMapping;            
+            }
+
             // create
             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
-            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.fovMultiplier = 1.0;
 
+            this.photoTexture = new Texture(urlOfPhoto, scene, true, !this._useDirectMapping);
+           
             // configure mesh
             this._mesh.material = material;
             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.
      */
     export class VideoDome extends Node {
+        private _useDirectMapping = false;
 
         /**
          * The video texture being displayed on the sphere
@@ -52,35 +53,45 @@ module BABYLON {
             autoPlay?: boolean,
             loop?: boolean,
             size?: number,
-            poster?: string
+            poster?: string,            
+            useDirectMapping?: boolean
         }, scene: Scene) {
             super(name, scene);
 
             // set defaults and manage values
             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.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
             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);
 
+            if (options.useDirectMapping === undefined) {
+                this._useDirectMapping = true;    
+            } else {
+                this._useDirectMapping = options.useDirectMapping;            
+            }
+
             // create
             let tempOptions: VideoTextureSettings = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
             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
-            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.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
             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 {
             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 generateStencilBuffer True to generate a stencil buffer
          * @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);
             scene = this.getScene();
 
@@ -204,6 +205,7 @@
             this._renderTargetOptions = {
                 generateMipMaps: generateMipMaps,
                 type: type,
+                format: format,
                 samplingMode: samplingMode,
                 generateDepthBuffer: generateDepthBuffer,
                 generateStencilBuffer: generateStencilBuffer

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

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

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

@@ -869,9 +869,9 @@
          */
         constructor(
             /** defines the first coordinate */
-            public x: number, 
+            public x: number = 0, 
             /** defines the second coordinate */
-            public y: number) {
+            public y: number = 0) {
         }
 
         /**
@@ -1496,15 +1496,15 @@
             /**
              * Defines the first coordinates (on X axis)
              */
-            public x: number,
+            public x: number = 0,
             /**
              * Defines the second coordinates (on Y axis)
              */
-            public y: number,
+            public y: number = 0,
             /**
              * 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
     export class Tmp {
         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 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

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

@@ -326,6 +326,7 @@
          * @returns the VertexData 
          */
         public transform(matrix: Matrix): VertexData {
+            var flip = matrix.m[0] * matrix.m[5] * matrix.m[10] < 0;
             var transformed = Vector3.Zero();
             var index: number;
             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;
         }
 

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

@@ -536,8 +536,10 @@ module BABYLON {
          * Returns the TransformNode.
          */
         public setParent(node: Nullable<Node>): TransformNode {
-
-            if (node === null) {
+            if (!node && !this.parent) {
+                return this;
+            }
+            if (!node) {
                 var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
                 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
          */
         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 {
         /**
+         * List of animations used by the particle system.
+         */
+        animations: Animation[];
+        /**
          * The id of the Particle system.
          */
         id: string;
@@ -114,12 +118,35 @@ module BABYLON {
          * Maximum power of emitting particles.
          */
         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.
          * 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.
@@ -188,6 +215,41 @@ module BABYLON {
          * Is this system ready to be used/rendered
          * @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 _activeCount: number;
         private _currentActiveCount: number;
+        private _accumulatedCount = 0;
         private _renderEffect: Effect;
         private _updateEffect: Effect;
 
@@ -56,8 +57,9 @@
         private _timeDelta = 0;
 
         private _randomTexture: RawTexture;
+        private _randomTexture2: RawTexture;
 
-        private _attributesStrideSize = 18;
+        private _attributesStrideSize = 21;
         private _updateEffectOptions: EffectCreationOptions;
 
         private _randomTextureSize: number;
@@ -284,6 +286,23 @@
             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
          * @return true if the system is ready
@@ -317,6 +336,7 @@
         public start(): void {
             this._started = true;
             this._stopped = false;
+            this._preWarmDone = false;
         }
 
         /**
@@ -361,7 +381,158 @@
             this._isBillboardBased = value;
 
             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.
@@ -385,6 +556,11 @@
                 ...options
             };
 
+            var optionsAsNumber = <number>options;
+            if (isFinite(optionsAsNumber)) {
+                fullOptions.capacity = optionsAsNumber;
+            }
+
             this._capacity = fullOptions.capacity;
             this._activeCount = fullOptions.capacity;
             this._currentActiveCount = 0;
@@ -392,12 +568,12 @@
             this._scene.particleSystems.push(this);
 
             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", 
                                 "angleRange", "radiusRange"],
                 uniformBuffersNames: [],
-                samplers:["randomSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
@@ -407,6 +583,8 @@
                 transformFeedbackVaryings: []
             };
 
+            this.particleEmitterType = new BoxParticleEmitter();
+
             // Random data
             var maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
             var d = [];
@@ -416,12 +594,22 @@
                 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.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.particleEmitterType = new BoxParticleEmitter();
         }
 
         private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {            
@@ -429,12 +617,22 @@
             updateVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3);
             updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
             updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
-            updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 1);
-            updateVertexBuffers["size"] = source.createVertexBuffer("size", 6, 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) {
                 updateVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3);
                 offset += 3;
@@ -453,10 +651,20 @@
             renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, this._attributesStrideSize, true);
             renderVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1, this._attributesStrideSize, true);
             renderVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1, this._attributesStrideSize, true);
-            renderVertexBuffers["size"] = source.createVertexBuffer("size", 6, 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) {
                 renderVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
                 offset += 3;
@@ -481,7 +689,15 @@
             var data = new Array<float>();
 
             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++) {
@@ -496,17 +712,28 @@
 
                 // Seed
                 data.push(Math.random());
+                data.push(Math.random());
+                data.push(Math.random());
+                data.push(Math.random());
 
                 // Size
                 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
                 data.push(0.0);
@@ -560,11 +787,29 @@
                 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) {
                 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) {
                 this._updateEffectOptions.transformFeedbackVaryings.push("outInitialDirection");
@@ -585,7 +830,11 @@
 
             if (this._isBillboardBased) {
                 defines += "\n#define BILLBOARD";
-            }            
+            }         
+            
+            if (this._colorGradientsTexture) {
+                defines += "\n#define COLORGRADIENTS";
+            }   
 
             if (this._renderEffect && this._renderEffect.defines === defines) {
                 return;
@@ -594,14 +843,15 @@
             this._renderEffect = new Effect("gpuRenderParticles", 
                                             ["position", "age", "life", "size", "color", "offset", "uv", "initialDirection", "angle"], 
                                             ["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.
+         * @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;
 
             if (!this._stopped) {
@@ -609,17 +859,66 @@
                     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
          */
-        public render(): number {
+        public render(preWarm = false): number {
             if (!this._started) {
                 return 0;
             }
 
+            this._createColorGradientTexture();
+            this._createSizeGradientTexture();
+
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
 
@@ -627,36 +926,61 @@
                 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
             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
-
             this._engine.enableEffect(this._updateEffect);
             this._engine.setState(false);    
             
             this._updateEffect.setFloat("currentCount", this._currentActiveCount);
             this._updateEffect.setFloat("timeDelta", this._timeDelta);
             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("randomSampler2", this._randomTexture2);
             this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
             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.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);
 
+            if (this._sizeGradientsTexture) {      
+                this._updateEffect.setTexture("sizeGradientSampler", this._sizeGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
@@ -683,48 +1007,53 @@
             this._engine.setRasterizerState(true);
             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
             this._targetIndex++;
             if (this._targetIndex === 2) {
@@ -789,13 +1118,27 @@
 
             this._releaseBuffers();
             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) {
                 this._randomTexture.dispose();
                 (<any>this._randomTexture) = null;
             }
 
+            if (this._randomTexture2) {
+                this._randomTexture2.dispose();
+                (<any>this._randomTexture2) = null;
+            }            
+
             if (disposeTexture && this.particleTexture) {
                 this.particleTexture.dispose();
                 this.particleTexture = null;
@@ -835,51 +1178,7 @@
         public serialize(): 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;            
         }
@@ -895,72 +1194,8 @@
             var name = parsedParticleSystem.name;
             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.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;
         }        

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

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

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

@@ -1,11 +1,5 @@
 module BABYLON {
 
-    /** @hidden */
-    class ColorGradient {
-        public gradient: number;
-        public color: Color4;
-    }
-
     /**
      * 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.
@@ -120,7 +114,16 @@
         /**
          * 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).
@@ -182,8 +185,27 @@
         public gravity = Vector3.Zero();
 
         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.
          * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
@@ -315,6 +337,12 @@
          */
         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
         */
@@ -459,19 +487,18 @@
                         continue;
                     }
                     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 {
                             particle.colorStep.scaleToRef(this._scaledUpdateSpeed, this._scaledColorStep);
@@ -489,6 +516,13 @@
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, 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) {
                             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
          * @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, color: Color4): ParticleSystem {
+        public addColorGradient(gradient: number, color: Color4, color2?: Color4): ParticleSystem {
             if (!this._colorGradients) {
                 this._colorGradients = [];
             }
 
             let colorGradient = new ColorGradient();
             colorGradient.gradient = gradient;
-            colorGradient.color = color;
+            colorGradient.color1 = color;
+            colorGradient.color2 = color2;
             this._colorGradients.push(colorGradient);
 
             this._colorGradients.sort((a, b) => {
@@ -520,7 +605,7 @@
                 }
 
                 return 0;
-            })
+            });
 
             return this;
         }
@@ -668,6 +753,12 @@
             if (this.subEmitters && this.subEmitters.length != 0) {
                 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.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.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
 
+                particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
+
                 if (!this._colorGradients || this._colorGradients.length === 0) {
                     var step = Scalar.RandomRange(0, 1.0);
 
@@ -875,11 +969,36 @@
                     this.colorDead.subtractToRef(particle.color, this._colorDiff);
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                 } 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 {
             if (this._customEffect) {
                 return this._customEffect;
@@ -904,17 +1023,8 @@
             if (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(
                     "particles",
@@ -928,24 +1038,26 @@
 
         /**
          * 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)
                 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
             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) {
@@ -1215,6 +1329,8 @@
                 program = this.customShader;
                 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);
+            } else if (this._customEffect) {
+                custom = this._customEffect;
             }
             var result = new ParticleSystem(name, this._capacity, this._scene, custom);
             result.customShader = program;
@@ -1244,53 +1360,9 @@
         public serialize(): 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.blendMode = this.blendMode;
             serializationObject.customShader = this.customShader;
             serializationObject.preventAutoStart = this.preventAutoStart;
 
@@ -1303,42 +1375,95 @@
 
             serializationObject.isAnimationSheetEnabled = this._isAnimationSheetEnabled;
 
-            // Emitter
-            if (this.particleEmitterType) {
-                serializationObject.particleEmitterType = this.particleEmitterType.serialize();
-            }            
-
             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
             if (parsedParticleSystem.textureName) {
                 particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene);
@@ -1377,24 +1502,105 @@
                 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.maxLifeTime = parsedParticleSystem.maxLifeTime;
             particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
             particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
             particleSystem.emitRate = parsedParticleSystem.emitRate;
-            particleSystem.minEmitBox = Vector3.FromArray(parsedParticleSystem.minEmitBox);
-            particleSystem.maxEmitBox = Vector3.FromArray(parsedParticleSystem.maxEmitBox);
             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.color2 = Color4.FromArray(parsedParticleSystem.color2);
             particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
             particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
             particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
-            particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);
             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.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
             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)
 {
 #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
 		return vec3(1.0 - s, t, 0);
@@ -34,9 +36,12 @@ vec3 computeReflectionCoords(vec4 worldPos, vec3 worldNormal)
 #ifdef REFLECTIONMAP_EQUIRECTANGULAR
 
 	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);
 #endif

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

@@ -1,6 +1,6 @@
 #version 300 es
 
-uniform vec4 colorDead;
+
 uniform mat4 view;
 uniform mat4 projection;
 
@@ -9,7 +9,6 @@ in vec3 position;
 in float age;
 in float life;
 in vec3 size;
-in vec4 color;
 #ifndef BILLBOARD
 in vec3 initialDirection;
 #endif
@@ -26,11 +25,22 @@ uniform mat4 invView;
 out float fClipDistance;
 #endif
 
+#ifdef COLORGRADIENTS
+uniform sampler2D colorGradientSampler;
+#else
+uniform vec4 colorDead;
+in vec4 color;
+#endif
 
 void main() {
   vUV = uv;
   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;
 

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

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

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

@@ -125,13 +125,13 @@ module BABYLON {
             var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
             for (var level = 0; level < mipmapCount; level++) {
                 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++) {
                     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);
 
-                    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
                 }
                 width = Math.max(1.0, width * 0.5);

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

@@ -3,6 +3,57 @@
         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
     // 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 {
@@ -1607,6 +1658,25 @@
                 }, 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 
      */
     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', () => {
             const scene = new BABYLON.Scene(subject);
             const babylonStandardMaterial = new BABYLON.StandardMaterial("specGloss", scene);
@@ -65,8 +42,9 @@ describe('Babylon glTF Serializer', () => {
             babylonStandardMaterial.specularColor = BABYLON.Color3.Black();
             babylonStandardMaterial.specularPower = 64;
             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]);
 
@@ -74,12 +52,10 @@ describe('Babylon glTF Serializer', () => {
 
             metalRough.roughnessFactor.should.be.approximately(0.328809, 1e-6);
         });
-
         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', () => {
             const scene = new BABYLON.Scene(subject);
 
@@ -92,7 +68,6 @@ describe('Babylon glTF Serializer', () => {
                 jsonData.asset.generator.should.be.equal("BabylonJS");
             });
         });
-
         it('should serialize sphere geometry in scene to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             BABYLON.Mesh.CreateSphere('sphere', 16, 2, scene);
@@ -125,33 +100,6 @@ describe('Babylon glTF Serializer', () => {
                     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', () => {
             const scene = new BABYLON.Scene(subject);
             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;
             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([
                     {
                         frame: 0,