Przeglądaj źródła

Merge pull request #3315 from BabylonJS/master

Nightly #3
David Catuhe 7 lat temu
rodzic
commit
1b1cc60855
49 zmienionych plików z 9523 dodań i 9111 usunięć
  1. 7816 7789
      Playground/babylon.d.txt
  2. 57 46
      Playground/js/frame.js
  3. 55 47
      Playground/js/index.js
  4. 3 3
      Viewer/dist/basicExample.html
  5. 516 508
      dist/preview release/babylon.d.ts
  6. 21 21
      dist/preview release/babylon.js
  7. 46 32
      dist/preview release/babylon.max.js
  8. 21 21
      dist/preview release/babylon.worker.js
  9. 31 31
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  10. 107 52
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  11. 1 0
      dist/preview release/gui/babylon.gui.d.ts
  12. 19 3
      dist/preview release/gui/babylon.gui.js
  13. 3 3
      dist/preview release/gui/babylon.gui.min.js
  14. 1 0
      dist/preview release/gui/babylon.gui.module.d.ts
  15. 17 6
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  16. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  17. 44 14
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  18. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  19. 61 20
      dist/preview release/loaders/babylon.glTFFileLoader.js
  20. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  21. 61 20
      dist/preview release/loaders/babylonjs.loaders.js
  22. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  23. 3 2
      dist/preview release/materialsLibrary/babylon.waterMaterial.d.ts
  24. 26 9
      dist/preview release/materialsLibrary/babylon.waterMaterial.js
  25. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  26. 26 9
      dist/preview release/materialsLibrary/babylonjs.materials.js
  27. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  28. 3 2
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  29. 64 64
      dist/preview release/viewer/babylon.viewer.js
  30. 1 0
      dist/preview release/what's new.md
  31. 131 131
      gui/src/controls/control.ts
  32. 68 52
      gui/src/controls/inputText.ts
  33. 34 34
      gui/src/controls/slider.ts
  34. 20 6
      loaders/src/glTF/1.0/babylon.glTFLoader.ts
  35. 44 13
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  36. 118 109
      localDev/index.html
  37. 42 17
      materialsLibrary/src/water/babylon.waterMaterial.ts
  38. 12 9
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  39. 14 17
      src/Cameras/babylon.targetCamera.ts
  40. 1 0
      src/Engine/babylon.engine.ts
  41. 11 0
      src/Gamepad/Controllers/babylon.windowsMotionController.ts
  42. 6 9
      src/babylon.scene.ts
  43. BIN
      tests/validation/ReferenceImages/GUI.png
  44. BIN
      tests/validation/ReferenceImages/charting.png
  45. BIN
      tests/validation/ReferenceImages/gltf1CesiumMan.png
  46. BIN
      tests/validation/ReferenceImages/pbrrough.png
  47. BIN
      tests/validation/ReferenceImages/upVector.png
  48. 6 0
      tests/validation/config.json
  49. 1 0
      tests/validation/validation.js

Plik diff jest za duży
+ 7816 - 7789
Playground/babylon.d.txt


+ 57 - 46
Playground/js/frame.js

@@ -61,72 +61,83 @@
             }
 
             var canvas = document.getElementById("renderCanvas");
-            engine = new BABYLON.Engine(canvas, true, { stencil: true });
-            BABYLON.Camera.ForceAttachControlToAlwaysPreventDefault = true;
-            engine.runRenderLoop(function () {
-                if (engine.scenes.length === 0) {
-                    return;
-                }
 
-                if (canvas.width !== canvas.clientWidth) {
-                    engine.resize();
-                }
+            var checkCamera = true;
+            var wrappedEval = false;
+            var createEngineFunction = "createDefaultEngine";
+            var createSceneFunction;
 
-                var scene = engine.scenes[0];
+            var createDefaultEngine = function () {
+                return new BABYLON.Engine(canvas, true, { stencil: true });
+            }
 
-                if (scene.activeCamera || scene.activeCameras.length > 0) {
-                    scene.render();
-                }
+            var scene;
 
-                if (fpsLabel) {
-                    fpsLabel.innerHTML = engine.getFps().toFixed() + " fps";
-                }
-            });
+            if (code.indexOf("createEngine") !== -1) {
+                createEngineFunction = "createEngine";
+            }
 
-            var scene;
             if (code.indexOf("delayCreateScene") !== -1) { // createScene
+                createSceneFunction = "delayCreateScene";
+                checkCamera = false;
+            } else if (code.indexOf("createScene") !== -1) { // createScene
+                createSceneFunction = "createScene";
+            } else if (code.indexOf("CreateScene") !== -1) { // CreateScene
+                createSceneFunction = "CreateScene";
+            } else if (code.indexOf("createscene") !== -1) { // createscene
+                createSceneFunction = "createscene";
+            }
+
+            if (!createSceneFunction) {
+                // just pasted code.
+                engine = createDefaultEngine();
+                scene = new BABYLON.Scene(engine);
+                eval("runScript = function(scene, canvas) {" + code + "}");
+                runScript(scene, canvas);
+
+                zipCode = "var scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
+            } else {
+                //execute the code
                 eval(code);
-                scene = delayCreateScene();
-                if (!scene) {
-                    showError("delayCreateScene function must return a scene.", null);
+                //create engine
+                eval("engine = " + createEngineFunction + "()");
+                if (!engine) {
+                    showError("createEngine function must return an engine.", null);
                     return;
                 }
 
-                zipCode = code + "\r\n\r\nvar scene = createScene();";
-            } if (code.indexOf("createScene") !== -1) { // createScene
-                eval(code);
-                scene = createScene();
+                //create scene
+                eval("scene = " + createSceneFunction + "()");
+
                 if (!scene) {
-                    showError("createScene function must return a scene.");
+                    showError(createSceneFunction + " function must return a scene.", null);
                     return;
                 }
 
-                zipCode = code + "\r\n\r\nvar scene = createScene();";
-            } else if (code.indexOf("CreateScene") !== -1) { // CreateScene
-                eval(code);
-                scene = CreateScene();
-                if (!scene) {
-                    showError("CreateScene function must return a scene.");
+                // update the scene code for the zip file
+                zipCode = code + "\r\n\r\nvar scene = " + createSceneFunction + "()";
+            }
+
+            BABYLON.Camera.ForceAttachControlToAlwaysPreventDefault = true;
+            engine.runRenderLoop(function () {
+                if (engine.scenes.length === 0) {
                     return;
                 }
 
-                zipCode = code + "\r\n\r\nvar scene = CreateScene();";
-            } else if (code.indexOf("createscene") !== -1) { // createscene
-                eval(code);
-                scene = createscene();
-                if (!scene) {
-                    showError("createscene function must return a scene.");
-                    return;
+                if (canvas.width !== canvas.clientWidth) {
+                    engine.resize();
                 }
 
-                zipCode = code + "\r\n\r\nvar scene = createscene();";
-            } else { // Direct code
-                scene = new BABYLON.Scene(engine);
-                eval("runScript = function(scene, canvas) {" + code + "}");
-                runScript(scene, canvas);
+                var scene = engine.scenes[0];
 
-                zipCode = "var scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
-            }
+                if (scene.activeCamera || scene.activeCameras.length > 0) {
+                    scene.render();
+                }
+
+                if (fpsLabel) {
+                    fpsLabel.innerHTML = engine.getFps().toFixed() + " fps";
+                }
+            });
 
         } catch (e) {
             // showError(e.message);

+ 55 - 47
Playground/js/index.js

@@ -355,77 +355,85 @@
                 }
 
                 var canvas = document.getElementById("renderCanvas");
-                engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });
                 document.getElementById("errorZone").style.display = 'none';
                 document.getElementById("errorZone").innerHTML = "";
                 document.getElementById("statusBar").innerHTML = "Loading assets...Please wait";
                 var checkCamera = true;
+                var wrappedEval = false;
+                var createEngineFunction = "createDefaultEngine";
+                var createSceneFunction;
 
-                engine.runRenderLoop(function () {
-                    if (engine.scenes.length === 0) {
-                        return;
-                    }
-
-                    if (canvas.width !== canvas.clientWidth) {
-                        engine.resize();
-                    }
+                var code = jsEditor.getValue();
 
-                    var scene = engine.scenes[0];
+                var createDefaultEngine = function () {
+                    return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });
+                }
 
-                    if (scene.activeCamera || scene.activeCameras.length > 0) {
-                        scene.render();
-                    }
+                var scene;
 
-                    fpsLabel.style.right = document.body.clientWidth - (jsEditor.domElement.clientWidth + canvas.clientWidth) + "px";
-                    fpsLabel.innerHTML = engine.getFps().toFixed() + " fps";
-                });
+                if (code.indexOf("createEngine") !== -1) {
+                    createEngineFunction = "createEngine";
+                }
 
-                var code = jsEditor.getValue();
-                var scene;
                 if (code.indexOf("delayCreateScene") !== -1) { // createScene
-                    eval(code);
-                    scene = delayCreateScene();
+                    createSceneFunction = "delayCreateScene";
                     checkCamera = false;
-                    if (!scene) {
-                        showError("delayCreateScene function must return a scene.", null);
-                        return;
-                    }
-
-                    zipCode = code + "\r\n\r\nvar scene = createScene();";
                 } else if (code.indexOf("createScene") !== -1) { // createScene
+                    createSceneFunction = "createScene";
+                } else if (code.indexOf("CreateScene") !== -1) { // CreateScene
+                    createSceneFunction = "CreateScene";
+                } else if (code.indexOf("createscene") !== -1) { // createscene
+                    createSceneFunction = "createscene";
+                }
+
+                if (!createSceneFunction) {
+                    // just pasted code.
+                    engine = createDefaultEngine();
+                    scene = new BABYLON.Scene(engine);
+                    eval("runScript = function(scene, canvas) {" + code + "}");
+                    runScript(scene, canvas);
+
+                    zipCode = "var scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
+                } else {
+                    //execute the code
                     eval(code);
-                    scene = createScene();
-                    if (!scene) {
-                        showError("createScene function must return a scene.", null);
+                    //create engine
+                    eval("engine = " + createEngineFunction + "()");
+                    if (!engine) {
+                        showError("createEngine function must return an engine.", null);
                         return;
                     }
 
-                    zipCode = code + "\r\n\r\nvar scene = createScene();";
-                } else if (code.indexOf("CreateScene") !== -1) { // CreateScene
-                    eval(code);
-                    scene = CreateScene();
+                    //create scene
+                    eval("scene = " + createSceneFunction + "()");
+
                     if (!scene) {
-                        showError("CreateScene function must return a scene.", null);
+                        showError(createSceneFunction + " function must return a scene.", null);
                         return;
                     }
 
-                    zipCode = code + "\r\n\r\nvar scene = CreateScene();";
-                } else if (code.indexOf("createscene") !== -1) { // createscene
-                    eval(code);
-                    scene = createscene();
-                    if (!scene) {
-                        showError("createscene function must return a scene.", null);
+                    // update the scene code for the zip file
+                    zipCode = code + "\r\n\r\nvar scene = " + createSceneFunction + "()";
+                }
+
+                engine.runRenderLoop(function () {
+                    if (engine.scenes.length === 0) {
                         return;
                     }
 
-                    zipCode = code + "\r\n\r\nvar scene = createscene();";
-                } else { // Direct code
-                    scene = new BABYLON.Scene(engine);
-                    eval("runScript = function(scene, canvas) {" + code + "}");
-                    runScript(scene, canvas);
+                    if (canvas.width !== canvas.clientWidth) {
+                        engine.resize();
+                    }
 
-                    zipCode = "var scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
-                }
+                    var scene = engine.scenes[0];
+
+                    if (scene.activeCamera || scene.activeCameras.length > 0) {
+                        scene.render();
+                    }
+
+                    fpsLabel.style.right = document.body.clientWidth - (jsEditor.domElement.clientWidth + canvas.clientWidth) + "px";
+                    fpsLabel.innerHTML = engine.getFps().toFixed() + " fps";
+                });
 
                 if (engine.scenes.length === 0) {
                     showError("You must at least create a scene.", null);

+ 3 - 3
Viewer/dist/basicExample.html

@@ -17,8 +17,8 @@
     </head>
 
     <body>
-        <babylon model.title="Amazing Rabbit" model.subtitle="BabylonJS" model.thumbnail="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png"
-            model.url="https://playground.babylonjs.com/scenes/Rabbit.babylon" camera.behaviors.auto-rotate="0" templates.nav-bar.params.disable-on-fullscreen="true"></babylon>
+        <babylon model.title="Damaged Helmet" model.subtitle="BabylonJS" model.thumbnail="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png"
+            model.url="https://www.babylonjs.com/Assets/DamagedHelmet/glTF/DamagedHelmet.gltf" camera.behaviors.auto-rotate="0" templates.nav-bar.params.disable-on-fullscreen="true"></babylon>
         <script src="viewer.js"></script>
         <script>
             // The following lines are redundant. 
@@ -31,4 +31,4 @@
         </script>
     </body>
 
-</html>
+</html>

Plik diff jest za duży
+ 516 - 508
dist/preview release/babylon.d.ts


Plik diff jest za duży
+ 21 - 21
dist/preview release/babylon.js


+ 46 - 32
dist/preview release/babylon.max.js

@@ -17903,6 +17903,7 @@ var BABYLON;
             this._forcePointsCloud = false;
             this.forceShowBoundingBoxes = false;
             this.animationsEnabled = true;
+            this.useConstantAnimationDeltaTime = false;
             this.constantlyUpdateMeshUnderPointer = false;
             this.hoverCursor = "pointer";
             this.defaultCursor = "";
@@ -19604,7 +19605,7 @@ var BABYLON;
                 }
                 this._animationTimeLast = now;
             }
-            var deltaTime = (now - this._animationTimeLast) * this.animationTimeScale;
+            var deltaTime = this.useConstantAnimationDeltaTime ? 16.0 : (now - this._animationTimeLast) * this.animationTimeScale;
             this._animationTime += deltaTime;
             this._animationTimeLast = now;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
@@ -20676,7 +20677,7 @@ var BABYLON;
                 var defaultFPS = (60.0 / 1000.0);
                 var defaultFrameTime = 1000 / 60; // frame time in MS
                 if (this._physicsEngine) {
-                    defaultFrameTime = this._physicsEngine.getTimeStep() / 1000;
+                    defaultFrameTime = this._physicsEngine.getTimeStep() * 1000;
                 }
                 var stepsTaken = 0;
                 var maxSubSteps = this._engine.getLockstepMaxSteps();
@@ -20691,18 +20692,15 @@ var BABYLON;
                     // Physics
                     if (this._physicsEngine) {
                         this.onBeforePhysicsObservable.notifyObservers(this);
-                        this._physicsEngine._step(defaultFPS);
+                        this._physicsEngine._step(defaultFrameTime / 1000);
                         this.onAfterPhysicsObservable.notifyObservers(this);
                     }
                     this.onAfterStepObservable.notifyObservers(this);
                     this._currentStepId++;
-                    if ((internalSteps > 1) && (stepsTaken != internalSteps - 1)) {
-                        this._evaluateActiveMeshes();
-                    }
                     stepsTaken++;
                     deltaTime -= defaultFrameTime;
-                } while (deltaTime > 0 && stepsTaken < maxSubSteps);
-                this._timeAccumulator = deltaTime;
+                } while (deltaTime > 0 && stepsTaken < internalSteps);
+                this._timeAccumulator = deltaTime < 0 ? 0 : deltaTime;
             }
             else {
                 // Animations
@@ -38425,7 +38423,7 @@ var BABYLON;
             _this._cameraTransformMatrix = BABYLON.Matrix.Zero();
             _this._cameraRotationMatrix = BABYLON.Matrix.Zero();
             _this._referencePoint = new BABYLON.Vector3(0, 0, 1);
-            _this._defaultUpVector = new BABYLON.Vector3(0, 1, 0);
+            _this._currentUpVector = new BABYLON.Vector3(0, 1, 0);
             _this._transformedReferencePoint = BABYLON.Vector3.Zero();
             _this._lookAtTemp = BABYLON.Matrix.Zero();
             _this._tempMatrix = BABYLON.Matrix.Zero();
@@ -38514,7 +38512,7 @@ var BABYLON;
         // Target
         TargetCamera.prototype.setTarget = function (target) {
             this.upVector.normalize();
-            BABYLON.Matrix.LookAtLHToRef(this.position, target, this._defaultUpVector, this._camMatrix);
+            BABYLON.Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
             this._camMatrix.invert();
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
             var vDir = target.subtract(this.position);
@@ -38614,27 +38612,22 @@ var BABYLON;
                 BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
             }
             //update the up vector!
-            BABYLON.Vector3.TransformNormalToRef(this._defaultUpVector, this._cameraRotationMatrix, this.upVector);
+            BABYLON.Vector3.TransformNormalToRef(this.upVector, this._cameraRotationMatrix, this._currentUpVector);
         };
         TargetCamera.prototype._getViewMatrix = function () {
-            if (!this.lockedTarget) {
-                // Compute
-                this._updateCameraRotationMatrix();
-                BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
-                // Computing target and final matrix
-                this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
-            }
-            else {
-                var targetPosition = this._getLockedTargetPosition();
-                if (targetPosition) {
-                    this._currentTarget.copyFrom(targetPosition);
-                }
+            if (this.lockedTarget) {
+                this.setTarget(this._getLockedTargetPosition());
             }
+            // Compute
+            this._updateCameraRotationMatrix();
+            BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
+            // Computing target and final matrix
+            this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
             if (this.getScene().useRightHandedSystem) {
-                BABYLON.Matrix.LookAtRHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
+                BABYLON.Matrix.LookAtRHToRef(this.position, this._currentTarget, this._currentUpVector, this._viewMatrix);
             }
             else {
-                BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
+                BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this._currentUpVector, this._viewMatrix);
             }
             return this._viewMatrix;
         };
@@ -57598,6 +57591,8 @@ var BABYLON;
                 pointingPoseMeshName: 'POINTING_POSE'
             };
             _this.onTrackpadChangedObservable = new BABYLON.Observable();
+            _this.onTrackpadValuesChangedObservable = new BABYLON.Observable();
+            _this.trackpad = { x: 0, y: 0 };
             _this.controllerType = BABYLON.PoseEnabledControllerType.WINDOWS;
             _this._loadedMeshInfo = null;
             return _this;
@@ -57637,6 +57632,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(WindowsMotionController.prototype, "onTouchpadValuesChangedObservable", {
+            get: function () {
+                return this.onTrackpadValuesChangedObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Called once per frame by the engine.
          */
@@ -57645,6 +57647,11 @@ var BABYLON;
             // Only need to animate axes if there is a loaded mesh
             if (this._loadedMeshInfo) {
                 if (this.browserGamepad.axes) {
+                    if (this.browserGamepad.axes[2] != this.trackpad.x || this.browserGamepad.axes[3] != this.trackpad.y) {
+                        this.trackpad.x = this.browserGamepad["axes"][2];
+                        this.trackpad.y = this.browserGamepad["axes"][3];
+                        this.onTrackpadValuesChangedObservable.notifyObservers(this.trackpad);
+                    }
                     for (var axis = 0; axis < this._mapping.axisMeshNames.length; axis++) {
                         this.lerpAxisTransform(axis, this.browserGamepad.axes[axis]);
                     }
@@ -72203,14 +72210,14 @@ var BABYLON;
             }
             // Exiting VR mode using 'ESC' key on desktop
             this._onKeyDown = function (event) {
-                if (event.keyCode === 27 && _this.isInVRMode()) {
+                if (event.keyCode === 27 && _this.isInVRMode) {
                     _this.exitVR();
                 }
             };
             document.addEventListener("keydown", this._onKeyDown);
             // Exiting VR mode double tapping the touch screen
             this._scene.onPrePointerObservable.add(function (pointerInfo, eventState) {
-                if (_this.isInVRMode()) {
+                if (_this.isInVRMode) {
                     _this.exitVR();
                     if (_this._fullscreenVRpresenting) {
                         _this._scene.getEngine().switchFullscreen(true);
@@ -72358,9 +72365,16 @@ var BABYLON;
                 }
             }
         };
-        VRExperienceHelper.prototype.isInVRMode = function () {
-            return this._webVRpresenting || this._fullscreenVRpresenting;
-        };
+        Object.defineProperty(VRExperienceHelper.prototype, "isInVRMode", {
+            /**
+             * Gets a value indicating if we are currently in VR mode.
+             */
+            get: function () {
+                return this._webVRpresenting || this._fullscreenVRpresenting;
+            },
+            enumerable: true,
+            configurable: true
+        });
         VRExperienceHelper.prototype.onVrDisplayPresentChange = function () {
             var vrDisplay = this._scene.getEngine().getVRDevice();
             if (vrDisplay) {
@@ -72386,7 +72400,7 @@ var BABYLON;
                 return;
             }
             this._btnVR.className = "babylonVRicon";
-            if (this.isInVRMode()) {
+            if (this.isInVRMode) {
                 this._btnVR.className += " vrdisplaypresenting";
             }
             else {
@@ -73170,7 +73184,7 @@ var BABYLON;
             }
         };
         VRExperienceHelper.prototype.dispose = function () {
-            if (this.isInVRMode()) {
+            if (this.isInVRMode) {
                 this.exitVR();
             }
             if (this._deviceOrientationCamera) {

Plik diff jest za duży
+ 21 - 21
dist/preview release/babylon.worker.js


Plik diff jest za duży
+ 31 - 31
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 107 - 52
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -17903,6 +17903,7 @@ var BABYLON;
             this._forcePointsCloud = false;
             this.forceShowBoundingBoxes = false;
             this.animationsEnabled = true;
+            this.useConstantAnimationDeltaTime = false;
             this.constantlyUpdateMeshUnderPointer = false;
             this.hoverCursor = "pointer";
             this.defaultCursor = "";
@@ -19604,7 +19605,7 @@ var BABYLON;
                 }
                 this._animationTimeLast = now;
             }
-            var deltaTime = (now - this._animationTimeLast) * this.animationTimeScale;
+            var deltaTime = this.useConstantAnimationDeltaTime ? 16.0 : (now - this._animationTimeLast) * this.animationTimeScale;
             this._animationTime += deltaTime;
             this._animationTimeLast = now;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
@@ -20676,7 +20677,7 @@ var BABYLON;
                 var defaultFPS = (60.0 / 1000.0);
                 var defaultFrameTime = 1000 / 60; // frame time in MS
                 if (this._physicsEngine) {
-                    defaultFrameTime = this._physicsEngine.getTimeStep() / 1000;
+                    defaultFrameTime = this._physicsEngine.getTimeStep() * 1000;
                 }
                 var stepsTaken = 0;
                 var maxSubSteps = this._engine.getLockstepMaxSteps();
@@ -20691,18 +20692,15 @@ var BABYLON;
                     // Physics
                     if (this._physicsEngine) {
                         this.onBeforePhysicsObservable.notifyObservers(this);
-                        this._physicsEngine._step(defaultFPS);
+                        this._physicsEngine._step(defaultFrameTime / 1000);
                         this.onAfterPhysicsObservable.notifyObservers(this);
                     }
                     this.onAfterStepObservable.notifyObservers(this);
                     this._currentStepId++;
-                    if ((internalSteps > 1) && (stepsTaken != internalSteps - 1)) {
-                        this._evaluateActiveMeshes();
-                    }
                     stepsTaken++;
                     deltaTime -= defaultFrameTime;
-                } while (deltaTime > 0 && stepsTaken < maxSubSteps);
-                this._timeAccumulator = deltaTime;
+                } while (deltaTime > 0 && stepsTaken < internalSteps);
+                this._timeAccumulator = deltaTime < 0 ? 0 : deltaTime;
             }
             else {
                 // Animations
@@ -38425,7 +38423,7 @@ var BABYLON;
             _this._cameraTransformMatrix = BABYLON.Matrix.Zero();
             _this._cameraRotationMatrix = BABYLON.Matrix.Zero();
             _this._referencePoint = new BABYLON.Vector3(0, 0, 1);
-            _this._defaultUpVector = new BABYLON.Vector3(0, 1, 0);
+            _this._currentUpVector = new BABYLON.Vector3(0, 1, 0);
             _this._transformedReferencePoint = BABYLON.Vector3.Zero();
             _this._lookAtTemp = BABYLON.Matrix.Zero();
             _this._tempMatrix = BABYLON.Matrix.Zero();
@@ -38514,7 +38512,7 @@ var BABYLON;
         // Target
         TargetCamera.prototype.setTarget = function (target) {
             this.upVector.normalize();
-            BABYLON.Matrix.LookAtLHToRef(this.position, target, this._defaultUpVector, this._camMatrix);
+            BABYLON.Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
             this._camMatrix.invert();
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
             var vDir = target.subtract(this.position);
@@ -38614,27 +38612,22 @@ var BABYLON;
                 BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
             }
             //update the up vector!
-            BABYLON.Vector3.TransformNormalToRef(this._defaultUpVector, this._cameraRotationMatrix, this.upVector);
+            BABYLON.Vector3.TransformNormalToRef(this.upVector, this._cameraRotationMatrix, this._currentUpVector);
         };
         TargetCamera.prototype._getViewMatrix = function () {
-            if (!this.lockedTarget) {
-                // Compute
-                this._updateCameraRotationMatrix();
-                BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
-                // Computing target and final matrix
-                this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
-            }
-            else {
-                var targetPosition = this._getLockedTargetPosition();
-                if (targetPosition) {
-                    this._currentTarget.copyFrom(targetPosition);
-                }
+            if (this.lockedTarget) {
+                this.setTarget(this._getLockedTargetPosition());
             }
+            // Compute
+            this._updateCameraRotationMatrix();
+            BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
+            // Computing target and final matrix
+            this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
             if (this.getScene().useRightHandedSystem) {
-                BABYLON.Matrix.LookAtRHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
+                BABYLON.Matrix.LookAtRHToRef(this.position, this._currentTarget, this._currentUpVector, this._viewMatrix);
             }
             else {
-                BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
+                BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this._currentUpVector, this._viewMatrix);
             }
             return this._viewMatrix;
         };
@@ -57444,6 +57437,8 @@ var BABYLON;
                 pointingPoseMeshName: 'POINTING_POSE'
             };
             _this.onTrackpadChangedObservable = new BABYLON.Observable();
+            _this.onTrackpadValuesChangedObservable = new BABYLON.Observable();
+            _this.trackpad = { x: 0, y: 0 };
             _this.controllerType = BABYLON.PoseEnabledControllerType.WINDOWS;
             _this._loadedMeshInfo = null;
             return _this;
@@ -57483,6 +57478,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(WindowsMotionController.prototype, "onTouchpadValuesChangedObservable", {
+            get: function () {
+                return this.onTrackpadValuesChangedObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Called once per frame by the engine.
          */
@@ -57491,6 +57493,11 @@ var BABYLON;
             // Only need to animate axes if there is a loaded mesh
             if (this._loadedMeshInfo) {
                 if (this.browserGamepad.axes) {
+                    if (this.browserGamepad.axes[2] != this.trackpad.x || this.browserGamepad.axes[3] != this.trackpad.y) {
+                        this.trackpad.x = this.browserGamepad["axes"][2];
+                        this.trackpad.y = this.browserGamepad["axes"][3];
+                        this.onTrackpadValuesChangedObservable.notifyObservers(this.trackpad);
+                    }
                     for (var axis = 0; axis < this._mapping.axisMeshNames.length; axis++) {
                         this.lerpAxisTransform(axis, this.browserGamepad.axes[axis]);
                     }
@@ -72049,14 +72056,14 @@ var BABYLON;
             }
             // Exiting VR mode using 'ESC' key on desktop
             this._onKeyDown = function (event) {
-                if (event.keyCode === 27 && _this.isInVRMode()) {
+                if (event.keyCode === 27 && _this.isInVRMode) {
                     _this.exitVR();
                 }
             };
             document.addEventListener("keydown", this._onKeyDown);
             // Exiting VR mode double tapping the touch screen
             this._scene.onPrePointerObservable.add(function (pointerInfo, eventState) {
-                if (_this.isInVRMode()) {
+                if (_this.isInVRMode) {
                     _this.exitVR();
                     if (_this._fullscreenVRpresenting) {
                         _this._scene.getEngine().switchFullscreen(true);
@@ -72204,9 +72211,16 @@ var BABYLON;
                 }
             }
         };
-        VRExperienceHelper.prototype.isInVRMode = function () {
-            return this._webVRpresenting || this._fullscreenVRpresenting;
-        };
+        Object.defineProperty(VRExperienceHelper.prototype, "isInVRMode", {
+            /**
+             * Gets a value indicating if we are currently in VR mode.
+             */
+            get: function () {
+                return this._webVRpresenting || this._fullscreenVRpresenting;
+            },
+            enumerable: true,
+            configurable: true
+        });
         VRExperienceHelper.prototype.onVrDisplayPresentChange = function () {
             var vrDisplay = this._scene.getEngine().getVRDevice();
             if (vrDisplay) {
@@ -72232,7 +72246,7 @@ var BABYLON;
                 return;
             }
             this._btnVR.className = "babylonVRicon";
-            if (this.isInVRMode()) {
+            if (this.isInVRMode) {
                 this._btnVR.className += " vrdisplaypresenting";
             }
             else {
@@ -73016,7 +73030,7 @@ var BABYLON;
             }
         };
         VRExperienceHelper.prototype.dispose = function () {
-            if (this.isInVRMode()) {
+            if (this.isInVRMode) {
                 this.exitVR();
             }
             if (this._deviceOrientationCamera) {
@@ -80457,10 +80471,7 @@ var BABYLON;
             if (!node.babylonNode) {
                 return newMesh;
             }
-            var multiMat = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
-            if (!newMesh.material) {
-                newMesh.material = multiMat;
-            }
+            var subMaterials = [];
             var vertexData = new BABYLON.VertexData();
             var geometry = new BABYLON.Geometry(id, gltfRuntime.scene, vertexData, false, newMesh);
             var verticesStarts = new Array();
@@ -80548,13 +80559,27 @@ var BABYLON;
                     }
                     vertexData.merge(tempVertexData);
                     // Sub material
-                    var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-                    multiMat.subMaterials.push(material === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
+                    var material_1 = gltfRuntime.scene.getMaterialByID(primitive.material);
+                    subMaterials.push(material_1 === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material_1);
                     // Update vertices start and index start
                     verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
                     indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
                 }
             }
+            var material;
+            if (subMaterials.length > 1) {
+                material = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
+                material.subMaterials = subMaterials;
+            }
+            else {
+                material = new BABYLON.StandardMaterial("multimat" + id, gltfRuntime.scene);
+            }
+            if (subMaterials.length === 1) {
+                material = subMaterials[0];
+            }
+            if (!newMesh.material) {
+                newMesh.material = material;
+            }
             // Apply geometry
             geometry.setAllVerticesData(vertexData, false);
             newMesh.computeWorldMatrix(true);
@@ -82420,32 +82445,52 @@ var BABYLON;
                     }
                     ;
                 });
-                var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
-                node.babylonMesh.material = multiMaterial;
-                var subMaterials = multiMaterial.subMaterials;
-                var _loop_1 = function (index) {
-                    var primitive = primitives[index];
+                if (primitives.length === 1) {
+                    var primitive = primitives[0];
                     if (primitive.material == null) {
-                        subMaterials[index] = this_1._getDefaultMaterial();
+                        node.babylonMesh.material = this._getDefaultMaterial();
                     }
                     else {
-                        var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                        var material = GLTFLoader._GetProperty(this._gltf.materials, primitive.material);
                         if (!material) {
                             throw new Error(context + ": Failed to find material " + primitive.material);
                         }
-                        this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                        this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                             if (isNew && _this._parent.onMaterialLoaded) {
                                 _this._parent.onMaterialLoaded(babylonMaterial);
                             }
-                            subMaterials[index] = babylonMaterial;
+                            node.babylonMesh.material = babylonMaterial;
                         });
                     }
-                };
-                var this_1 = this;
-                for (var index = 0; index < primitives.length; index++) {
-                    _loop_1(index);
                 }
-                ;
+                else {
+                    var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                    node.babylonMesh.material = multiMaterial;
+                    var subMaterials_1 = multiMaterial.subMaterials;
+                    var _loop_1 = function (index) {
+                        var primitive = primitives[index];
+                        if (primitive.material == null) {
+                            subMaterials_1[index] = this_1._getDefaultMaterial();
+                        }
+                        else {
+                            var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                            if (!material) {
+                                throw new Error(context + ": Failed to find material " + primitive.material);
+                            }
+                            this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                                if (isNew && _this._parent.onMaterialLoaded) {
+                                    _this._parent.onMaterialLoaded(babylonMaterial);
+                                }
+                                subMaterials_1[index] = babylonMaterial;
+                            });
+                        }
+                    };
+                    var this_1 = this;
+                    for (var index = 0; index < primitives.length; index++) {
+                        _loop_1(index);
+                    }
+                    ;
+                }
             };
             GLTFLoader.prototype._loadAllVertexDataAsync = function (context, mesh, onSuccess) {
                 var primitives = mesh.primitives;
@@ -83563,6 +83608,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -83582,6 +83630,13 @@ var BABYLON;
                             }
                         }
                     }
+                    else if (mesh.material !== null) {
+                        this._compileMaterialAsync(mesh.material, mesh, function () {
+                            if (--remaining === 0) {
+                                onSuccess();
+                            }
+                        });
+                    }
                 }
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {

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

@@ -678,6 +678,7 @@ declare module BABYLON.GUI {
         placeholderColor: string;
         placeholderText: string;
         text: string;
+        width: string | number;
         constructor(name?: string | undefined, text?: string);
         onBlur(): void;
         onFocus(): void;

+ 19 - 3
dist/preview release/gui/babylon.gui.js

@@ -1370,7 +1370,7 @@ var BABYLON;
                 this.notRenderable = false;
             };
             Control.prototype.linkWithMesh = function (mesh) {
-                if (!this._host || this._root !== this._host._rootContainer) {
+                if (!this._host || this._root && this._root !== this._host._rootContainer) {
                     BABYLON.Tools.Error("Cannot link a control to a mesh if the control is not at root level");
                     return;
                 }
@@ -2777,10 +2777,10 @@ var BABYLON;
                         context.shadowOffsetY = this.shadowOffsetY;
                     }
                     if (this._thumbWidth.isPixel) {
-                        effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.height);
+                        effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.width);
                     }
                     else {
-                        effectiveThumbWidth = this._currentMeasure.height * this._thumbWidth.getValue(this._host);
+                        effectiveThumbWidth = this._currentMeasure.width * this._thumbWidth.getValue(this._host);
                     }
                     if (this._barOffset.isPixel) {
                         effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), this._currentMeasure.height);
@@ -4329,6 +4329,22 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            Object.defineProperty(InputText.prototype, "width", {
+                get: function () {
+                    return this._width.toString(this._host);
+                },
+                set: function (value) {
+                    if (this._width.toString(this._host) === value) {
+                        return;
+                    }
+                    if (this._width.fromString(value)) {
+                        this._markAsDirty();
+                    }
+                    this.autoStretchWidth = false;
+                },
+                enumerable: true,
+                configurable: true
+            });
             InputText.prototype.onBlur = function () {
                 this._isFocused = false;
                 this._scrollLeft = null;

Plik diff jest za duży
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


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

@@ -683,6 +683,7 @@ declare module BABYLON.GUI {
         placeholderColor: string;
         placeholderText: string;
         text: string;
+        width: string | number;
         constructor(name?: string | undefined, text?: string);
         onBlur(): void;
         onFocus(): void;

+ 17 - 6
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -830,10 +830,7 @@ var BABYLON;
             if (!node.babylonNode) {
                 return newMesh;
             }
-            var multiMat = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
-            if (!newMesh.material) {
-                newMesh.material = multiMat;
-            }
+            var subMaterials = [];
             var vertexData = new BABYLON.VertexData();
             var geometry = new BABYLON.Geometry(id, gltfRuntime.scene, vertexData, false, newMesh);
             var verticesStarts = new Array();
@@ -921,13 +918,27 @@ var BABYLON;
                     }
                     vertexData.merge(tempVertexData);
                     // Sub material
-                    var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-                    multiMat.subMaterials.push(material === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
+                    var material_1 = gltfRuntime.scene.getMaterialByID(primitive.material);
+                    subMaterials.push(material_1 === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material_1);
                     // Update vertices start and index start
                     verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
                     indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
                 }
             }
+            var material;
+            if (subMaterials.length > 1) {
+                material = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
+                material.subMaterials = subMaterials;
+            }
+            else {
+                material = new BABYLON.StandardMaterial("multimat" + id, gltfRuntime.scene);
+            }
+            if (subMaterials.length === 1) {
+                material = subMaterials[0];
+            }
+            if (!newMesh.material) {
+                newMesh.material = material;
+            }
             // Apply geometry
             geometry.setAllVerticesData(vertexData, false);
             newMesh.computeWorldMatrix(true);

Plik diff jest za duży
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 44 - 14
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -666,32 +666,52 @@ var BABYLON;
                     }
                     ;
                 });
-                var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
-                node.babylonMesh.material = multiMaterial;
-                var subMaterials = multiMaterial.subMaterials;
-                var _loop_1 = function (index) {
-                    var primitive = primitives[index];
+                if (primitives.length === 1) {
+                    var primitive = primitives[0];
                     if (primitive.material == null) {
-                        subMaterials[index] = this_1._getDefaultMaterial();
+                        node.babylonMesh.material = this._getDefaultMaterial();
                     }
                     else {
-                        var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                        var material = GLTFLoader._GetProperty(this._gltf.materials, primitive.material);
                         if (!material) {
                             throw new Error(context + ": Failed to find material " + primitive.material);
                         }
-                        this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                        this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                             if (isNew && _this._parent.onMaterialLoaded) {
                                 _this._parent.onMaterialLoaded(babylonMaterial);
                             }
-                            subMaterials[index] = babylonMaterial;
+                            node.babylonMesh.material = babylonMaterial;
                         });
                     }
-                };
-                var this_1 = this;
-                for (var index = 0; index < primitives.length; index++) {
-                    _loop_1(index);
                 }
-                ;
+                else {
+                    var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                    node.babylonMesh.material = multiMaterial;
+                    var subMaterials_1 = multiMaterial.subMaterials;
+                    var _loop_1 = function (index) {
+                        var primitive = primitives[index];
+                        if (primitive.material == null) {
+                            subMaterials_1[index] = this_1._getDefaultMaterial();
+                        }
+                        else {
+                            var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                            if (!material) {
+                                throw new Error(context + ": Failed to find material " + primitive.material);
+                            }
+                            this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                                if (isNew && _this._parent.onMaterialLoaded) {
+                                    _this._parent.onMaterialLoaded(babylonMaterial);
+                                }
+                                subMaterials_1[index] = babylonMaterial;
+                            });
+                        }
+                    };
+                    var this_1 = this;
+                    for (var index = 0; index < primitives.length; index++) {
+                        _loop_1(index);
+                    }
+                    ;
+                }
             };
             GLTFLoader.prototype._loadAllVertexDataAsync = function (context, mesh, onSuccess) {
                 var primitives = mesh.primitives;
@@ -1809,6 +1829,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -1828,6 +1851,13 @@ var BABYLON;
                             }
                         }
                     }
+                    else if (mesh.material !== null) {
+                        this._compileMaterialAsync(mesh.material, mesh, function () {
+                            if (--remaining === 0) {
+                                onSuccess();
+                            }
+                        });
+                    }
                 }
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {

Plik diff jest za duży
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 61 - 20
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -830,10 +830,7 @@ var BABYLON;
             if (!node.babylonNode) {
                 return newMesh;
             }
-            var multiMat = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
-            if (!newMesh.material) {
-                newMesh.material = multiMat;
-            }
+            var subMaterials = [];
             var vertexData = new BABYLON.VertexData();
             var geometry = new BABYLON.Geometry(id, gltfRuntime.scene, vertexData, false, newMesh);
             var verticesStarts = new Array();
@@ -921,13 +918,27 @@ var BABYLON;
                     }
                     vertexData.merge(tempVertexData);
                     // Sub material
-                    var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-                    multiMat.subMaterials.push(material === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
+                    var material_1 = gltfRuntime.scene.getMaterialByID(primitive.material);
+                    subMaterials.push(material_1 === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material_1);
                     // Update vertices start and index start
                     verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
                     indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
                 }
             }
+            var material;
+            if (subMaterials.length > 1) {
+                material = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
+                material.subMaterials = subMaterials;
+            }
+            else {
+                material = new BABYLON.StandardMaterial("multimat" + id, gltfRuntime.scene);
+            }
+            if (subMaterials.length === 1) {
+                material = subMaterials[0];
+            }
+            if (!newMesh.material) {
+                newMesh.material = material;
+            }
             // Apply geometry
             geometry.setAllVerticesData(vertexData, false);
             newMesh.computeWorldMatrix(true);
@@ -2811,32 +2822,52 @@ var BABYLON;
                     }
                     ;
                 });
-                var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
-                node.babylonMesh.material = multiMaterial;
-                var subMaterials = multiMaterial.subMaterials;
-                var _loop_1 = function (index) {
-                    var primitive = primitives[index];
+                if (primitives.length === 1) {
+                    var primitive = primitives[0];
                     if (primitive.material == null) {
-                        subMaterials[index] = this_1._getDefaultMaterial();
+                        node.babylonMesh.material = this._getDefaultMaterial();
                     }
                     else {
-                        var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                        var material = GLTFLoader._GetProperty(this._gltf.materials, primitive.material);
                         if (!material) {
                             throw new Error(context + ": Failed to find material " + primitive.material);
                         }
-                        this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                        this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                             if (isNew && _this._parent.onMaterialLoaded) {
                                 _this._parent.onMaterialLoaded(babylonMaterial);
                             }
-                            subMaterials[index] = babylonMaterial;
+                            node.babylonMesh.material = babylonMaterial;
                         });
                     }
-                };
-                var this_1 = this;
-                for (var index = 0; index < primitives.length; index++) {
-                    _loop_1(index);
                 }
-                ;
+                else {
+                    var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                    node.babylonMesh.material = multiMaterial;
+                    var subMaterials_1 = multiMaterial.subMaterials;
+                    var _loop_1 = function (index) {
+                        var primitive = primitives[index];
+                        if (primitive.material == null) {
+                            subMaterials_1[index] = this_1._getDefaultMaterial();
+                        }
+                        else {
+                            var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                            if (!material) {
+                                throw new Error(context + ": Failed to find material " + primitive.material);
+                            }
+                            this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                                if (isNew && _this._parent.onMaterialLoaded) {
+                                    _this._parent.onMaterialLoaded(babylonMaterial);
+                                }
+                                subMaterials_1[index] = babylonMaterial;
+                            });
+                        }
+                    };
+                    var this_1 = this;
+                    for (var index = 0; index < primitives.length; index++) {
+                        _loop_1(index);
+                    }
+                    ;
+                }
             };
             GLTFLoader.prototype._loadAllVertexDataAsync = function (context, mesh, onSuccess) {
                 var primitives = mesh.primitives;
@@ -3954,6 +3985,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -3973,6 +4007,13 @@ var BABYLON;
                             }
                         }
                     }
+                    else if (mesh.material !== null) {
+                        this._compileMaterialAsync(mesh.material, mesh, function () {
+                            if (--remaining === 0) {
+                                onSuccess();
+                            }
+                        });
+                    }
                 }
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {

Plik diff jest za duży
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 61 - 20
dist/preview release/loaders/babylonjs.loaders.js

@@ -1804,10 +1804,7 @@ var BABYLON;
             if (!node.babylonNode) {
                 return newMesh;
             }
-            var multiMat = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
-            if (!newMesh.material) {
-                newMesh.material = multiMat;
-            }
+            var subMaterials = [];
             var vertexData = new BABYLON.VertexData();
             var geometry = new BABYLON.Geometry(id, gltfRuntime.scene, vertexData, false, newMesh);
             var verticesStarts = new Array();
@@ -1895,13 +1892,27 @@ var BABYLON;
                     }
                     vertexData.merge(tempVertexData);
                     // Sub material
-                    var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-                    multiMat.subMaterials.push(material === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
+                    var material_1 = gltfRuntime.scene.getMaterialByID(primitive.material);
+                    subMaterials.push(material_1 === null ? GLTF1.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material_1);
                     // Update vertices start and index start
                     verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
                     indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
                 }
             }
+            var material;
+            if (subMaterials.length > 1) {
+                material = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
+                material.subMaterials = subMaterials;
+            }
+            else {
+                material = new BABYLON.StandardMaterial("multimat" + id, gltfRuntime.scene);
+            }
+            if (subMaterials.length === 1) {
+                material = subMaterials[0];
+            }
+            if (!newMesh.material) {
+                newMesh.material = material;
+            }
             // Apply geometry
             geometry.setAllVerticesData(vertexData, false);
             newMesh.computeWorldMatrix(true);
@@ -3767,32 +3778,52 @@ var BABYLON;
                     }
                     ;
                 });
-                var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
-                node.babylonMesh.material = multiMaterial;
-                var subMaterials = multiMaterial.subMaterials;
-                var _loop_1 = function (index) {
-                    var primitive = primitives[index];
+                if (primitives.length === 1) {
+                    var primitive = primitives[0];
                     if (primitive.material == null) {
-                        subMaterials[index] = this_1._getDefaultMaterial();
+                        node.babylonMesh.material = this._getDefaultMaterial();
                     }
                     else {
-                        var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                        var material = GLTFLoader._GetProperty(this._gltf.materials, primitive.material);
                         if (!material) {
                             throw new Error(context + ": Failed to find material " + primitive.material);
                         }
-                        this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                        this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                             if (isNew && _this._parent.onMaterialLoaded) {
                                 _this._parent.onMaterialLoaded(babylonMaterial);
                             }
-                            subMaterials[index] = babylonMaterial;
+                            node.babylonMesh.material = babylonMaterial;
                         });
                     }
-                };
-                var this_1 = this;
-                for (var index = 0; index < primitives.length; index++) {
-                    _loop_1(index);
                 }
-                ;
+                else {
+                    var multiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                    node.babylonMesh.material = multiMaterial;
+                    var subMaterials_1 = multiMaterial.subMaterials;
+                    var _loop_1 = function (index) {
+                        var primitive = primitives[index];
+                        if (primitive.material == null) {
+                            subMaterials_1[index] = this_1._getDefaultMaterial();
+                        }
+                        else {
+                            var material = GLTFLoader._GetProperty(this_1._gltf.materials, primitive.material);
+                            if (!material) {
+                                throw new Error(context + ": Failed to find material " + primitive.material);
+                            }
+                            this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
+                                if (isNew && _this._parent.onMaterialLoaded) {
+                                    _this._parent.onMaterialLoaded(babylonMaterial);
+                                }
+                                subMaterials_1[index] = babylonMaterial;
+                            });
+                        }
+                    };
+                    var this_1 = this;
+                    for (var index = 0; index < primitives.length; index++) {
+                        _loop_1(index);
+                    }
+                    ;
+                }
             };
             GLTFLoader.prototype._loadAllVertexDataAsync = function (context, mesh, onSuccess) {
                 var primitives = mesh.primitives;
@@ -4910,6 +4941,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -4929,6 +4963,13 @@ var BABYLON;
                             }
                         }
                     }
+                    else if (mesh.material !== null) {
+                        this._compileMaterialAsync(mesh.material, mesh, function () {
+                            if (--remaining === 0) {
+                                onSuccess();
+                            }
+                        });
+                    }
                 }
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {

Plik diff jest za duży
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 3 - 2
dist/preview release/materialsLibrary/babylon.waterMaterial.d.ts

@@ -75,13 +75,14 @@ declare module BABYLON {
         private _lastDeltaTime;
         private _renderId;
         private _useLogarithmicDepth;
+        private _waitingRenderList;
         /**
         * Constructor
         */
         constructor(name: string, scene: Scene, renderTargetSize?: Vector2);
         useLogarithmicDepth: boolean;
-        readonly refractionTexture: RenderTargetTexture;
-        readonly reflectionTexture: RenderTargetTexture;
+        readonly refractionTexture: Nullable<RenderTargetTexture>;
+        readonly reflectionTexture: Nullable<RenderTargetTexture>;
         addToRenderList(node: any): void;
         enableRenderTargets(enable: boolean): void;
         getRenderList(): Nullable<AbstractMesh[]>;

+ 26 - 9
dist/preview release/materialsLibrary/babylon.waterMaterial.js

@@ -158,24 +158,28 @@ var BABYLON;
         });
         // Methods
         WaterMaterial.prototype.addToRenderList = function (node) {
-            if (this._refractionRTT.renderList) {
+            if (this._refractionRTT && this._refractionRTT.renderList) {
                 this._refractionRTT.renderList.push(node);
             }
-            if (this._reflectionRTT.renderList) {
+            if (this._reflectionRTT && this._reflectionRTT.renderList) {
                 this._reflectionRTT.renderList.push(node);
             }
         };
         WaterMaterial.prototype.enableRenderTargets = function (enable) {
             var refreshRate = enable ? 1 : 0;
-            this._refractionRTT.refreshRate = refreshRate;
-            this._reflectionRTT.refreshRate = refreshRate;
+            if (this._refractionRTT) {
+                this._refractionRTT.refreshRate = refreshRate;
+            }
+            if (this._reflectionRTT) {
+                this._reflectionRTT.refreshRate = refreshRate;
+            }
         };
         WaterMaterial.prototype.getRenderList = function () {
-            return this._refractionRTT.renderList;
+            return this._refractionRTT ? this._refractionRTT.renderList : [];
         };
         Object.defineProperty(WaterMaterial.prototype, "renderTargetsEnabled", {
             get: function () {
-                return !(this._refractionRTT.refreshRate === 0);
+                return !(this._refractionRTT && this._refractionRTT.refreshRate === 0);
             },
             enumerable: true,
             configurable: true
@@ -241,7 +245,14 @@ var BABYLON;
             defines._needNormals = BABYLON.MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, true, this._maxSimultaneousLights, this._disableLighting);
             // Attribs
             BABYLON.MaterialHelper.PrepareDefinesForAttributes(mesh, defines, true, true);
+            // Configure this
             this._mesh = mesh;
+            if (this._waitingRenderList) {
+                for (var i = 0; i < this._waitingRenderList.length; i++) {
+                    this.addToRenderList(scene.getNodeByID(this._waitingRenderList[i]));
+                }
+                this._waitingRenderList = null;
+            }
             // Get correct effect      
             if (defines.isDirty) {
                 defines.markAsProcessed();
@@ -508,8 +519,12 @@ var BABYLON;
         WaterMaterial.prototype.serialize = function () {
             var serializationObject = BABYLON.SerializationHelper.Serialize(this);
             serializationObject.customType = "BABYLON.WaterMaterial";
-            serializationObject.reflectionTexture.isRenderTarget = true;
-            serializationObject.refractionTexture.isRenderTarget = true;
+            serializationObject.renderList = [];
+            if (this._refractionRTT && this._refractionRTT.renderList) {
+                for (var i = 0; i < this._refractionRTT.renderList.length; i++) {
+                    serializationObject.renderList.push(this._refractionRTT.renderList[i].id);
+                }
+            }
             return serializationObject;
         };
         WaterMaterial.prototype.getClassName = function () {
@@ -517,7 +532,9 @@ var BABYLON;
         };
         // Statics
         WaterMaterial.Parse = function (source, scene, rootUrl) {
-            return BABYLON.SerializationHelper.Parse(function () { return new WaterMaterial(source.name, scene); }, source, scene, rootUrl);
+            var mat = BABYLON.SerializationHelper.Parse(function () { return new WaterMaterial(source.name, scene); }, source, scene, rootUrl);
+            mat._waitingRenderList = source.renderList;
+            return mat;
         };
         WaterMaterial.CreateDefaultMesh = function (name, scene) {
             var mesh = BABYLON.Mesh.CreateGround(name, 512, 512, 32, scene, false);

Plik diff jest za duży
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


+ 26 - 9
dist/preview release/materialsLibrary/babylonjs.materials.js

@@ -1572,24 +1572,28 @@ var BABYLON;
         });
         // Methods
         WaterMaterial.prototype.addToRenderList = function (node) {
-            if (this._refractionRTT.renderList) {
+            if (this._refractionRTT && this._refractionRTT.renderList) {
                 this._refractionRTT.renderList.push(node);
             }
-            if (this._reflectionRTT.renderList) {
+            if (this._reflectionRTT && this._reflectionRTT.renderList) {
                 this._reflectionRTT.renderList.push(node);
             }
         };
         WaterMaterial.prototype.enableRenderTargets = function (enable) {
             var refreshRate = enable ? 1 : 0;
-            this._refractionRTT.refreshRate = refreshRate;
-            this._reflectionRTT.refreshRate = refreshRate;
+            if (this._refractionRTT) {
+                this._refractionRTT.refreshRate = refreshRate;
+            }
+            if (this._reflectionRTT) {
+                this._reflectionRTT.refreshRate = refreshRate;
+            }
         };
         WaterMaterial.prototype.getRenderList = function () {
-            return this._refractionRTT.renderList;
+            return this._refractionRTT ? this._refractionRTT.renderList : [];
         };
         Object.defineProperty(WaterMaterial.prototype, "renderTargetsEnabled", {
             get: function () {
-                return !(this._refractionRTT.refreshRate === 0);
+                return !(this._refractionRTT && this._refractionRTT.refreshRate === 0);
             },
             enumerable: true,
             configurable: true
@@ -1655,7 +1659,14 @@ var BABYLON;
             defines._needNormals = BABYLON.MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, true, this._maxSimultaneousLights, this._disableLighting);
             // Attribs
             BABYLON.MaterialHelper.PrepareDefinesForAttributes(mesh, defines, true, true);
+            // Configure this
             this._mesh = mesh;
+            if (this._waitingRenderList) {
+                for (var i = 0; i < this._waitingRenderList.length; i++) {
+                    this.addToRenderList(scene.getNodeByID(this._waitingRenderList[i]));
+                }
+                this._waitingRenderList = null;
+            }
             // Get correct effect      
             if (defines.isDirty) {
                 defines.markAsProcessed();
@@ -1922,8 +1933,12 @@ var BABYLON;
         WaterMaterial.prototype.serialize = function () {
             var serializationObject = BABYLON.SerializationHelper.Serialize(this);
             serializationObject.customType = "BABYLON.WaterMaterial";
-            serializationObject.reflectionTexture.isRenderTarget = true;
-            serializationObject.refractionTexture.isRenderTarget = true;
+            serializationObject.renderList = [];
+            if (this._refractionRTT && this._refractionRTT.renderList) {
+                for (var i = 0; i < this._refractionRTT.renderList.length; i++) {
+                    serializationObject.renderList.push(this._refractionRTT.renderList[i].id);
+                }
+            }
             return serializationObject;
         };
         WaterMaterial.prototype.getClassName = function () {
@@ -1931,7 +1946,9 @@ var BABYLON;
         };
         // Statics
         WaterMaterial.Parse = function (source, scene, rootUrl) {
-            return BABYLON.SerializationHelper.Parse(function () { return new WaterMaterial(source.name, scene); }, source, scene, rootUrl);
+            var mat = BABYLON.SerializationHelper.Parse(function () { return new WaterMaterial(source.name, scene); }, source, scene, rootUrl);
+            mat._waitingRenderList = source.renderList;
+            return mat;
         };
         WaterMaterial.CreateDefaultMesh = function (name, scene) {
             var mesh = BABYLON.Mesh.CreateGround(name, 512, 512, 32, scene, false);

Plik diff jest za duży
+ 1 - 1
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 3 - 2
dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts

@@ -220,13 +220,14 @@ declare module BABYLON {
         private _lastDeltaTime;
         private _renderId;
         private _useLogarithmicDepth;
+        private _waitingRenderList;
         /**
         * Constructor
         */
         constructor(name: string, scene: Scene, renderTargetSize?: Vector2);
         useLogarithmicDepth: boolean;
-        readonly refractionTexture: RenderTargetTexture;
-        readonly reflectionTexture: RenderTargetTexture;
+        readonly refractionTexture: Nullable<RenderTargetTexture>;
+        readonly reflectionTexture: Nullable<RenderTargetTexture>;
         addToRenderList(node: any): void;
         enableRenderTargets(enable: boolean): void;
         getRenderList(): Nullable<AbstractMesh[]>;

Plik diff jest za duży
+ 64 - 64
dist/preview release/viewer/babylon.viewer.js


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

@@ -1,6 +1,7 @@
 # 3.1.0:
 
 ## Major updates
+- Added VRExperienceHelper to create WebVR experience with 2 lines of code. [Doc here](http://doc.babylonjs.com/how_to/webvr_helper) ([davrous](https://github.com/davrous), [TrevorDev](https://github.com/TrevorDev))
 - Viewer (TODO)
 - Added BackgroundMaterial [Doc here](https://doc.babylonjs.com/how_to/backgroundmaterial)
 - Added EnvironmentHelper [Doc here](https://doc.babylonjs.com/babylon101/environment#skybox-and-ground)

+ 131 - 131
gui/src/controls/control.ts

@@ -1,9 +1,9 @@
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
 
 module BABYLON.GUI {
-    export class Control {       
-        private _alpha = 1; 
-        private _alphaSet = false; 
+    export class Control {
+        private _alpha = 1;
+        private _alphaSet = false;
         private _zIndex = 0;
         public _root: Nullable<Container>;
         public _host: AdvancedDynamicTexture;
@@ -15,7 +15,7 @@ module BABYLON.GUI {
         private _font: string;
         public _width = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
         public _height = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
-        protected _fontOffset: {ascent: number, height: number, descent: number};
+        protected _fontOffset: { ascent: number, height: number, descent: number };
         private _color = "";
         protected _horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         protected _verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
@@ -24,7 +24,7 @@ module BABYLON.GUI {
         private _paddingLeft = new ValueAndUnit(0);
         private _paddingRight = new ValueAndUnit(0);
         private _paddingTop = new ValueAndUnit(0);
-        private _paddingBottom = new ValueAndUnit(0);        
+        private _paddingBottom = new ValueAndUnit(0);
         public _left = new ValueAndUnit(0);
         public _top = new ValueAndUnit(0);
         private _scaleX = 1.0;
@@ -49,15 +49,15 @@ module BABYLON.GUI {
         public isHitTestVisible = true;
         public isPointerBlocker = false;
         public isFocusInvisible = false;
- 
+
         public shadowOffsetX = 0;
         public shadowOffsetY = 0;
         public shadowBlur = 0;
-        public shadowColor = '#000'; 
+        public shadowColor = '#000';
 
         protected _linkOffsetX = new ValueAndUnit(0);
         protected _linkOffsetY = new ValueAndUnit(0);
-        
+
         // Properties
 
         public get typeName(): string {
@@ -74,37 +74,37 @@ module BABYLON.GUI {
         * An event triggered when the pointer move out of the control.
         * @type {BABYLON.Observable}
         */
-        public onPointerOutObservable = new Observable<Control>();        
+        public onPointerOutObservable = new Observable<Control>();
 
         /**
         * An event triggered when the pointer taps the control
         * @type {BABYLON.Observable}
         */
-        public onPointerDownObservable = new Observable<Vector2WithInfo>();     
+        public onPointerDownObservable = new Observable<Vector2WithInfo>();
 
         /**
         * An event triggered when pointer up
         * @type {BABYLON.Observable}
         */
-        public onPointerUpObservable = new Observable<Vector2WithInfo>();     
+        public onPointerUpObservable = new Observable<Vector2WithInfo>();
 
         /**
         * An event triggered when pointer enters the control
         * @type {BABYLON.Observable}
         */
-        public onPointerEnterObservable = new Observable<Control>();    
+        public onPointerEnterObservable = new Observable<Control>();
 
         /**
         * An event triggered when the control is marked as dirty
         * @type {BABYLON.Observable}
         */
-        public onDirtyObservable = new Observable<Control>();         
-        
-         /**
-        * An event triggered after the control is drawn
-        * @type {BABYLON.Observable}
-        */
-        public onAfterDrawObservable = new Observable<Control>();    
+        public onDirtyObservable = new Observable<Control>();
+
+        /**
+       * An event triggered after the control is drawn
+       * @type {BABYLON.Observable}
+       */
+        public onAfterDrawObservable = new Observable<Control>();
 
         public get alpha(): number {
             return this._alpha;
@@ -117,7 +117,7 @@ module BABYLON.GUI {
             this._alphaSet = true;
             this._alpha = value;
             this._markAsDirty();
-        }                 
+        }
 
         public get scaleX(): number {
             return this._scaleX;
@@ -131,7 +131,7 @@ module BABYLON.GUI {
             this._scaleX = value;
             this._markAsDirty();
             this._markMatrixAsDirty();
-        }     
+        }
 
         public get scaleY(): number {
             return this._scaleY;
@@ -145,7 +145,7 @@ module BABYLON.GUI {
             this._scaleY = value;
             this._markAsDirty();
             this._markMatrixAsDirty();
-        }  
+        }
 
         public get rotation(): number {
             return this._rotation;
@@ -159,7 +159,7 @@ module BABYLON.GUI {
             this._rotation = value;
             this._markAsDirty();
             this._markMatrixAsDirty();
-        }    
+        }
 
         public get transformCenterY(): number {
             return this._transformCenterY;
@@ -173,7 +173,7 @@ module BABYLON.GUI {
             this._transformCenterY = value;
             this._markAsDirty();
             this._markMatrixAsDirty();
-        }     
+        }
 
         public get transformCenterX(): number {
             return this._transformCenterX;
@@ -187,7 +187,7 @@ module BABYLON.GUI {
             this._transformCenterX = value;
             this._markAsDirty();
             this._markMatrixAsDirty();
-        }    
+        }
 
         public get horizontalAlignment(): number {
             return this._horizontalAlignment;
@@ -200,7 +200,7 @@ module BABYLON.GUI {
 
             this._horizontalAlignment = value;
             this._markAsDirty();
-        } 
+        }
 
         public get verticalAlignment(): number {
             return this._verticalAlignment;
@@ -213,17 +213,17 @@ module BABYLON.GUI {
 
             this._verticalAlignment = value;
             this._markAsDirty();
-        } 
+        }
 
         public get width(): string | number {
             return this._width.toString(this._host);
         }
 
-        public get widthInPixels(): number  {
+        public get widthInPixels(): number {
             return this._width.getValueInPixel(this._host, this._cachedParentMeasure.width);
         }
 
-        public set width(value: string | number ) {
+        public set width(value: string | number) {
             if (this._width.toString(this._host) === value) {
                 return;
             }
@@ -233,15 +233,15 @@ module BABYLON.GUI {
             }
         }
 
-        public get height(): string | number  {
+        public get height(): string | number {
             return this._height.toString(this._host);
         }
 
-        public get heightInPixels(): number  {
+        public get heightInPixels(): number {
             return this._height.getValueInPixel(this._host, this._cachedParentMeasure.height);
         }
 
-        public set height(value: string | number ) {
+        public set height(value: string | number) {
             if (this._height.toString(this._host) === value) {
                 return;
             }
@@ -249,7 +249,7 @@ module BABYLON.GUI {
             if (this._height.fromString(value)) {
                 this._markAsDirty();
             }
-        }   
+        }
 
         public get fontFamily(): string {
             return this._fontFamily;
@@ -275,17 +275,17 @@ module BABYLON.GUI {
 
             this._fontStyle = value;
             this._fontSet = true;
-        }     
-        
-        public get fontSizeInPixels(): number  {
+        }
+
+        public get fontSizeInPixels(): number {
             return this._fontSize.getValueInPixel(this._host, 100);
         }
 
-        public get fontSize(): string | number  {
+        public get fontSize(): string | number {
             return this._fontSize.toString(this._host);
         }
 
-        public set fontSize(value: string | number ) {
+        public set fontSize(value: string | number) {
             if (this._fontSize.toString(this._host) === value) {
                 return;
             }
@@ -307,7 +307,7 @@ module BABYLON.GUI {
 
             this._color = value;
             this._markAsDirty();
-        }                       
+        }
 
         public get zIndex(): number {
             return this._zIndex;
@@ -354,126 +354,126 @@ module BABYLON.GUI {
         public get isDirty(): boolean {
             return this._isDirty;
         }
-        
-        public get paddingLeft(): string | number  {
+
+        public get paddingLeft(): string | number {
             return this._paddingLeft.toString(this._host);
         }
 
-        public get paddingLeftInPixels(): number  {
+        public get paddingLeftInPixels(): number {
             return this._paddingLeft.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }             
+        }
 
-        public set paddingLeft(value: string | number ) {
+        public set paddingLeft(value: string | number) {
             if (this._paddingLeft.fromString(value)) {
                 this._markAsDirty();
             }
-        }    
+        }
 
-        public get paddingRight(): string | number  {
+        public get paddingRight(): string | number {
             return this._paddingRight.toString(this._host);
         }
 
-        public get paddingRightInPixels(): number  {
+        public get paddingRightInPixels(): number {
             return this._paddingRight.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }         
+        }
 
-        public set paddingRight(value: string | number ) {
+        public set paddingRight(value: string | number) {
             if (this._paddingRight.fromString(value)) {
                 this._markAsDirty();
             }
         }
 
-        public get paddingTop(): string | number  {
+        public get paddingTop(): string | number {
             return this._paddingTop.toString(this._host);
         }
 
-        public get paddingTopInPixels(): number  {
+        public get paddingTopInPixels(): number {
             return this._paddingTop.getValueInPixel(this._host, this._cachedParentMeasure.height);
-        }         
+        }
 
-        public set paddingTop(value: string | number ) {
+        public set paddingTop(value: string | number) {
             if (this._paddingTop.fromString(value)) {
                 this._markAsDirty();
             }
         }
 
-        public get paddingBottom(): string | number  {
+        public get paddingBottom(): string | number {
             return this._paddingBottom.toString(this._host);
         }
 
-        public get paddingBottomInPixels(): number  {
+        public get paddingBottomInPixels(): number {
             return this._paddingBottom.getValueInPixel(this._host, this._cachedParentMeasure.height);
-        }                 
+        }
 
-        public set paddingBottom(value: string | number ) {
+        public set paddingBottom(value: string | number) {
             if (this._paddingBottom.fromString(value)) {
                 this._markAsDirty();
             }
-        }     
+        }
 
-        public get left(): string | number  {
+        public get left(): string | number {
             return this._left.toString(this._host);
         }
 
-        public get leftInPixels(): number  {
+        public get leftInPixels(): number {
             return this._left.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }          
+        }
 
-        public set left(value: string | number ) {
+        public set left(value: string | number) {
             if (this._left.fromString(value)) {
                 this._markAsDirty();
             }
-        }  
+        }
 
-        public get top(): string | number  {
+        public get top(): string | number {
             return this._top.toString(this._host);
         }
 
-        public get topInPixels(): number  {
+        public get topInPixels(): number {
             return this._top.getValueInPixel(this._host, this._cachedParentMeasure.height);
-        }        
+        }
 
-        public set top(value: string | number ) {
+        public set top(value: string | number) {
             if (this._top.fromString(value)) {
                 this._markAsDirty();
             }
-        }     
+        }
 
-        public get linkOffsetX(): string | number  {
+        public get linkOffsetX(): string | number {
             return this._linkOffsetX.toString(this._host);
         }
 
-        public get linkOffsetXInPixels(): number  {
+        public get linkOffsetXInPixels(): number {
             return this._linkOffsetX.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }        
+        }
 
-        public set linkOffsetX(value: string | number ) {
+        public set linkOffsetX(value: string | number) {
             if (this._linkOffsetX.fromString(value)) {
                 this._markAsDirty();
             }
-        }      
+        }
 
-        public get linkOffsetY(): string | number  {
+        public get linkOffsetY(): string | number {
             return this._linkOffsetY.toString(this._host);
         }
 
-        public get linkOffsetYInPixels(): number  {
+        public get linkOffsetYInPixels(): number {
             return this._linkOffsetY.getValueInPixel(this._host, this._cachedParentMeasure.height);
-        }        
+        }
 
-        public set linkOffsetY(value: string | number ) {
+        public set linkOffsetY(value: string | number) {
             if (this._linkOffsetY.fromString(value)) {
                 this._markAsDirty();
             }
-        }                
+        }
 
         public get centerX(): number {
             return this._currentMeasure.left + this._currentMeasure.width / 2;
-        }       
+        }
 
         public get centerY(): number {
             return this._currentMeasure.top + this._currentMeasure.height / 2;
-        }                   
+        }
 
         // Functions
         constructor(public name?: string) {
@@ -505,13 +505,13 @@ module BABYLON.GUI {
 
             return result;
         }
-        
+
         public moveToVector3(position: Vector3, scene: Scene): void {
             if (!this._host || this._root !== this._host._rootContainer) {
                 Tools.Error("Cannot move a control to a vector3 if the control is not at root level");
                 return;
             }
-            
+
             this.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
             this.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
 
@@ -528,7 +528,7 @@ module BABYLON.GUI {
         }
 
         public linkWithMesh(mesh: Nullable<AbstractMesh>): void {
-            if (!this._host || this._root !== this._host._rootContainer) {
+            if (!this._host || this._root && this._root !== this._host._rootContainer) {
                 Tools.Error("Cannot link a control to a mesh if the control is not at root level");
                 return;
             }
@@ -561,7 +561,7 @@ module BABYLON.GUI {
             this._markAsDirty();
         }
 
-        public _markAsDirty(): void {            
+        public _markAsDirty(): void {
             this._isDirty = true;
 
             if (!this._host) {
@@ -584,7 +584,7 @@ module BABYLON.GUI {
         }
 
         protected _transform(context: CanvasRenderingContext2D): void {
-            if (!this._isMatrixDirty && this._scaleX === 1 && this._scaleY ===1 && this._rotation === 0) {
+            if (!this._isMatrixDirty && this._scaleX === 1 && this._scaleY === 1 && this._rotation === 0) {
                 return;
             }
 
@@ -600,7 +600,7 @@ module BABYLON.GUI {
             context.scale(this._scaleX, this._scaleY);
 
             // preTranslate
-            context.translate(-offsetX, -offsetY);    
+            context.translate(-offsetX, -offsetY);
 
             // Need to update matrices?
             if (this._isMatrixDirty || this._cachedOffsetX !== offsetX || this._cachedOffsetY !== offsetY) {
@@ -633,7 +633,7 @@ module BABYLON.GUI {
             }
         }
 
-        protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {     
+        protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
             if (this._isDirty || !this._cachedParentMeasure.isEqualsTo(parentMeasure)) {
                 this._isDirty = false;
                 this._currentMeasure.copyFrom(parentMeasure);
@@ -657,8 +657,8 @@ module BABYLON.GUI {
 
                 if (this.onDirtyObservable.hasObservers()) {
                     this.onDirtyObservable.notifyObservers(this);
-                }                
-            }     
+                }
+            }
 
             if (this._currentMeasure.left > parentMeasure.left + parentMeasure.width) {
                 return false;
@@ -677,8 +677,8 @@ module BABYLON.GUI {
             }
 
             // Transform
-            this._transform(context); 
-                        
+            this._transform(context);
+
             // Clip
             this._clip(context);
             context.clip();
@@ -686,40 +686,40 @@ module BABYLON.GUI {
             return true;
         }
 
-        protected _clip( context: CanvasRenderingContext2D) {
+        protected _clip(context: CanvasRenderingContext2D) {
             context.beginPath();
-            
-            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+
+            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                 var shadowOffsetX = this.shadowOffsetX;
                 var shadowOffsetY = this.shadowOffsetY;
                 var shadowBlur = this.shadowBlur;
-                
-                var leftShadowOffset = Math.min(Math.min(shadowOffsetX, 0) - shadowBlur*2, 0);
-                var rightShadowOffset = Math.max(Math.max(shadowOffsetX, 0) + shadowBlur*2, 0);
-                var topShadowOffset = Math.min(Math.min(shadowOffsetY, 0) - shadowBlur*2, 0);
-                var bottomShadowOffset = Math.max(Math.max(shadowOffsetY, 0) + shadowBlur*2, 0);
-
-                context.rect(this._currentMeasure.left + leftShadowOffset, 
-                            this._currentMeasure.top + topShadowOffset, 
-                            this._currentMeasure.width + rightShadowOffset - leftShadowOffset, 
-                            this._currentMeasure.height + bottomShadowOffset - topShadowOffset);
+
+                var leftShadowOffset = Math.min(Math.min(shadowOffsetX, 0) - shadowBlur * 2, 0);
+                var rightShadowOffset = Math.max(Math.max(shadowOffsetX, 0) + shadowBlur * 2, 0);
+                var topShadowOffset = Math.min(Math.min(shadowOffsetY, 0) - shadowBlur * 2, 0);
+                var bottomShadowOffset = Math.max(Math.max(shadowOffsetY, 0) + shadowBlur * 2, 0);
+
+                context.rect(this._currentMeasure.left + leftShadowOffset,
+                    this._currentMeasure.top + topShadowOffset,
+                    this._currentMeasure.width + rightShadowOffset - leftShadowOffset,
+                    this._currentMeasure.height + bottomShadowOffset - topShadowOffset);
             } else {
-                context.rect(this._currentMeasure.left ,this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                context.rect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
             }
         }
 
-        public _measure(): void {  
+        public _measure(): void {
             // Width / Height
             if (this._width.isPixel) {
                 this._currentMeasure.width = this._width.getValue(this._host);
             } else {
-                this._currentMeasure.width *= this._width.getValue(this._host); 
+                this._currentMeasure.width *= this._width.getValue(this._host);
             }
 
             if (this._height.isPixel) {
                 this._currentMeasure.height = this._height.getValue(this._host);
             } else {
-                this._currentMeasure.height *= this._height.getValue(this._host); 
+                this._currentMeasure.height *= this._height.getValue(this._host);
             }
         }
 
@@ -757,7 +757,7 @@ module BABYLON.GUI {
                     y = (parentHeight - height) / 2;
                     break;
             }
-            
+
             if (this._paddingLeft.isPixel) {
                 this._currentMeasure.left += this._paddingLeft.getValue(this._host);
                 this._currentMeasure.width -= this._paddingLeft.getValue(this._host);
@@ -784,7 +784,7 @@ module BABYLON.GUI {
                 this._currentMeasure.height -= this._paddingBottom.getValue(this._host);
             } else {
                 this._currentMeasure.height -= parentHeight * this._paddingBottom.getValue(this._host);
-            }            
+            }
 
             if (this._left.isPixel) {
                 this._currentMeasure.left += this._left.getValue(this._host);
@@ -799,12 +799,12 @@ module BABYLON.GUI {
             }
 
             this._currentMeasure.left += x;
-            this._currentMeasure.top += y;            
+            this._currentMeasure.top += y;
         }
 
         protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             // Do nothing
-        }        
+        }
 
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             // Do nothing
@@ -814,7 +814,7 @@ module BABYLON.GUI {
             // Do nothing
         }
 
-        public contains(x: number, y: number) : boolean {
+        public contains(x: number, y: number): boolean {
             // Invert transform
             this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
 
@@ -836,7 +836,7 @@ module BABYLON.GUI {
 
             if (y > this._currentMeasure.top + this._currentMeasure.height) {
                 return false;
-            } 
+            }
 
             if (this.isPointerBlocker) {
                 this._host._shouldBlockPointer = true;
@@ -902,9 +902,9 @@ module BABYLON.GUI {
 
         public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
             this._downCount = 0;
-            
+
             var canNotify: boolean = this.onPointerUpObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
-            
+
             if (canNotify && this.parent != null) this.parent._onPointerUp(target, coordinates, buttonIndex);
         }
 
@@ -919,7 +919,7 @@ module BABYLON.GUI {
 
                 var previousControlOver = this._host._lastControlOver;
                 if (previousControlOver && previousControlOver !== this) {
-                    previousControlOver._onPointerOut(this);                
+                    previousControlOver._onPointerOut(this);
                 }
 
                 if (previousControlOver !== this) {
@@ -944,7 +944,7 @@ module BABYLON.GUI {
                 this._host._lastControlDown = null;
                 return true;
             }
-        
+
             return false;
         }
 
@@ -954,7 +954,7 @@ module BABYLON.GUI {
             }
 
             this._font = this._fontStyle + " " + this._fontSize.getValue(this._host) + "px " + this._fontFamily;
-        
+
             this._fontOffset = Control._GetFontOffset(this._font);
         }
 
@@ -982,7 +982,7 @@ module BABYLON.GUI {
         private static _HORIZONTAL_ALIGNMENT_LEFT = 0;
         private static _HORIZONTAL_ALIGNMENT_RIGHT = 1;
         private static _HORIZONTAL_ALIGNMENT_CENTER = 2;
-        
+
         private static _VERTICAL_ALIGNMENT_TOP = 0;
         private static _VERTICAL_ALIGNMENT_BOTTOM = 1;
         private static _VERTICAL_ALIGNMENT_CENTER = 2;
@@ -1011,9 +1011,9 @@ module BABYLON.GUI {
             return Control._VERTICAL_ALIGNMENT_CENTER;
         }
 
-        private static _FontHeightSizes: {[key: string]: {ascent: number, height: number, descent: number}} = {};
+        private static _FontHeightSizes: { [key: string]: { ascent: number, height: number, descent: number } } = {};
 
-        public static _GetFontOffset(font: string): {ascent: number, height: number, descent: number} {
+        public static _GetFontOffset(font: string): { ascent: number, height: number, descent: number } {
 
             if (Control._FontHeightSizes[font]) {
                 return Control._FontHeightSizes[font];
@@ -1051,10 +1051,10 @@ module BABYLON.GUI {
         };
 
         public static AddHeader(control: Control, text: string, size: string | number, options: { isHorizontal: boolean, controlFirst: boolean }): StackPanel {
-            let panel = new BABYLON.GUI.StackPanel("panel");        
+            let panel = new BABYLON.GUI.StackPanel("panel");
             let isHorizontal = options ? options.isHorizontal : true;
             let controlFirst = options ? options.controlFirst : true;
-            
+
             panel.isVertical = !isHorizontal;
 
             let header = new BABYLON.GUI.TextBlock("header");
@@ -1068,14 +1068,14 @@ module BABYLON.GUI {
 
             if (controlFirst) {
                 panel.addControl(control);
-                panel.addControl(header); 
+                panel.addControl(header);
                 header.paddingLeft = "5px";
             } else {
-                panel.addControl(header); 
+                panel.addControl(header);
                 panel.addControl(control);
                 header.paddingRight = "5px";
             }
-            
+
             header.shadowBlur = control.shadowBlur;
             header.shadowColor = control.shadowColor;
             header.shadowOffsetX = control.shadowOffsetX;
@@ -1084,7 +1084,7 @@ module BABYLON.GUI {
             return panel;
         }
 
-        protected static drawEllipse(x:number, y:number, width:number, height:number, context:CanvasRenderingContext2D):void{
+        protected static drawEllipse(x: number, y: number, width: number, height: number, context: CanvasRenderingContext2D): void {
             context.translate(x, y);
             context.scale(width, height);
 
@@ -1092,8 +1092,8 @@ module BABYLON.GUI {
             context.arc(0, 0, 1, 0, 2 * Math.PI);
             context.closePath();
 
-            context.scale(1/width, 1/height);
-            context.translate(-x, -y);            
+            context.scale(1 / width, 1 / height);
+            context.translate(-x, -y);
         }
-    }    
+    }
 }

+ 68 - 52
gui/src/controls/inputText.ts

@@ -4,21 +4,21 @@ module BABYLON.GUI {
     export class InputText extends Control implements IFocusableControl {
         private _text = "";
         private _placeholderText = "";
-        private _background = "#222222";   
-        private _focusedBackground = "#000000";   
-        private _placeholderColor = "gray";   
+        private _background = "#222222";
+        private _focusedBackground = "#000000";
+        private _placeholderColor = "gray";
         private _thickness = 1;
         private _margin = new ValueAndUnit(10, ValueAndUnit.UNITMODE_PIXEL);
-        private _autoStretchWidth = true;        
+        private _autoStretchWidth = true;
         private _maxWidth = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
         private _isFocused = false;
         private _blinkTimeout: number;
         private _blinkIsEven = false;
-        private _cursorOffset = 0;        
+        private _cursorOffset = 0;
         private _scrollLeft: Nullable<number>;
         private _textWidth: number;
         private _clickedCoordinate: Nullable<number>;
-        
+
         public promptMessage = "Please enter text:";
 
         public onTextChangedObservable = new Observable<InputText>();
@@ -29,11 +29,11 @@ module BABYLON.GUI {
             return this._maxWidth.toString(this._host);
         }
 
-        public get maxWidthInPixels(): number  {
+        public get maxWidthInPixels(): number {
             return this._maxWidth.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }             
+        }
 
-        public set maxWidth(value: string | number ) {
+        public set maxWidth(value: string | number) {
             if (this._maxWidth.toString(this._host) === value) {
                 return;
             }
@@ -41,15 +41,15 @@ module BABYLON.GUI {
             if (this._maxWidth.fromString(value)) {
                 this._markAsDirty();
             }
-        }        
+        }
 
         public get margin(): string {
             return this._margin.toString(this._host);
         }
 
-        public get marginInPixels(): number  {
+        public get marginInPixels(): number {
             return this._margin.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }            
+        }
 
         public set margin(value: string) {
             if (this._margin.toString(this._host) === value) {
@@ -59,8 +59,8 @@ module BABYLON.GUI {
             if (this._margin.fromString(value)) {
                 this._markAsDirty();
             }
-        }   
-        
+        }
+
         public get autoStretchWidth(): boolean {
             return this._autoStretchWidth;
         }
@@ -72,8 +72,8 @@ module BABYLON.GUI {
 
             this._autoStretchWidth = value;
             this._markAsDirty();
-        }           
-        
+        }
+
         public get thickness(): number {
             return this._thickness;
         }
@@ -85,7 +85,7 @@ module BABYLON.GUI {
 
             this._thickness = value;
             this._markAsDirty();
-        }          
+        }
 
         public get focusedBackground(): string {
             return this._focusedBackground;
@@ -98,7 +98,7 @@ module BABYLON.GUI {
 
             this._focusedBackground = value;
             this._markAsDirty();
-        }  
+        }
 
         public get background(): string {
             return this._background;
@@ -111,7 +111,7 @@ module BABYLON.GUI {
 
             this._background = value;
             this._markAsDirty();
-        }  
+        }
 
         public get placeholderColor(): string {
             return this._placeholderColor;
@@ -124,8 +124,8 @@ module BABYLON.GUI {
 
             this._placeholderColor = value;
             this._markAsDirty();
-        }          
-        
+        }
+
         public get placeholderText(): string {
             return this._placeholderText;
         }
@@ -136,7 +136,7 @@ module BABYLON.GUI {
             }
             this._placeholderText = value;
             this._markAsDirty();
-        }        
+        }
 
         public get text(): string {
             return this._text;
@@ -152,6 +152,22 @@ module BABYLON.GUI {
             this.onTextChangedObservable.notifyObservers(this);
         }
 
+        public get width(): string | number {
+            return this._width.toString(this._host);
+        }
+
+        public set width(value: string | number) {
+            if (this._width.toString(this._host) === value) {
+                return;
+            }
+
+            if (this._width.fromString(value)) {
+                this._markAsDirty();
+            }
+
+            this.autoStretchWidth = false;
+        }
+
         constructor(public name?: string, text: string = "") {
             super(name);
 
@@ -216,20 +232,20 @@ module BABYLON.GUI {
                         this.text = this._text.slice(0, deletePosition) + this._text.slice(deletePosition + 1);
                         this._cursorOffset--;
                     }
-                    return;                    
+                    return;
                 case 13: // RETURN
                     this._host.focusedControl = null;
                     return;
                 case 35: // END
                     this._cursorOffset = 0;
                     this._blinkIsEven = false;
-                    this._markAsDirty();                
+                    this._markAsDirty();
                     return;
                 case 36: // HOME
                     this._cursorOffset = this._text.length;
                     this._blinkIsEven = false;
-                    this._markAsDirty();                
-                return;
+                    this._markAsDirty();
+                    return;
                 case 37: // LEFT
                     this._cursorOffset++;
                     if (this._cursorOffset > this._text.length) {
@@ -255,16 +271,16 @@ module BABYLON.GUI {
                 (keyCode > 47 && keyCode < 58) ||       // Numbers
                 (keyCode > 64 && keyCode < 91) ||       // Letters
                 (keyCode > 185 && keyCode < 193) ||     // Special characters
-                (keyCode > 218  && keyCode < 223) ||    // Special characters
+                (keyCode > 218 && keyCode < 223) ||    // Special characters
                 (keyCode > 95 && keyCode < 112)) {      // Numpad
-                    if (this._cursorOffset === 0) {
-                        this.text += key;
-                    } else {
-                        let insertPosition = this._text.length - this._cursorOffset;
+                if (this._cursorOffset === 0) {
+                    this.text += key;
+                } else {
+                    let insertPosition = this._text.length - this._cursorOffset;
 
-                        this.text = this._text.slice(0, insertPosition) + key + this._text.slice(insertPosition);
-                    }
+                    this.text = this._text.slice(0, insertPosition) + key + this._text.slice(insertPosition);
                 }
+            }
         }
 
         public processKeyboard(evt: KeyboardEvent): void {
@@ -276,8 +292,8 @@ module BABYLON.GUI {
 
             this._applyStates(context);
             if (this._processMeasures(parentMeasure, context)) {
-                
-                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     context.shadowColor = this.shadowColor;
                     context.shadowBlur = this.shadowBlur;
                     context.shadowOffsetX = this.shadowOffsetX;
@@ -288,16 +304,16 @@ module BABYLON.GUI {
                 if (this._isFocused) {
                     if (this._focusedBackground) {
                         context.fillStyle = this._focusedBackground;
-    
+
                         context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
-                    }                        
+                    }
                 } else if (this._background) {
                     context.fillStyle = this._background;
 
                     context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                 }
-                
-                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     context.shadowBlur = 0;
                     context.shadowOffsetX = 0;
                     context.shadowOffsetY = 0;
@@ -315,7 +331,7 @@ module BABYLON.GUI {
 
                 let text = this._text;
 
-                if (!this._isFocused && !this._text && this._placeholderText) {  
+                if (!this._isFocused && !this._text && this._placeholderText) {
                     text = this._placeholderText;
 
                     if (this._placeholderColor) {
@@ -323,7 +339,7 @@ module BABYLON.GUI {
                     }
                 }
 
-                this._textWidth = context.measureText(text).width;   
+                this._textWidth = context.measureText(text).width;
                 let marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
                 if (this._autoStretchWidth) {
                     this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), this._textWidth + marginWidth) + "px";
@@ -336,7 +352,7 @@ module BABYLON.GUI {
                 context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
                 context.clip();
 
-                if (this._isFocused && this._textWidth > availableWidth) {      
+                if (this._isFocused && this._textWidth > availableWidth) {
                     let textLeft = clipTextLeft - this._textWidth + availableWidth;
                     if (!this._scrollLeft) {
                         this._scrollLeft = textLeft;
@@ -348,7 +364,7 @@ module BABYLON.GUI {
                 context.fillText(text, this._scrollLeft, this._currentMeasure.top + rootY);
 
                 // Cursor
-                if (this._isFocused) {         
+                if (this._isFocused) {
 
                     // Need to move cursor
                     if (this._clickedCoordinate) {
@@ -364,7 +380,7 @@ module BABYLON.GUI {
                             this._cursorOffset++;
                             currentSize = context.measureText(text.substr(text.length - this._cursorOffset, this._cursorOffset)).width;
 
-                        } while(currentSize < absoluteCursorPosition);
+                        } while (currentSize < absoluteCursorPosition);
 
                         // Find closest move
                         if (Math.abs(absoluteCursorPosition - currentSize) > previousDist) {
@@ -378,18 +394,18 @@ module BABYLON.GUI {
                     // Render cursor
                     if (!this._blinkIsEven) {
                         let cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
-                        let cursorOffsetWidth = context.measureText(cursorOffsetText).width;   
-                        let cursorLeft = this._scrollLeft  + this._textWidth - cursorOffsetWidth;
-    
+                        let cursorOffsetWidth = context.measureText(cursorOffsetText).width;
+                        let cursorLeft = this._scrollLeft + this._textWidth - cursorOffsetWidth;
+
                         if (cursorLeft < clipTextLeft) {
                             this._scrollLeft += (clipTextLeft - cursorLeft);
                             cursorLeft = clipTextLeft;
                             this._markAsDirty();
                         } else if (cursorLeft > clipTextLeft + availableWidth) {
-                            this._scrollLeft += (clipTextLeft  + availableWidth - cursorLeft);
+                            this._scrollLeft += (clipTextLeft + availableWidth - cursorLeft);
                             cursorLeft = clipTextLeft + availableWidth;
                             this._markAsDirty();
-                        }                   
+                        }
                         context.fillRect(cursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, 2, this._fontOffset.height);
                     }
 
@@ -409,8 +425,8 @@ module BABYLON.GUI {
                     }
                     context.lineWidth = this._thickness;
 
-                    context.strokeRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2, 
-                                        this._currentMeasure.width - this._thickness, this._currentMeasure.height - this._thickness);
+                    context.strokeRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2,
+                        this._currentMeasure.width - this._thickness, this._currentMeasure.height - this._thickness);
                 }
             }
             context.restore();
@@ -435,7 +451,7 @@ module BABYLON.GUI {
 
         public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
             super._onPointerUp(target, coordinates, buttonIndex);
-        }  
+        }
 
         public dispose() {
             super.dispose();

+ 34 - 34
gui/src/controls/slider.ts

@@ -3,10 +3,10 @@
 module BABYLON.GUI {
     export class Slider extends Control {
         private _thumbWidth = new ValueAndUnit(30, ValueAndUnit.UNITMODE_PIXEL, false);
-        private _minimum = 0; 
+        private _minimum = 0;
         private _maximum = 100;
         private _value = 50;
-        private _background = "black";   
+        private _background = "black";
         private _borderColor = "white";
         private _barOffset = new ValueAndUnit(5, ValueAndUnit.UNITMODE_PIXEL, false);
         private _isThumbCircle = false;
@@ -24,7 +24,7 @@ module BABYLON.GUI {
 
             this._borderColor = value;
             this._markAsDirty();
-        }  
+        }
 
         public get background(): string {
             return this._background;
@@ -37,17 +37,17 @@ module BABYLON.GUI {
 
             this._background = value;
             this._markAsDirty();
-        }     
+        }
 
-        public get barOffset(): string | number  {
+        public get barOffset(): string | number {
             return this._barOffset.toString(this._host);
         }
 
-        public get barOffsetInPixels(): number  {
+        public get barOffsetInPixels(): number {
             return this._barOffset.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }            
+        }
 
-        public set barOffset(value: string | number ) {
+        public set barOffset(value: string | number) {
             if (this._barOffset.toString(this._host) === value) {
                 return;
             }
@@ -55,17 +55,17 @@ module BABYLON.GUI {
             if (this._barOffset.fromString(value)) {
                 this._markAsDirty();
             }
-        }      
+        }
 
-        public get thumbWidth(): string | number  {
+        public get thumbWidth(): string | number {
             return this._thumbWidth.toString(this._host);
         }
 
-        public get thumbWidthInPixels(): number  {
+        public get thumbWidthInPixels(): number {
             return this._thumbWidth.getValueInPixel(this._host, this._cachedParentMeasure.width);
-        }          
+        }
 
-        public set thumbWidth(value: string | number ) {
+        public set thumbWidth(value: string | number) {
             if (this._thumbWidth.toString(this._host) === value) {
                 return;
             }
@@ -73,7 +73,7 @@ module BABYLON.GUI {
             if (this._thumbWidth.fromString(value)) {
                 this._markAsDirty();
             }
-        }              
+        }
 
         public get minimum(): number {
             return this._minimum;
@@ -88,7 +88,7 @@ module BABYLON.GUI {
             this._markAsDirty();
 
             this.value = Math.max(Math.min(this.value, this._maximum), this._minimum);
-        }         
+        }
 
         public get maximum(): number {
             return this._maximum;
@@ -103,7 +103,7 @@ module BABYLON.GUI {
             this._markAsDirty();
 
             this.value = Math.max(Math.min(this.value, this._maximum), this._minimum);
-        }     
+        }
 
         public get value(): number {
             return this._value;
@@ -119,7 +119,7 @@ module BABYLON.GUI {
             this._markAsDirty();
 
             this.onValueChangedObservable.notifyObservers(this._value);
-        }                             
+        }
 
         public get isThumbCircle(): boolean {
             return this._isThumbCircle;
@@ -142,7 +142,7 @@ module BABYLON.GUI {
 
         protected _getTypeName(): string {
             return "Slider";
-        }              
+        }
 
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
@@ -153,7 +153,7 @@ module BABYLON.GUI {
                 var effectiveThumbWidth;
                 var effectiveBarOffset;
 
-                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     context.shadowColor = this.shadowColor;
                     context.shadowBlur = this.shadowBlur;
                     context.shadowOffsetX = this.shadowOffsetX;
@@ -161,16 +161,16 @@ module BABYLON.GUI {
                 }
 
                 if (this._thumbWidth.isPixel) {
-                    effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.height);
+                    effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.width);
                 } else {
-                    effectiveThumbWidth = this._currentMeasure.height * this._thumbWidth.getValue(this._host); 
+                    effectiveThumbWidth = this._currentMeasure.width * this._thumbWidth.getValue(this._host);
                 }
 
                 if (this._barOffset.isPixel) {
                     effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), this._currentMeasure.height);
                 } else {
-                    effectiveBarOffset = this._currentMeasure.height * this._barOffset.getValue(this._host); 
-                }                
+                    effectiveBarOffset = this._currentMeasure.height * this._barOffset.getValue(this._host);
+                }
 
 
                 var left = this._currentMeasure.left + effectiveThumbWidth / 2;
@@ -181,7 +181,7 @@ module BABYLON.GUI {
                 context.fillStyle = this._background;
                 context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
 
-                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     context.shadowBlur = 0;
                     context.shadowOffsetX = 0;
                     context.shadowOffsetY = 0;
@@ -190,7 +190,7 @@ module BABYLON.GUI {
                 context.fillStyle = this.color;
                 context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
 
-                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                     context.shadowColor = this.shadowColor;
                     context.shadowBlur = this.shadowBlur;
                     context.shadowOffsetX = this.shadowOffsetX;
@@ -203,7 +203,7 @@ module BABYLON.GUI {
                     context.arc(left + thumbPosition, this._currentMeasure.top + this._currentMeasure.height / 2, effectiveThumbWidth / 2, 0, 2 * Math.PI);
                     context.fill();
 
-                    if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowBlur = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetY = 0;
@@ -214,8 +214,8 @@ module BABYLON.GUI {
                 }
                 else {
                     context.fillRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
-                    
-                    if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowBlur = 0;
                         context.shadowOffsetX = 0;
                         context.shadowOffsetY = 0;
@@ -231,8 +231,8 @@ module BABYLON.GUI {
         // Events
         private _pointerIsDown = false;
 
-        private _updateValueFromPointer(x: number, y:number): void {
-            if(this.rotation != 0){
+        private _updateValueFromPointer(x: number, y: number): void {
+            if (this.rotation != 0) {
                 this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
                 x = this._transformedPosition.x;
             }
@@ -260,11 +260,11 @@ module BABYLON.GUI {
             super._onPointerMove(target, coordinates);
         }
 
-        public _onPointerUp (target: Control, coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
             this._pointerIsDown = false;
-            
+
             this._host._capturingControl = null;
             super._onPointerUp(target, coordinates, buttonIndex);
-        }         
-    }    
+        }
+    }
 }

+ 20 - 6
loaders/src/glTF/1.0/babylon.glTFLoader.ts

@@ -581,11 +581,8 @@ module BABYLON.GLTF1 {
         if (!node.babylonNode) {
             return newMesh;
         }
-        var multiMat = new MultiMaterial("multimat" + id, gltfRuntime.scene);
 
-        if (!newMesh.material) {
-            newMesh.material = multiMat;
-        }
+        const subMaterials : Material[] = [];
 
         var vertexData = new VertexData();
         var geometry = new Geometry(id, gltfRuntime.scene, vertexData, false, newMesh);
@@ -690,14 +687,31 @@ module BABYLON.GLTF1 {
                 vertexData.merge(tempVertexData);
 
                 // Sub material
-                var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-                multiMat.subMaterials.push(material === null ? GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
+                let material = gltfRuntime.scene.getMaterialByID(primitive.material);
+
+                subMaterials.push(material === null ? GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
 
                 // Update vertices start and index start
                 verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
                 indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
             }
         }
+        let material : StandardMaterial | MultiMaterial;
+        if (subMaterials.length > 1) {
+            material = new MultiMaterial("multimat" + id, gltfRuntime.scene);
+            (material as MultiMaterial).subMaterials = subMaterials;
+        }
+        else {
+            material = new StandardMaterial("multimat" + id, gltfRuntime.scene);
+        }
+
+        if (subMaterials.length === 1) {
+            material = (subMaterials[0] as StandardMaterial);
+        }
+
+        if (!newMesh.material) {
+            newMesh.material = material;
+        }
 
         // Apply geometry
         geometry.setAllVerticesData(vertexData, false);

+ 44 - 13
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -463,30 +463,51 @@ module BABYLON.GLTF2 {
                 };
             });
 
-            const multiMaterial = new MultiMaterial(node.babylonMesh.name, this._babylonScene);
-            node.babylonMesh.material = multiMaterial;
-            const subMaterials = multiMaterial.subMaterials;
-            for (let index = 0; index < primitives.length; index++) {
-                const primitive = primitives[index];
-
+            if (primitives.length === 1) {  
+                const primitive = primitives[0];
                 if (primitive.material == null) {
-                    subMaterials[index] = this._getDefaultMaterial();
+                    node.babylonMesh.material = this._getDefaultMaterial();
                 }
                 else {
                     const material = GLTFLoader._GetProperty(this._gltf.materials, primitive.material);
                     if (!material) {
                         throw new Error(context + ": Failed to find material " + primitive.material);
                     }
-
+    
                     this._loadMaterial("#/materials/" + material.index, material, (babylonMaterial, isNew) => {
                         if (isNew && this._parent.onMaterialLoaded) {
                             this._parent.onMaterialLoaded(babylonMaterial);
                         }
-
-                        subMaterials[index] = babylonMaterial;
-                    });
-                }
-            };
+                        node.babylonMesh.material = babylonMaterial;
+                    }); 
+                } 
+            }
+            else {
+                const multiMaterial = new MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                node.babylonMesh.material = multiMaterial;
+                const subMaterials = multiMaterial.subMaterials;
+                for (let index = 0; index < primitives.length; index++) {
+                    const primitive = primitives[index];
+    
+                    if (primitive.material == null) {
+                        subMaterials[index] = this._getDefaultMaterial();
+                    }
+                    else {
+                        const material = GLTFLoader._GetProperty(this._gltf.materials, primitive.material);
+                        if (!material) {
+                            throw new Error(context + ": Failed to find material " + primitive.material);
+                        }
+    
+                        this._loadMaterial("#/materials/" + material.index, material, (babylonMaterial, isNew) => {
+                            if (isNew && this._parent.onMaterialLoaded) {
+                                this._parent.onMaterialLoaded(babylonMaterial);
+                            }
+    
+                            subMaterials[index] = babylonMaterial;
+                        });
+                    }
+                };
+            }
         }
 
         private _loadAllVertexDataAsync(context: string, mesh: IGLTFMesh, onSuccess: () => void): void {
@@ -1739,6 +1760,9 @@ module BABYLON.GLTF2 {
                         }
                     }
                 }
+                else {
+                    remaining++;
+                }
             }
 
             if (remaining === 0) {
@@ -1758,6 +1782,13 @@ module BABYLON.GLTF2 {
                         }
                     }
                 }
+                else if (mesh.material !== null) {
+                    this._compileMaterialAsync(mesh.material, mesh, () => {
+                        if (--remaining === 0) {
+                            onSuccess();
+                        }
+                    })
+                }
             }
         }
 

+ 118 - 109
localDev/index.html

@@ -1,111 +1,120 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-	<title>Local Development</title>
-	
-    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
-	<script src="https://preview.babylonjs.com/cannon.js"></script>
-	<script src="https://preview.babylonjs.com/Oimo.js"></script>
-	<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
-	<script src="../Tools/DevLoader/BabylonLoader.js"></script>
-	<script src="src/webgl-debug.js"></script>
-
-	<style>
-		html, body {
-			width: 100%;
-			height: 100%;
-			padding: 0;
-			margin: 0;
-			overflow: hidden;
-		}
-
-		#renderCanvas {
-			width: 100%;
-			height: 100%;
-		}
-
-		#fps {
-			position: absolute;
-			background-color: black;
-			border: 2px solid red;
-			text-align: center;
-			font-size: 16px;
-			color: white;
-			top: 15px;
-			right: 10px;
-			width: 60px;
-			height: 20px;
-		}
-	</style>
-</head>
-<body>
-	<div id="fps">0</div>
-	<canvas id="renderCanvas" touch-action="none"></canvas>
-	
-	<script>
-		var canvas = document.getElementById("renderCanvas");
-	//	canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(canvas);
-		var divFps = document.getElementById("fps");
-
-		// Global to simulate PG.
-		var engine = null;
-
-		// Allow querystring to navigate easily in debug in local samples.
-		var indexjs = 'src/index';
-		var sampleSearch = /sample=([0-9]+)/i;
-		var matches = null;
-		if ((matches = sampleSearch.exec(window.location)) !== null) {			
-			indexjs += '.';
-			indexjs += matches[1];
-		}
-		indexjs += '.js';
-
-		// Load the scripts + map file to allow vscode debug.
-		BABYLONDEVTOOLS.Loader
-			.require(indexjs)
-			.load(function() {
-				if (BABYLON.Engine.isSupported()) {
-					engine = new BABYLON.Engine(canvas, true, { stencil: true, disableWebGL2Support: false, preserveDrawingBuffer: true });
-					BABYLONDEVTOOLS.Loader.debugShortcut(engine);
-
-					// call the scene creation from the js.
-					if (typeof delayCreateScene !== "undefined") {
-						var scene = delayCreateScene();
-
-						if (scene) {
-							// Register a render loop to repeatedly render the scene
-
-							engine.runRenderLoop(function () {
-								if (scene.activeCamera) {
-									scene.render();
-								}
-								divFps.innerHTML = engine.getFps().toFixed() + " fps";
-							});
-						}
-					}
-					else {
-						var scene = createScene();
-
-						if (scene) {
-							// Register a render loop to repeatedly render the scene
-
-							engine.runRenderLoop(function () {
-								scene.render();
-								divFps.innerHTML = engine.getFps().toFixed() + " fps";
-							});
-						}
-					}
-
-					// Resize
-					window.addEventListener("resize", function () {
-						engine.resize();
-					});
-					
-				}
-				else {
-					alert('BabylonJS is not supported.')
-				}
-			});
-	</script>
-</body>
-</html>
+
+    <head>
+        <title>Local Development</title>
+
+        <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
+        <script src="https://preview.babylonjs.com/cannon.js"></script>
+        <script src="https://preview.babylonjs.com/Oimo.js"></script>
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
+        <script src="../Tools/DevLoader/BabylonLoader.js"></script>
+        <script src="src/webgl-debug.js"></script>
+
+        <style>
+            html,
+            body {
+                width: 100%;
+                height: 100%;
+                padding: 0;
+                margin: 0;
+                overflow: hidden;
+            }
+
+            #renderCanvas {
+                width: 100%;
+                height: 100%;
+            }
+
+            #fps {
+                position: absolute;
+                background-color: black;
+                border: 2px solid red;
+                text-align: center;
+                font-size: 16px;
+                color: white;
+                top: 15px;
+                right: 10px;
+                width: 60px;
+                height: 20px;
+            }
+        </style>
+    </head>
+
+    <body>
+        <div id="fps">0</div>
+        <canvas id="renderCanvas" touch-action="none"></canvas>
+
+        <script>
+            var canvas = document.getElementById("renderCanvas");
+            //	canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(canvas);
+            var divFps = document.getElementById("fps");
+
+            // Global to simulate PG.
+            var engine = null;
+
+            // Allow querystring to navigate easily in debug in local samples.
+            var indexjs = 'src/index';
+            var sampleSearch = /sample=([0-9]+)/i;
+            var matches = null;
+            if ((matches = sampleSearch.exec(window.location)) !== null) {
+                indexjs += '.';
+                indexjs += matches[1];
+            }
+            indexjs += '.js';
+
+            // Load the scripts + map file to allow vscode debug.
+            BABYLONDEVTOOLS.Loader
+                .require(indexjs)
+                .load(function () {
+                    if (BABYLON.Engine.isSupported()) {
+                        if (typeof createEngine !== "undefined") {
+                            engine = createEngine();
+                        } else {
+                            engine = new BABYLON.Engine(canvas, true, { stencil: true, disableWebGL2Support: false, preserveDrawingBuffer: true });
+                        }
+
+                        BABYLONDEVTOOLS.Loader.debugShortcut(engine);
+
+                        // call the scene creation from the js.
+                        if (typeof delayCreateScene !== "undefined") {
+                            var scene = delayCreateScene();
+
+                            if (scene) {
+                                // Register a render loop to repeatedly render the scene
+
+                                engine.runRenderLoop(function () {
+                                    if (scene.activeCamera) {
+                                        scene.render();
+                                    }
+                                    divFps.innerHTML = engine.getFps().toFixed() + " fps";
+                                });
+                            }
+                        }
+                        else {
+                            var scene = createScene();
+
+                            if (scene) {
+                                // Register a render loop to repeatedly render the scene
+
+                                engine.runRenderLoop(function () {
+                                    scene.render();
+                                    divFps.innerHTML = engine.getFps().toFixed() + " fps";
+                                });
+                            }
+                        }
+
+                        // Resize
+                        window.addEventListener("resize", function () {
+                            engine.resize();
+                        });
+
+                    }
+                    else {
+                        alert('BabylonJS is not supported.')
+                    }
+                });
+        </script>
+    </body>
+
+</html>

+ 42 - 17
materialsLibrary/src/water/babylon.waterMaterial.ts

@@ -140,8 +140,8 @@ module BABYLON {
 		*/
         private _mesh: Nullable<AbstractMesh> = null;
 
-        private _refractionRTT: RenderTargetTexture;
-        private _reflectionRTT: RenderTargetTexture;
+        private _refractionRTT: Nullable<RenderTargetTexture>;
+        private _reflectionRTT: Nullable<RenderTargetTexture>;
 
         private _reflectionTransform: Matrix = Matrix.Zero();
         private _lastTime: number = 0;
@@ -151,6 +151,8 @@ module BABYLON {
 
         private _useLogarithmicDepth: boolean;
 
+        private _waitingRenderList: Nullable<string[]>;
+
         /**
 		* Constructor
 		*/
@@ -162,8 +164,8 @@ module BABYLON {
             // Create render targets
             this.getRenderTargetTextures = (): SmartArray<RenderTargetTexture> => {
                 this._renderTargets.reset();
-                this._renderTargets.push(this._reflectionRTT);
-                this._renderTargets.push(this._refractionRTT);
+                this._renderTargets.push(<RenderTargetTexture> this._reflectionRTT);
+                this._renderTargets.push(<RenderTargetTexture> this._refractionRTT);
 
                 return this._renderTargets;
             }
@@ -181,21 +183,21 @@ module BABYLON {
         }
 
         // Get / Set
-        public get refractionTexture(): RenderTargetTexture {
+        public get refractionTexture(): Nullable<RenderTargetTexture> {
             return this._refractionRTT;
         }
 
-        public get reflectionTexture(): RenderTargetTexture {
+        public get reflectionTexture(): Nullable<RenderTargetTexture> {
             return this._reflectionRTT;
         }
 
         // Methods
         public addToRenderList(node: any): void {
-            if (this._refractionRTT.renderList) {
+            if (this._refractionRTT && this._refractionRTT.renderList) {
                 this._refractionRTT.renderList.push(node);
             }
 
-            if (this._reflectionRTT.renderList) {
+            if (this._reflectionRTT && this._reflectionRTT.renderList) {
                 this._reflectionRTT.renderList.push(node);
             }
         }
@@ -203,16 +205,21 @@ module BABYLON {
         public enableRenderTargets(enable: boolean): void {
             var refreshRate = enable ? 1 : 0;
 
-            this._refractionRTT.refreshRate = refreshRate;
-            this._reflectionRTT.refreshRate = refreshRate;
+            if (this._refractionRTT) {
+                this._refractionRTT.refreshRate = refreshRate;
+            }
+
+            if (this._reflectionRTT) {
+                this._reflectionRTT.refreshRate = refreshRate;
+            }
         }
 
         public getRenderList(): Nullable<AbstractMesh[]> {
-            return this._refractionRTT.renderList;
+            return this._refractionRTT ? this._refractionRTT.renderList : [];
         }
 
         public get renderTargetsEnabled(): boolean {
-            return !(this._refractionRTT.refreshRate === 0);
+            return !(this._refractionRTT && this._refractionRTT.refreshRate === 0);
         }
 
         public needAlphaBlending(): boolean {
@@ -292,8 +299,17 @@ module BABYLON {
             // Attribs
             MaterialHelper.PrepareDefinesForAttributes(mesh, defines, true, true);
 
+            // Configure this
             this._mesh = mesh;
 
+            if (this._waitingRenderList) {
+                for (var i = 0; i < this._waitingRenderList.length; i++) {
+                    this.addToRenderList(scene.getNodeByID(this._waitingRenderList[i]));
+                }
+
+                this._waitingRenderList = null;
+            }
+
             // Get correct effect      
             if (defines.isDirty) {
                 defines.markAsProcessed();
@@ -598,12 +614,12 @@ module BABYLON {
                 this.bumpTexture.dispose();
             }
 
-            var index = this.getScene().customRenderTargets.indexOf(this._refractionRTT);
+            var index = this.getScene().customRenderTargets.indexOf(<RenderTargetTexture> this._refractionRTT);
             if (index != -1) {
                 this.getScene().customRenderTargets.splice(index, 1);
             }
             index = -1;
-            index = this.getScene().customRenderTargets.indexOf(this._reflectionRTT);
+            index = this.getScene().customRenderTargets.indexOf(<RenderTargetTexture> this._reflectionRTT);
             if (index != -1) {
                 this.getScene().customRenderTargets.splice(index, 1);
             }
@@ -625,8 +641,14 @@ module BABYLON {
         public serialize(): any {
             var serializationObject = SerializationHelper.Serialize(this);
             serializationObject.customType = "BABYLON.WaterMaterial";
-            serializationObject.reflectionTexture.isRenderTarget = true;
-            serializationObject.refractionTexture.isRenderTarget = true;
+            
+            serializationObject.renderList = [];
+            if (this._refractionRTT && this._refractionRTT.renderList) {
+                for (var i = 0; i < this._refractionRTT.renderList.length; i++) {
+                    serializationObject.renderList.push(this._refractionRTT.renderList[i].id);
+                }
+            }
+
             return serializationObject;
         }
 
@@ -636,7 +658,10 @@ module BABYLON {
 
         // Statics
         public static Parse(source: any, scene: Scene, rootUrl: string): WaterMaterial {
-            return SerializationHelper.Parse(() => new WaterMaterial(source.name, scene), source, scene, rootUrl);
+            var mat = SerializationHelper.Parse(() => new WaterMaterial(source.name, scene), source, scene, rootUrl);
+            mat._waitingRenderList = source.renderList;
+
+            return mat;
         }
 
         public static CreateDefaultMesh(name: string, scene: Scene): Mesh {

+ 12 - 9
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -151,7 +151,7 @@ module BABYLON {
             this._canvas = scene.getEngine().getRenderingCanvas();
 
             this._defaultHeight = webVROptions.defaultHeight || 1.7;
-            
+
             if (webVROptions.createFallbackVRDeviceOrientationFreeCamera === undefined) {
                 webVROptions.createFallbackVRDeviceOrientationFreeCamera = true;
             }
@@ -181,7 +181,7 @@ module BABYLON {
                         this._deviceOrientationCamera.rotation = targetCamera.rotation.clone();
                     }
                 }
-                this._scene.activeCamera = this._deviceOrientationCamera;                
+                this._scene.activeCamera = this._deviceOrientationCamera;
                 if (this._canvas) {
                     this._scene.activeCamera.attachControl(this._canvas);
                 }
@@ -263,7 +263,7 @@ module BABYLON {
 
             // Exiting VR mode using 'ESC' key on desktop
             this._onKeyDown = (event: KeyboardEvent) => {
-                if (event.keyCode === 27 && this.isInVRMode()) {
+                if (event.keyCode === 27 && this.isInVRMode) {
                     this.exitVR();
                 }
             };
@@ -271,7 +271,7 @@ module BABYLON {
 
             // Exiting VR mode double tapping the touch screen
             this._scene.onPrePointerObservable.add((pointerInfo, eventState) => {
-                if (this.isInVRMode()) {
+                if (this.isInVRMode) {
                     this.exitVR();
                     if (this._fullscreenVRpresenting) {
                         this._scene.getEngine().switchFullscreen(true);
@@ -359,7 +359,10 @@ module BABYLON {
             }
         }
 
-        private isInVRMode() {
+        /**
+         * Gets a value indicating if we are currently in VR mode.
+         */
+        public get isInVRMode(): boolean {
             return this._webVRpresenting || this._fullscreenVRpresenting;
         }
 
@@ -393,7 +396,7 @@ module BABYLON {
                 return;
             }
             this._btnVR.className = "babylonVRicon";
-            if (this.isInVRMode()) {
+            if (this.isInVRMode) {
                 this._btnVR.className += " vrdisplaypresenting";
             } else {
                 if (this._webVRready) this._btnVR.className += " vrdisplayready";
@@ -1050,11 +1053,11 @@ module BABYLON {
 
             // Teleport the hmd to where the user is looking by moving the anchor to where they are looking minus the
             // offset of the headset from the anchor. Then add the helper's position to account for user's height offset
-            if(this.webVRCamera.leftCamera){
+            if (this.webVRCamera.leftCamera) {
                 this._workingVector.copyFrom(this.webVRCamera.leftCamera.globalPosition);
                 this._workingVector.subtractInPlace(this.webVRCamera.position);
                 this._haloCenter.subtractToRef(this._workingVector, this._workingVector);
-            }else{
+            } else {
                 this._workingVector.copyFrom(this._haloCenter);
             }
             this._workingVector.y += this._defaultHeight;
@@ -1271,7 +1274,7 @@ module BABYLON {
         }
 
         public dispose() {
-            if (this.isInVRMode()) {
+            if (this.isInVRMode) {
                 this.exitVR();
             }
 

+ 14 - 17
src/Cameras/babylon.targetCamera.ts

@@ -25,7 +25,7 @@
         private _rigCamTransformMatrix: Matrix;
 
         public _referencePoint = new Vector3(0, 0, 1);
-        private _defaultUpVector = new Vector3(0, 1, 0);
+        private _currentUpVector = new Vector3(0, 1, 0);
         public _transformedReferencePoint = Vector3.Zero();
         public _lookAtTemp = Matrix.Zero();
         public _tempMatrix = Matrix.Zero();
@@ -148,7 +148,7 @@
         public setTarget(target: Vector3): void {
             this.upVector.normalize();
 
-            Matrix.LookAtLHToRef(this.position, target, this._defaultUpVector, this._camMatrix);
+            Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
             this._camMatrix.invert();
 
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
@@ -271,31 +271,28 @@
             } else {
                 Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
             }
+
             //update the up vector!
-            BABYLON.Vector3.TransformNormalToRef(this._defaultUpVector, this._cameraRotationMatrix, this.upVector);
+            BABYLON.Vector3.TransformNormalToRef(this.upVector, this._cameraRotationMatrix, this._currentUpVector);
         }
 
         public _getViewMatrix(): Matrix {
-            if (!this.lockedTarget) {
-                // Compute
-                this._updateCameraRotationMatrix();
+            if (this.lockedTarget) {
+                this.setTarget(this._getLockedTargetPosition()!);
+            }
 
-                Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
+            // Compute
+            this._updateCameraRotationMatrix();
 
-                // Computing target and final matrix
-                this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
-            } else {
-                let targetPosition = this._getLockedTargetPosition();
+            Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
 
-                if (targetPosition) {
-                    this._currentTarget.copyFrom(targetPosition);
-                }
-            }
+            // Computing target and final matrix
+            this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
 
             if (this.getScene().useRightHandedSystem) {
-                Matrix.LookAtRHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
+                Matrix.LookAtRHToRef(this.position, this._currentTarget, this._currentUpVector, this._viewMatrix);
             } else {
-                Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
+                Matrix.LookAtLHToRef(this.position, this._currentTarget, this._currentUpVector, this._viewMatrix);
             }
 
             return this._viewMatrix;

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

@@ -298,6 +298,7 @@
         deterministicLockstep?: boolean;
         lockstepMaxSteps?: number;
         doNotHandleContextLost?: boolean;
+        constantAnimationDeltaTime?: number;
     }
 
     export interface IDisplayChangedEventArgs {

+ 11 - 0
src/Gamepad/Controllers/babylon.windowsMotionController.ts

@@ -66,6 +66,8 @@ module BABYLON {
         };
 
         public onTrackpadChangedObservable = new Observable<ExtendedGamepadButton>();
+        public onTrackpadValuesChangedObservable = new Observable<StickValues>();
+        public trackpad: StickValues = { x: 0, y: 0 };
 
         constructor(vrGamepad: any) {
             super(vrGamepad);
@@ -92,6 +94,10 @@ module BABYLON {
         public get onTouchpadButtonStateChangedObservable(): Observable<ExtendedGamepadButton> {
             return this.onTrackpadChangedObservable;
         }
+
+        public get onTouchpadValuesChangedObservable(): Observable<StickValues> {
+            return this.onTrackpadValuesChangedObservable;
+        }
         
         /**
          * Called once per frame by the engine.
@@ -102,6 +108,11 @@ module BABYLON {
             // Only need to animate axes if there is a loaded mesh
             if (this._loadedMeshInfo) {
                 if (this.browserGamepad.axes) {
+                    if(this.browserGamepad.axes[2] != this.trackpad.x || this.browserGamepad.axes[3] != this.trackpad.y){
+                        this.trackpad.x = this.browserGamepad["axes"][2];
+                        this.trackpad.y = this.browserGamepad["axes"][3];
+                        this.onTrackpadValuesChangedObservable.notifyObservers(this.trackpad);
+                    }
                     for (let axis = 0; axis < this._mapping.axisMeshNames.length; axis++) {
                         this.lerpAxisTransform(axis, this.browserGamepad.axes[axis]);
                     }

+ 6 - 9
src/babylon.scene.ts

@@ -181,6 +181,7 @@
         public forceShowBoundingBoxes = false;
         public clipPlane: Nullable<Plane>;
         public animationsEnabled = true;
+        public useConstantAnimationDeltaTime = false;
         public constantlyUpdateMeshUnderPointer = false;
 
         public hoverCursor = "pointer";
@@ -2070,7 +2071,7 @@
                 }
                 this._animationTimeLast = now;
             }
-            var deltaTime = (now - this._animationTimeLast) * this.animationTimeScale;
+            var deltaTime = this.useConstantAnimationDeltaTime ? 16.0 : (now - this._animationTimeLast) * this.animationTimeScale;
             this._animationTime += deltaTime;
             this._animationTimeLast = now;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
@@ -3377,7 +3378,7 @@
                 let defaultFrameTime = 1000 / 60; // frame time in MS
 
                 if (this._physicsEngine) {
-                    defaultFrameTime = this._physicsEngine.getTimeStep() / 1000;
+                    defaultFrameTime = this._physicsEngine.getTimeStep() * 1000;
                 }
                 let stepsTaken = 0;
 
@@ -3397,23 +3398,19 @@
                     // Physics
                     if (this._physicsEngine) {
                         this.onBeforePhysicsObservable.notifyObservers(this);
-                        this._physicsEngine._step(defaultFPS);
+                        this._physicsEngine._step(defaultFrameTime / 1000);
                         this.onAfterPhysicsObservable.notifyObservers(this);
                     }
 
                     this.onAfterStepObservable.notifyObservers(this);
                     this._currentStepId++;
 
-                    if ((internalSteps > 1) && (stepsTaken != internalSteps - 1)) {
-                        this._evaluateActiveMeshes();
-                    }
-
                     stepsTaken++;
                     deltaTime -= defaultFrameTime;
 
-                } while (deltaTime > 0 && stepsTaken < maxSubSteps);
+                } while (deltaTime > 0 && stepsTaken < internalSteps);
 
-                this._timeAccumulator = deltaTime;
+                this._timeAccumulator = deltaTime < 0 ? 0 : deltaTime;
 
             }
             else {

BIN
tests/validation/ReferenceImages/GUI.png


BIN
tests/validation/ReferenceImages/charting.png


BIN
tests/validation/ReferenceImages/gltf1CesiumMan.png


BIN
tests/validation/ReferenceImages/pbrrough.png


BIN
tests/validation/ReferenceImages/upVector.png


+ 6 - 0
tests/validation/config.json

@@ -318,6 +318,12 @@
       "referenceImage": "GUI.png"
     },
     {
+      "title": "Up Vector",
+      "renderCount": 20,
+      "playgroundId": "#2FNBTG#27",
+      "referenceImage": "upVector.png"
+    },
+    {
       "title": "Water material (only visual check)",
       "renderCount": 10,
       "scriptToRun": "/Demos/WaterMaterial/water.js",

+ 1 - 0
tests/validation/validation.js

@@ -104,6 +104,7 @@ function processCurrentScene(test, resultCanvas, result, renderImage, index, wai
     currentScene.executeWhenReady(function () {
         var renderCount = test.renderCount || 1;
 
+        currentScene.useConstantAnimationDeltaTime = true;
         engine.runRenderLoop(function () {
             currentScene.render();
             renderCount--;