浏览代码

Merge remote-tracking branch 'BabylonJS/master' into viewer-fixes

# Conflicts:
#	dist/preview release/viewer/babylon.viewer.js
Raanan Weber 7 年之前
父节点
当前提交
c95170e599
共有 54 个文件被更改,包括 9533 次插入8589 次删除
  1. 7325 7298
      Playground/babylon.d.txt
  2. 3 3
      Viewer/dist/basicExample.html
  3. 495 487
      dist/preview release/babylon.d.ts
  4. 21 21
      dist/preview release/babylon.js
  5. 47 33
      dist/preview release/babylon.max.js
  6. 21 21
      dist/preview release/babylon.worker.js
  7. 32 32
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  8. 187 84
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  9. 1 0
      dist/preview release/gui/babylon.gui.d.ts
  10. 19 3
      dist/preview release/gui/babylon.gui.js
  11. 3 3
      dist/preview release/gui/babylon.gui.min.js
  12. 1 0
      dist/preview release/gui/babylon.gui.module.d.ts
  13. 58 6
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  14. 64 16
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  15. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  16. 58 7
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  17. 123 45
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  18. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  19. 58 7
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  20. 140 51
      dist/preview release/loaders/babylon.glTFFileLoader.js
  21. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  22. 140 51
      dist/preview release/loaders/babylonjs.loaders.js
  23. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  24. 58 7
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  25. 3 2
      dist/preview release/materialsLibrary/babylon.waterMaterial.d.ts
  26. 26 9
      dist/preview release/materialsLibrary/babylon.waterMaterial.js
  27. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  28. 26 9
      dist/preview release/materialsLibrary/babylonjs.materials.js
  29. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  30. 3 2
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  31. 1 0
      dist/preview release/what's new.md
  32. 131 131
      gui/src/controls/control.ts
  33. 68 52
      gui/src/controls/inputText.ts
  34. 34 34
      gui/src/controls/slider.ts
  35. 20 6
      loaders/src/glTF/1.0/babylon.glTFLoader.ts
  36. 75 38
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  37. 131 52
      loaders/src/glTF/README.md
  38. 80 13
      loaders/src/glTF/babylon.glTFFileLoader.ts
  39. 8 8
      sandbox/index.js
  40. 12 9
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  41. 14 17
      src/Cameras/babylon.targetCamera.ts
  42. 1 0
      src/Engine/babylon.engine.ts
  43. 11 0
      src/Gamepad/Controllers/babylon.windowsMotionController.ts
  44. 1 1
      src/Mesh/babylon.transformNode.ts
  45. 7 10
      src/babylon.scene.ts
  46. 二进制
      tests/validation/ReferenceImages/GUI.png
  47. 二进制
      tests/validation/ReferenceImages/charting.png
  48. 二进制
      tests/validation/ReferenceImages/fresnel.png
  49. 二进制
      tests/validation/ReferenceImages/gltf1CesiumMan.png
  50. 二进制
      tests/validation/ReferenceImages/pbrrough.png
  51. 二进制
      tests/validation/ReferenceImages/procedural.png
  52. 二进制
      tests/validation/ReferenceImages/upVector.png
  53. 14 9
      tests/validation/config.json
  54. 1 0
      tests/validation/validation.js

文件差异内容过多而无法显示
+ 7325 - 7298
Playground/babylon.d.txt


+ 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>

文件差异内容过多而无法显示
+ 495 - 487
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 21 - 21
dist/preview release/babylon.js


+ 47 - 33
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,22 +20692,19 @@ 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
-                var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
+                var deltaTime = this.useConstantAnimationDeltaTime ? 16 : Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
                 this._animationRatio = deltaTime * (60.0 / 1000.0);
                 this._animate();
                 this.onAfterAnimationsObservable.notifyObservers(this);
@@ -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) {

文件差异内容过多而无法显示
+ 21 - 21
dist/preview release/babylon.worker.js


文件差异内容过多而无法显示
+ 32 - 32
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 187 - 84
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,22 +20692,19 @@ 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
-                var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
+                var deltaTime = this.useConstantAnimationDeltaTime ? 16 : Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
                 this._animationRatio = deltaTime * (60.0 / 1000.0);
                 this._animate();
                 this.onAfterAnimationsObservable.notifyObservers(this);
@@ -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) {
@@ -79630,27 +79644,63 @@ var BABYLON;
 (function (BABYLON) {
     var GLTFLoaderCoordinateSystemMode;
     (function (GLTFLoaderCoordinateSystemMode) {
-        // Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene (scene.useRightHandedSystem).
-        // NOTE: When scene.useRightHandedSystem is false, an additional transform will be added to the root to transform the data from right-handed to left-handed.
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["AUTO"] = 0] = "AUTO";
-        // The glTF right-handed data is not transformed in any form and is loaded directly.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["PASS_THROUGH"] = 1] = "PASS_THROUGH";
-        // Sets the useRightHandedSystem flag on the scene.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 2] = "FORCE_RIGHT_HANDED";
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 1] = "FORCE_RIGHT_HANDED";
     })(GLTFLoaderCoordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode || (BABYLON.GLTFLoaderCoordinateSystemMode = {}));
+    var GLTFLoaderAnimationStartMode;
+    (function (GLTFLoaderAnimationStartMode) {
+        /**
+         * No animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["NONE"] = 0] = "NONE";
+        /**
+         * The first animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["FIRST"] = 1] = "FIRST";
+        /**
+         * All animations will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["ALL"] = 2] = "ALL";
+    })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
-            // V2 options
+            // #endregion
+            // #region V2 options
+            /**
+             * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+             */
             this.coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+            /**
+             * The animation start mode (NONE, FIRST, ALL).
+             */
+            this.animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+            /**
+             * Set to true to compile materials before raising the success callback.
+             */
             this.compileMaterials = false;
-            this.compileShadowGenerators = false;
+            /**
+             * Set to true to also compile materials with clip planes.
+             */
             this.useClipPlane = false;
+            /**
+             * Set to true to compile shadow generators before raising the success callback.
+             */
+            this.compileShadowGenerators = false;
             this.name = "gltf";
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
             };
         }
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
                 this._loader.dispose();
@@ -79842,9 +79892,10 @@ var BABYLON;
             }
             return result;
         };
-        // V1 options
-        GLTFFileLoader.HomogeneousCoordinates = false;
+        // #endregion
+        // #region V1 options
         GLTFFileLoader.IncrementalLoading = true;
+        GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
     }());
     BABYLON.GLTFFileLoader = GLTFFileLoader;
@@ -80457,10 +80508,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 +80596,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);
@@ -82261,21 +82323,38 @@ var BABYLON;
                 }
                 return skeletons;
             };
-            GLTFLoader.prototype._getAnimationTargets = function () {
-                var targets = new Array();
+            GLTFLoader.prototype._startAnimations = function () {
                 var animations = this._gltf.animations;
-                if (animations) {
-                    for (var _i = 0, animations_1 = animations; _i < animations_1.length; _i++) {
-                        var animation = animations_1[_i];
-                        targets.push.apply(targets, animation.targets);
-                    }
+                if (!animations) {
+                    return;
                 }
-                return targets;
-            };
-            GLTFLoader.prototype._startAnimations = function () {
-                for (var _i = 0, _a = this._getAnimationTargets(); _i < _a.length; _i++) {
-                    var target = _a[_i];
-                    this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                switch (this._parent.animationStartMode) {
+                    case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
+                        // do nothing
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.FIRST: {
+                        var animation = animations[0];
+                        for (var _i = 0, _a = animation.targets; _i < _a.length; _i++) {
+                            var target = _a[_i];
+                            this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                        }
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.ALL: {
+                        for (var _b = 0, animations_1 = animations; _b < animations_1.length; _b++) {
+                            var animation = animations_1[_b];
+                            for (var _c = 0, _d = animation.targets; _c < _d.length; _c++) {
+                                var target = _d[_c];
+                                this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                            }
+                        }
+                        break;
+                    }
+                    default: {
+                        BABYLON.Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                        return;
+                    }
                 }
             };
             GLTFLoader.prototype._loadDefaultScene = function (nodeNames) {
@@ -82286,6 +82365,7 @@ var BABYLON;
                 this._loadScene("#/scenes/" + scene.index, scene, nodeNames);
             };
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
+                var _this = this;
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
                 switch (this._parent.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -82296,10 +82376,6 @@ var BABYLON;
                         }
                         break;
                     }
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH: {
-                        // do nothing
-                        break;
-                    }
                     case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED: {
                         this._babylonScene.useRightHandedSystem = true;
                         break;
@@ -82325,6 +82401,7 @@ var BABYLON;
                     this._traverseNodes(context, nodeIndices, function (node) {
                         if (nodeNames.indexOf(node.name) !== -1) {
                             filteredNodeIndices_1.push(node.index);
+                            node.parent = _this._rootNode;
                             return false;
                         }
                         return true;
@@ -82420,32 +82497,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;
@@ -83529,10 +83626,6 @@ var BABYLON;
             };
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
                 var _this = this;
-                if (!this._parent.compileMaterials) {
-                    onSuccess();
-                    return;
-                }
                 if (this._parent.useClipPlane) {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
@@ -83563,6 +83656,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -83582,6 +83678,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;

文件差异内容过多而无法显示
+ 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;

+ 58 - 6
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -1,9 +1,28 @@
 
 declare module BABYLON {
     enum GLTFLoaderCoordinateSystemMode {
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         AUTO = 0,
-        PASS_THROUGH = 1,
-        FORCE_RIGHT_HANDED = 2,
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        FORCE_RIGHT_HANDED = 1,
+    }
+    enum GLTFLoaderAnimationStartMode {
+        /**
+         * No animation will start.
+         */
+        NONE = 0,
+        /**
+         * The first animation will start.
+         */
+        FIRST = 1,
+        /**
+         * All animations will start.
+         */
+        ALL = 2,
     }
     interface IGLTFLoaderData {
         json: Object;
@@ -16,25 +35,58 @@ declare module BABYLON {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        /**
+         * Raised when the asset has been parsed.
+         * The data.json property stores the glTF JSON.
+         * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+         */
         onParsed: (data: IGLTFLoaderData) => void;
-        static HomogeneousCoordinates: boolean;
         static IncrementalLoading: boolean;
+        static HomogeneousCoordinates: boolean;
+        /**
+         * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+         */
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        /**
+         * The animation start mode (NONE, FIRST, ALL).
+         */
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        /**
+         * Set to true to compile materials before raising the success callback.
+         */
         compileMaterials: boolean;
-        compileShadowGenerators: boolean;
+        /**
+         * Set to true to also compile materials with clip planes.
+         */
         useClipPlane: boolean;
+        /**
+         * Set to true to compile shadow generators before raising the success callback.
+         */
+        compileShadowGenerators: boolean;
+        /**
+         * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         */
         onMeshLoaded: (mesh: AbstractMesh) => void;
+        /**
+         * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+         */
         onTextureLoaded: (texture: BaseTexture) => void;
+        /**
+         * Raised when the loader creates a material after parsing the glTF properties of the material.
+         */
         onMaterialLoaded: (material: Material) => void;
         /**
-         * Raised when the asset is completely loaded, just before the loader is disposed.
+         * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
-         * For assets without LODs, raised when the model is complete just after onSuccess.
+         * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
         onComplete: () => void;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;

+ 64 - 16
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -3,27 +3,63 @@ var BABYLON;
 (function (BABYLON) {
     var GLTFLoaderCoordinateSystemMode;
     (function (GLTFLoaderCoordinateSystemMode) {
-        // Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene (scene.useRightHandedSystem).
-        // NOTE: When scene.useRightHandedSystem is false, an additional transform will be added to the root to transform the data from right-handed to left-handed.
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["AUTO"] = 0] = "AUTO";
-        // The glTF right-handed data is not transformed in any form and is loaded directly.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["PASS_THROUGH"] = 1] = "PASS_THROUGH";
-        // Sets the useRightHandedSystem flag on the scene.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 2] = "FORCE_RIGHT_HANDED";
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 1] = "FORCE_RIGHT_HANDED";
     })(GLTFLoaderCoordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode || (BABYLON.GLTFLoaderCoordinateSystemMode = {}));
+    var GLTFLoaderAnimationStartMode;
+    (function (GLTFLoaderAnimationStartMode) {
+        /**
+         * No animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["NONE"] = 0] = "NONE";
+        /**
+         * The first animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["FIRST"] = 1] = "FIRST";
+        /**
+         * All animations will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["ALL"] = 2] = "ALL";
+    })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
-            // V2 options
+            // #endregion
+            // #region V2 options
+            /**
+             * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+             */
             this.coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+            /**
+             * The animation start mode (NONE, FIRST, ALL).
+             */
+            this.animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+            /**
+             * Set to true to compile materials before raising the success callback.
+             */
             this.compileMaterials = false;
-            this.compileShadowGenerators = false;
+            /**
+             * Set to true to also compile materials with clip planes.
+             */
             this.useClipPlane = false;
+            /**
+             * Set to true to compile shadow generators before raising the success callback.
+             */
+            this.compileShadowGenerators = false;
             this.name = "gltf";
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
             };
         }
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
                 this._loader.dispose();
@@ -215,9 +251,10 @@ var BABYLON;
             }
             return result;
         };
-        // V1 options
-        GLTFFileLoader.HomogeneousCoordinates = false;
+        // #endregion
+        // #region V1 options
         GLTFFileLoader.IncrementalLoading = true;
+        GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
     }());
     BABYLON.GLTFFileLoader = GLTFFileLoader;
@@ -830,10 +867,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 +955,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);

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 58 - 7
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -1,9 +1,28 @@
 
 declare module BABYLON {
     enum GLTFLoaderCoordinateSystemMode {
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         AUTO = 0,
-        PASS_THROUGH = 1,
-        FORCE_RIGHT_HANDED = 2,
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        FORCE_RIGHT_HANDED = 1,
+    }
+    enum GLTFLoaderAnimationStartMode {
+        /**
+         * No animation will start.
+         */
+        NONE = 0,
+        /**
+         * The first animation will start.
+         */
+        FIRST = 1,
+        /**
+         * All animations will start.
+         */
+        ALL = 2,
     }
     interface IGLTFLoaderData {
         json: Object;
@@ -16,25 +35,58 @@ declare module BABYLON {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        /**
+         * Raised when the asset has been parsed.
+         * The data.json property stores the glTF JSON.
+         * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+         */
         onParsed: (data: IGLTFLoaderData) => void;
-        static HomogeneousCoordinates: boolean;
         static IncrementalLoading: boolean;
+        static HomogeneousCoordinates: boolean;
+        /**
+         * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+         */
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        /**
+         * The animation start mode (NONE, FIRST, ALL).
+         */
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        /**
+         * Set to true to compile materials before raising the success callback.
+         */
         compileMaterials: boolean;
-        compileShadowGenerators: boolean;
+        /**
+         * Set to true to also compile materials with clip planes.
+         */
         useClipPlane: boolean;
+        /**
+         * Set to true to compile shadow generators before raising the success callback.
+         */
+        compileShadowGenerators: boolean;
+        /**
+         * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         */
         onMeshLoaded: (mesh: AbstractMesh) => void;
+        /**
+         * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+         */
         onTextureLoaded: (texture: BaseTexture) => void;
+        /**
+         * Raised when the loader creates a material after parsing the glTF properties of the material.
+         */
         onMaterialLoaded: (material: Material) => void;
         /**
-         * Raised when the asset is completely loaded, just before the loader is disposed.
+         * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
-         * For assets without LODs, raised when the model is complete just after onSuccess.
+         * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
         onComplete: () => void;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -336,7 +388,6 @@ declare module BABYLON.GLTF2 {
         private _loadData(data);
         private _getMeshes();
         private _getSkeletons();
-        private _getAnimationTargets();
         private _startAnimations();
         private _loadDefaultScene(nodeNames);
         private _loadScene(context, scene, nodeNames);

+ 123 - 45
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -3,27 +3,63 @@ var BABYLON;
 (function (BABYLON) {
     var GLTFLoaderCoordinateSystemMode;
     (function (GLTFLoaderCoordinateSystemMode) {
-        // Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene (scene.useRightHandedSystem).
-        // NOTE: When scene.useRightHandedSystem is false, an additional transform will be added to the root to transform the data from right-handed to left-handed.
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["AUTO"] = 0] = "AUTO";
-        // The glTF right-handed data is not transformed in any form and is loaded directly.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["PASS_THROUGH"] = 1] = "PASS_THROUGH";
-        // Sets the useRightHandedSystem flag on the scene.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 2] = "FORCE_RIGHT_HANDED";
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 1] = "FORCE_RIGHT_HANDED";
     })(GLTFLoaderCoordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode || (BABYLON.GLTFLoaderCoordinateSystemMode = {}));
+    var GLTFLoaderAnimationStartMode;
+    (function (GLTFLoaderAnimationStartMode) {
+        /**
+         * No animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["NONE"] = 0] = "NONE";
+        /**
+         * The first animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["FIRST"] = 1] = "FIRST";
+        /**
+         * All animations will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["ALL"] = 2] = "ALL";
+    })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
-            // V2 options
+            // #endregion
+            // #region V2 options
+            /**
+             * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+             */
             this.coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+            /**
+             * The animation start mode (NONE, FIRST, ALL).
+             */
+            this.animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+            /**
+             * Set to true to compile materials before raising the success callback.
+             */
             this.compileMaterials = false;
-            this.compileShadowGenerators = false;
+            /**
+             * Set to true to also compile materials with clip planes.
+             */
             this.useClipPlane = false;
+            /**
+             * Set to true to compile shadow generators before raising the success callback.
+             */
+            this.compileShadowGenerators = false;
             this.name = "gltf";
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
             };
         }
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
                 this._loader.dispose();
@@ -215,9 +251,10 @@ var BABYLON;
             }
             return result;
         };
-        // V1 options
-        GLTFFileLoader.HomogeneousCoordinates = false;
+        // #endregion
+        // #region V1 options
         GLTFFileLoader.IncrementalLoading = true;
+        GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
     }());
     BABYLON.GLTFFileLoader = GLTFFileLoader;
@@ -507,21 +544,38 @@ var BABYLON;
                 }
                 return skeletons;
             };
-            GLTFLoader.prototype._getAnimationTargets = function () {
-                var targets = new Array();
+            GLTFLoader.prototype._startAnimations = function () {
                 var animations = this._gltf.animations;
-                if (animations) {
-                    for (var _i = 0, animations_1 = animations; _i < animations_1.length; _i++) {
-                        var animation = animations_1[_i];
-                        targets.push.apply(targets, animation.targets);
-                    }
+                if (!animations) {
+                    return;
                 }
-                return targets;
-            };
-            GLTFLoader.prototype._startAnimations = function () {
-                for (var _i = 0, _a = this._getAnimationTargets(); _i < _a.length; _i++) {
-                    var target = _a[_i];
-                    this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                switch (this._parent.animationStartMode) {
+                    case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
+                        // do nothing
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.FIRST: {
+                        var animation = animations[0];
+                        for (var _i = 0, _a = animation.targets; _i < _a.length; _i++) {
+                            var target = _a[_i];
+                            this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                        }
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.ALL: {
+                        for (var _b = 0, animations_1 = animations; _b < animations_1.length; _b++) {
+                            var animation = animations_1[_b];
+                            for (var _c = 0, _d = animation.targets; _c < _d.length; _c++) {
+                                var target = _d[_c];
+                                this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                            }
+                        }
+                        break;
+                    }
+                    default: {
+                        BABYLON.Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                        return;
+                    }
                 }
             };
             GLTFLoader.prototype._loadDefaultScene = function (nodeNames) {
@@ -532,6 +586,7 @@ var BABYLON;
                 this._loadScene("#/scenes/" + scene.index, scene, nodeNames);
             };
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
+                var _this = this;
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
                 switch (this._parent.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -542,10 +597,6 @@ var BABYLON;
                         }
                         break;
                     }
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH: {
-                        // do nothing
-                        break;
-                    }
                     case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED: {
                         this._babylonScene.useRightHandedSystem = true;
                         break;
@@ -571,6 +622,7 @@ var BABYLON;
                     this._traverseNodes(context, nodeIndices, function (node) {
                         if (nodeNames.indexOf(node.name) !== -1) {
                             filteredNodeIndices_1.push(node.index);
+                            node.parent = _this._rootNode;
                             return false;
                         }
                         return true;
@@ -666,32 +718,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;
@@ -1775,10 +1847,6 @@ var BABYLON;
             };
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
                 var _this = this;
-                if (!this._parent.compileMaterials) {
-                    onSuccess();
-                    return;
-                }
                 if (this._parent.useClipPlane) {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
@@ -1809,6 +1877,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -1828,6 +1899,13 @@ var BABYLON;
                             }
                         }
                     }
+                    else if (mesh.material !== null) {
+                        this._compileMaterialAsync(mesh.material, mesh, function () {
+                            if (--remaining === 0) {
+                                onSuccess();
+                            }
+                        });
+                    }
                 }
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 58 - 7
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -1,9 +1,28 @@
 
 declare module BABYLON {
     enum GLTFLoaderCoordinateSystemMode {
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         AUTO = 0,
-        PASS_THROUGH = 1,
-        FORCE_RIGHT_HANDED = 2,
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        FORCE_RIGHT_HANDED = 1,
+    }
+    enum GLTFLoaderAnimationStartMode {
+        /**
+         * No animation will start.
+         */
+        NONE = 0,
+        /**
+         * The first animation will start.
+         */
+        FIRST = 1,
+        /**
+         * All animations will start.
+         */
+        ALL = 2,
     }
     interface IGLTFLoaderData {
         json: Object;
@@ -16,25 +35,58 @@ declare module BABYLON {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        /**
+         * Raised when the asset has been parsed.
+         * The data.json property stores the glTF JSON.
+         * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+         */
         onParsed: (data: IGLTFLoaderData) => void;
-        static HomogeneousCoordinates: boolean;
         static IncrementalLoading: boolean;
+        static HomogeneousCoordinates: boolean;
+        /**
+         * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+         */
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        /**
+         * The animation start mode (NONE, FIRST, ALL).
+         */
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        /**
+         * Set to true to compile materials before raising the success callback.
+         */
         compileMaterials: boolean;
-        compileShadowGenerators: boolean;
+        /**
+         * Set to true to also compile materials with clip planes.
+         */
         useClipPlane: boolean;
+        /**
+         * Set to true to compile shadow generators before raising the success callback.
+         */
+        compileShadowGenerators: boolean;
+        /**
+         * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         */
         onMeshLoaded: (mesh: AbstractMesh) => void;
+        /**
+         * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+         */
         onTextureLoaded: (texture: BaseTexture) => void;
+        /**
+         * Raised when the loader creates a material after parsing the glTF properties of the material.
+         */
         onMaterialLoaded: (material: Material) => void;
         /**
-         * Raised when the asset is completely loaded, just before the loader is disposed.
+         * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
-         * For assets without LODs, raised when the model is complete just after onSuccess.
+         * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
         onComplete: () => void;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -883,7 +935,6 @@ declare module BABYLON.GLTF2 {
         private _loadData(data);
         private _getMeshes();
         private _getSkeletons();
-        private _getAnimationTargets();
         private _startAnimations();
         private _loadDefaultScene(nodeNames);
         private _loadScene(context, scene, nodeNames);

+ 140 - 51
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3,27 +3,63 @@ var BABYLON;
 (function (BABYLON) {
     var GLTFLoaderCoordinateSystemMode;
     (function (GLTFLoaderCoordinateSystemMode) {
-        // Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene (scene.useRightHandedSystem).
-        // NOTE: When scene.useRightHandedSystem is false, an additional transform will be added to the root to transform the data from right-handed to left-handed.
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["AUTO"] = 0] = "AUTO";
-        // The glTF right-handed data is not transformed in any form and is loaded directly.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["PASS_THROUGH"] = 1] = "PASS_THROUGH";
-        // Sets the useRightHandedSystem flag on the scene.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 2] = "FORCE_RIGHT_HANDED";
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 1] = "FORCE_RIGHT_HANDED";
     })(GLTFLoaderCoordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode || (BABYLON.GLTFLoaderCoordinateSystemMode = {}));
+    var GLTFLoaderAnimationStartMode;
+    (function (GLTFLoaderAnimationStartMode) {
+        /**
+         * No animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["NONE"] = 0] = "NONE";
+        /**
+         * The first animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["FIRST"] = 1] = "FIRST";
+        /**
+         * All animations will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["ALL"] = 2] = "ALL";
+    })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
-            // V2 options
+            // #endregion
+            // #region V2 options
+            /**
+             * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+             */
             this.coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+            /**
+             * The animation start mode (NONE, FIRST, ALL).
+             */
+            this.animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+            /**
+             * Set to true to compile materials before raising the success callback.
+             */
             this.compileMaterials = false;
-            this.compileShadowGenerators = false;
+            /**
+             * Set to true to also compile materials with clip planes.
+             */
             this.useClipPlane = false;
+            /**
+             * Set to true to compile shadow generators before raising the success callback.
+             */
+            this.compileShadowGenerators = false;
             this.name = "gltf";
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
             };
         }
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
                 this._loader.dispose();
@@ -215,9 +251,10 @@ var BABYLON;
             }
             return result;
         };
-        // V1 options
-        GLTFFileLoader.HomogeneousCoordinates = false;
+        // #endregion
+        // #region V1 options
         GLTFFileLoader.IncrementalLoading = true;
+        GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
     }());
     BABYLON.GLTFFileLoader = GLTFFileLoader;
@@ -830,10 +867,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 +955,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);
@@ -2652,21 +2700,38 @@ var BABYLON;
                 }
                 return skeletons;
             };
-            GLTFLoader.prototype._getAnimationTargets = function () {
-                var targets = new Array();
+            GLTFLoader.prototype._startAnimations = function () {
                 var animations = this._gltf.animations;
-                if (animations) {
-                    for (var _i = 0, animations_1 = animations; _i < animations_1.length; _i++) {
-                        var animation = animations_1[_i];
-                        targets.push.apply(targets, animation.targets);
-                    }
+                if (!animations) {
+                    return;
                 }
-                return targets;
-            };
-            GLTFLoader.prototype._startAnimations = function () {
-                for (var _i = 0, _a = this._getAnimationTargets(); _i < _a.length; _i++) {
-                    var target = _a[_i];
-                    this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                switch (this._parent.animationStartMode) {
+                    case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
+                        // do nothing
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.FIRST: {
+                        var animation = animations[0];
+                        for (var _i = 0, _a = animation.targets; _i < _a.length; _i++) {
+                            var target = _a[_i];
+                            this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                        }
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.ALL: {
+                        for (var _b = 0, animations_1 = animations; _b < animations_1.length; _b++) {
+                            var animation = animations_1[_b];
+                            for (var _c = 0, _d = animation.targets; _c < _d.length; _c++) {
+                                var target = _d[_c];
+                                this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                            }
+                        }
+                        break;
+                    }
+                    default: {
+                        BABYLON.Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                        return;
+                    }
                 }
             };
             GLTFLoader.prototype._loadDefaultScene = function (nodeNames) {
@@ -2677,6 +2742,7 @@ var BABYLON;
                 this._loadScene("#/scenes/" + scene.index, scene, nodeNames);
             };
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
+                var _this = this;
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
                 switch (this._parent.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -2687,10 +2753,6 @@ var BABYLON;
                         }
                         break;
                     }
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH: {
-                        // do nothing
-                        break;
-                    }
                     case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED: {
                         this._babylonScene.useRightHandedSystem = true;
                         break;
@@ -2716,6 +2778,7 @@ var BABYLON;
                     this._traverseNodes(context, nodeIndices, function (node) {
                         if (nodeNames.indexOf(node.name) !== -1) {
                             filteredNodeIndices_1.push(node.index);
+                            node.parent = _this._rootNode;
                             return false;
                         }
                         return true;
@@ -2811,32 +2874,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;
@@ -3920,10 +4003,6 @@ var BABYLON;
             };
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
                 var _this = this;
-                if (!this._parent.compileMaterials) {
-                    onSuccess();
-                    return;
-                }
                 if (this._parent.useClipPlane) {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
@@ -3954,6 +4033,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -3973,6 +4055,13 @@ var BABYLON;
                             }
                         }
                     }
+                    else if (mesh.material !== null) {
+                        this._compileMaterialAsync(mesh.material, mesh, function () {
+                            if (--remaining === 0) {
+                                onSuccess();
+                            }
+                        });
+                    }
                 }
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {

文件差异内容过多而无法显示
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 140 - 51
dist/preview release/loaders/babylonjs.loaders.js

@@ -977,27 +977,63 @@ var BABYLON;
 (function (BABYLON) {
     var GLTFLoaderCoordinateSystemMode;
     (function (GLTFLoaderCoordinateSystemMode) {
-        // Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene (scene.useRightHandedSystem).
-        // NOTE: When scene.useRightHandedSystem is false, an additional transform will be added to the root to transform the data from right-handed to left-handed.
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["AUTO"] = 0] = "AUTO";
-        // The glTF right-handed data is not transformed in any form and is loaded directly.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["PASS_THROUGH"] = 1] = "PASS_THROUGH";
-        // Sets the useRightHandedSystem flag on the scene.
-        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 2] = "FORCE_RIGHT_HANDED";
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 1] = "FORCE_RIGHT_HANDED";
     })(GLTFLoaderCoordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode || (BABYLON.GLTFLoaderCoordinateSystemMode = {}));
+    var GLTFLoaderAnimationStartMode;
+    (function (GLTFLoaderAnimationStartMode) {
+        /**
+         * No animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["NONE"] = 0] = "NONE";
+        /**
+         * The first animation will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["FIRST"] = 1] = "FIRST";
+        /**
+         * All animations will start.
+         */
+        GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["ALL"] = 2] = "ALL";
+    })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
-            // V2 options
+            // #endregion
+            // #region V2 options
+            /**
+             * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+             */
             this.coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+            /**
+             * The animation start mode (NONE, FIRST, ALL).
+             */
+            this.animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+            /**
+             * Set to true to compile materials before raising the success callback.
+             */
             this.compileMaterials = false;
-            this.compileShadowGenerators = false;
+            /**
+             * Set to true to also compile materials with clip planes.
+             */
             this.useClipPlane = false;
+            /**
+             * Set to true to compile shadow generators before raising the success callback.
+             */
+            this.compileShadowGenerators = false;
             this.name = "gltf";
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
             };
         }
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
                 this._loader.dispose();
@@ -1189,9 +1225,10 @@ var BABYLON;
             }
             return result;
         };
-        // V1 options
-        GLTFFileLoader.HomogeneousCoordinates = false;
+        // #endregion
+        // #region V1 options
         GLTFFileLoader.IncrementalLoading = true;
+        GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
     }());
     BABYLON.GLTFFileLoader = GLTFFileLoader;
@@ -1804,10 +1841,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 +1929,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);
@@ -3608,21 +3656,38 @@ var BABYLON;
                 }
                 return skeletons;
             };
-            GLTFLoader.prototype._getAnimationTargets = function () {
-                var targets = new Array();
+            GLTFLoader.prototype._startAnimations = function () {
                 var animations = this._gltf.animations;
-                if (animations) {
-                    for (var _i = 0, animations_1 = animations; _i < animations_1.length; _i++) {
-                        var animation = animations_1[_i];
-                        targets.push.apply(targets, animation.targets);
-                    }
+                if (!animations) {
+                    return;
                 }
-                return targets;
-            };
-            GLTFLoader.prototype._startAnimations = function () {
-                for (var _i = 0, _a = this._getAnimationTargets(); _i < _a.length; _i++) {
-                    var target = _a[_i];
-                    this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                switch (this._parent.animationStartMode) {
+                    case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
+                        // do nothing
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.FIRST: {
+                        var animation = animations[0];
+                        for (var _i = 0, _a = animation.targets; _i < _a.length; _i++) {
+                            var target = _a[_i];
+                            this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                        }
+                        break;
+                    }
+                    case BABYLON.GLTFLoaderAnimationStartMode.ALL: {
+                        for (var _b = 0, animations_1 = animations; _b < animations_1.length; _b++) {
+                            var animation = animations_1[_b];
+                            for (var _c = 0, _d = animation.targets; _c < _d.length; _c++) {
+                                var target = _d[_c];
+                                this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                            }
+                        }
+                        break;
+                    }
+                    default: {
+                        BABYLON.Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                        return;
+                    }
                 }
             };
             GLTFLoader.prototype._loadDefaultScene = function (nodeNames) {
@@ -3633,6 +3698,7 @@ var BABYLON;
                 this._loadScene("#/scenes/" + scene.index, scene, nodeNames);
             };
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
+                var _this = this;
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
                 switch (this._parent.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -3643,10 +3709,6 @@ var BABYLON;
                         }
                         break;
                     }
-                    case BABYLON.GLTFLoaderCoordinateSystemMode.PASS_THROUGH: {
-                        // do nothing
-                        break;
-                    }
                     case BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED: {
                         this._babylonScene.useRightHandedSystem = true;
                         break;
@@ -3672,6 +3734,7 @@ var BABYLON;
                     this._traverseNodes(context, nodeIndices, function (node) {
                         if (nodeNames.indexOf(node.name) !== -1) {
                             filteredNodeIndices_1.push(node.index);
+                            node.parent = _this._rootNode;
                             return false;
                         }
                         return true;
@@ -3767,32 +3830,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;
@@ -4876,10 +4959,6 @@ var BABYLON;
             };
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
                 var _this = this;
-                if (!this._parent.compileMaterials) {
-                    onSuccess();
-                    return;
-                }
                 if (this._parent.useClipPlane) {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
@@ -4910,6 +4989,9 @@ var BABYLON;
                             }
                         }
                     }
+                    else {
+                        remaining++;
+                    }
                 }
                 if (remaining === 0) {
                     onSuccess();
@@ -4929,6 +5011,13 @@ var BABYLON;
                             }
                         }
                     }
+                    else if (mesh.material !== null) {
+                        this._compileMaterialAsync(mesh.material, mesh, function () {
+                            if (--remaining === 0) {
+                                onSuccess();
+                            }
+                        });
+                    }
                 }
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {

文件差异内容过多而无法显示
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 58 - 7
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -99,9 +99,28 @@ declare module BABYLON {
 
 declare module BABYLON {
     enum GLTFLoaderCoordinateSystemMode {
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         AUTO = 0,
-        PASS_THROUGH = 1,
-        FORCE_RIGHT_HANDED = 2,
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
+        FORCE_RIGHT_HANDED = 1,
+    }
+    enum GLTFLoaderAnimationStartMode {
+        /**
+         * No animation will start.
+         */
+        NONE = 0,
+        /**
+         * The first animation will start.
+         */
+        FIRST = 1,
+        /**
+         * All animations will start.
+         */
+        ALL = 2,
     }
     interface IGLTFLoaderData {
         json: Object;
@@ -114,25 +133,58 @@ declare module BABYLON {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        /**
+         * Raised when the asset has been parsed.
+         * The data.json property stores the glTF JSON.
+         * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+         */
         onParsed: (data: IGLTFLoaderData) => void;
-        static HomogeneousCoordinates: boolean;
         static IncrementalLoading: boolean;
+        static HomogeneousCoordinates: boolean;
+        /**
+         * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+         */
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        /**
+         * The animation start mode (NONE, FIRST, ALL).
+         */
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        /**
+         * Set to true to compile materials before raising the success callback.
+         */
         compileMaterials: boolean;
-        compileShadowGenerators: boolean;
+        /**
+         * Set to true to also compile materials with clip planes.
+         */
         useClipPlane: boolean;
+        /**
+         * Set to true to compile shadow generators before raising the success callback.
+         */
+        compileShadowGenerators: boolean;
+        /**
+         * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         */
         onMeshLoaded: (mesh: AbstractMesh) => void;
+        /**
+         * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+         */
         onTextureLoaded: (texture: BaseTexture) => void;
+        /**
+         * Raised when the loader creates a material after parsing the glTF properties of the material.
+         */
         onMaterialLoaded: (material: Material) => void;
         /**
-         * Raised when the asset is completely loaded, just before the loader is disposed.
+         * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
-         * For assets without LODs, raised when the model is complete just after onSuccess.
+         * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
         onComplete: () => void;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
@@ -981,7 +1033,6 @@ declare module BABYLON.GLTF2 {
         private _loadData(data);
         private _getMeshes();
         private _getSkeletons();
-        private _getAnimationTargets();
         private _startAnimations();
         private _loadDefaultScene(nodeNames);
         private _loadScene(context, scene, nodeNames);

+ 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);

文件差异内容过多而无法显示
+ 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);

文件差异内容过多而无法显示
+ 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[]>;

+ 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);

+ 75 - 38
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -85,7 +85,7 @@ module BABYLON.GLTF2 {
 
         // IE 11 Compatibility.
         private static _progressEventFactory: (name: string, data: IProgressEventData) => ProgressEvent;
-        
+
         private static _createProgressEventByConstructor(name: string, data: IProgressEventData): ProgressEvent {
             return new ProgressEvent(name, data);
         }
@@ -272,22 +272,36 @@ module BABYLON.GLTF2 {
             return skeletons;
         }
 
-        private _getAnimationTargets(): any[] {
-            const targets = new Array();
-
+        private _startAnimations(): void {
             const animations = this._gltf.animations;
-            if (animations) {
-                for (const animation of animations) {
-                    targets.push(...animation.targets);
-                }
+            if (!animations) {
+                return;
             }
 
-            return targets;
-        }
-
-        private _startAnimations(): void {
-            for (const target of this._getAnimationTargets()) { 
-                this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+            switch (this._parent.animationStartMode) {
+                case GLTFLoaderAnimationStartMode.NONE: {
+                    // do nothing
+                    break;
+                }
+                case GLTFLoaderAnimationStartMode.FIRST: {
+                    const animation = animations[0];
+                    for (const target of animation.targets) {
+                        this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                    }
+                    break;
+                }
+                case GLTFLoaderAnimationStartMode.ALL: {
+                    for (const animation of animations) {
+                        for (const target of animation.targets) {
+                            this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true);
+                        }
+                    }
+                    break;
+                }
+                default: {
+                    Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                    return;
+                }
             }
         }
 
@@ -306,16 +320,12 @@ module BABYLON.GLTF2 {
             switch (this._parent.coordinateSystemMode) {
                 case GLTFLoaderCoordinateSystemMode.AUTO: {
                     if (!this._babylonScene.useRightHandedSystem) {
-                        this._rootNode.rotation = [ 0, 1, 0, 0 ];
-                        this._rootNode.scale = [ 1, 1, -1 ];
+                        this._rootNode.rotation = [0, 1, 0, 0];
+                        this._rootNode.scale = [1, 1, -1];
                         this._loadTransform(this._rootNode);
                     }
                     break;
                 }
-                case GLTFLoaderCoordinateSystemMode.PASS_THROUGH: {
-                    // do nothing
-                    break;
-                }
                 case GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED: {
                     this._babylonScene.useRightHandedSystem = true;
                     break;
@@ -346,6 +356,7 @@ module BABYLON.GLTF2 {
                 this._traverseNodes(context, nodeIndices, node => {
                     if (nodeNames.indexOf(node.name) !== -1) {
                         filteredNodeIndices.push(node.index);
+                        node.parent = this._rootNode;
                         return false;
                     }
 
@@ -463,14 +474,10 @@ 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);
@@ -482,11 +489,36 @@ module BABYLON.GLTF2 {
                         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 {
@@ -774,7 +806,7 @@ module BABYLON.GLTF2 {
                             // Tangent data for morph targets is stored as xyz delta.
                             // The vertexData.tangent is stored as xyzw.
                             // So we need to skip every fourth vertexData.tangent.
-                            for (let i = 0, j = 0; i < values.length; i++, j++) {
+                            for (let i = 0, j = 0; i < values.length; i++ , j++) {
                                 values[i] += vertexData.tangents![j];
                                 if ((i + 1) % 3 == 0) {
                                     j++;
@@ -1317,7 +1349,7 @@ module BABYLON.GLTF2 {
             this._loaderTrackers.push(tracker);
 
             this._addLoaderPendingData(tracker);
-            
+
             action();
 
             this._removeLoaderPendingData(tracker);
@@ -1621,7 +1653,7 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private static _AssignIndices(array?: Array<{index: number}>): void {
+        private static _AssignIndices(array?: Array<{ index: number }>): void {
             if (array) {
                 for (let index = 0; index < array.length; index++) {
                     array[index].index = index;
@@ -1703,11 +1735,6 @@ module BABYLON.GLTF2 {
         }
 
         private _compileMaterialAsync(babylonMaterial: Material, babylonMesh: AbstractMesh, onSuccess: () => void): void {
-            if (!this._parent.compileMaterials) {
-                onSuccess();
-                return;
-            }
-
             if (this._parent.useClipPlane) {
                 babylonMaterial.forceCompilation(babylonMesh, () => {
                     babylonMaterial.forceCompilation(babylonMesh, () => {
@@ -1739,6 +1766,9 @@ module BABYLON.GLTF2 {
                         }
                     }
                 }
+                else {
+                    remaining++;
+                }
             }
 
             if (remaining === 0) {
@@ -1758,6 +1788,13 @@ module BABYLON.GLTF2 {
                         }
                     }
                 }
+                else if (mesh.material !== null) {
+                    this._compileMaterialAsync(mesh.material, mesh, () => {
+                        if (--remaining === 0) {
+                            onSuccess();
+                        }
+                    })
+                }
             }
         }
 

+ 131 - 52
loaders/src/glTF/README.md

@@ -1,17 +1,16 @@
 # Babylon.js glTF File Loader
 
-# Usage
 The glTF file loader is a SceneLoader plugin.
 
-[glTF2 Playground example](http://www.babylonjs-playground.com/#6MZV8R)
+[Simple Playground Example](http://www.babylonjs-playground.com/#2IK4U7)
 
-## Step 1 - Include the glTF File Loader
+## Setup
 
 **Full Version**
 
 This loader supports both glTF 1.0 and 2.0 and will use the correct loader based on the glTF version string.
 
-```
+```HTML
 <script src="babylon.js"></script>
 <script src="babylon.glTFFileLoader.js"></script>
 ```
@@ -20,7 +19,7 @@ This loader supports both glTF 1.0 and 2.0 and will use the correct loader based
 
 This loader supports only glTF 1.0 and will fail to load glTF 2.0.
 
-```
+```HTML
 <script src="babylon.js"></script>
 <script src="babylon.glTF1FileLoader.js"></script>
 ```
@@ -29,75 +28,155 @@ This loader supports only glTF 1.0 and will fail to load glTF 2.0.
 
 This loader supports only glTF 2.0 and will fail to load glTF 1.0.
 
-```
+```HTML
 <script src="babylon.js"></script>
 <script src="babylon.glTF2FileLoader.js"></script>
 ```
 
-## Step 2 - Call the Scene Loader
+## Loading the Scene
+The Load function loads a glTF asset into a new scene.
+```JavaScript
+BABYLON.SceneLoader.Load("./", "duck.gltf", engine, function (scene) {
+    // do something with the scene
+});
 ```
-BABYLON.SceneLoader.Load("./", "duck.gltf", engine, function (scene) { 
-   // do somethings with the scene
+
+The Append function appends a glTF file to an existing scene.
+```JavaScript
+BABYLON.SceneLoader.Append("./", "duck.gltf", scene, function (scene) {
+    // do something with the scene
 });
 ```
 
-You can also call the ImportMesh function and import specific meshes
+The ImportMesh function imports specific meshes from a glTF asset to an existing scene and returns the imported meshes and skeletons.
+```JavaScript
+// The first parameter can be set to null to load all meshes and skeletons
+BABYLON.SceneLoader.ImportMesh(["myMesh1", "myMesh2"], "./", "duck.gltf", scene, function (meshes, particleSystems, skeletons) {
+    // do something with the meshes and skeletons
+    // particleSystems are always null for glTF assets
+});
 ```
-// meshesNames can be set to "null" to load all meshes and skeletons
-BABYLON.SceneLoader.ImportMesh(["myMesh1", "myMesh2", "..."], "./", "duck.gltf", scene, function (meshes, particleSystems, skeletons) { 
-   // do somethings with the meshes, particleSystems (not handled in glTF files) and skeletons
+
+## Advanced
+
+The SceneLoader returns the glTF loader instance to enable setting properties per instance.
+
+```JavaScript
+var loader = BABYLON.SceneLoader.Load("./", "duck.gltf", engine, function (scene) {
+    // do something with the scene
 });
+
+// do something with the loader
+// loader.<option1> = <...>
+// loader.<option2> = <...>
+// loader.dispose();
 ```
 
-You can also append a glTF file to a scene. When using `SceneLoader.Append`, configure the scene to use right handed system by setting the property `useRightHandedSystem` to true. 
+#### onParsed
+Raised when the asset has been parsed. The `data.json` property stores the glTF JSON. The `data.bin` property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
 
+```JavaScript
+loader.onParsed = function (data) {
+    // do something with the data
+};
 ```
-// glTF Files use right handed system 
-scene.useRightHandedSystem = true;
 
-// Append sample glTF model to scene
-BABYLON.SceneLoader.Append("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoomBox/glTF/", "BoomBox.gltf", scene, function (scene) {
-}, null, function (scene) {
-    alert("error");
-});
+### Version 1 Only
+
+#### IncrementalLoading
+Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders. Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled. Defaults to true.
+
+```JavaScript
+BABYLON.GLTFFileLoader.IncrementalLoading = false;
+```
+
+#### HomogeneousCoordinates
+Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters. Defaults to false.
+
+```JavaScript
+BABYLON.GLTFFileLoader.HomogeneousCoordinates = true;
+```
+
+### Version 2 Only
+
+#### coordinateSystemMode
+The coordinate system mode (AUTO, FORCE_RIGHT_HANDED). Defaults to AUTO.
+
+```JavaScript
+loader.coordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED;
 ```
 
-## Step 3 (V1 Only) - Optionally Specify Flags
+#### animationStartMode
+The animation start mode (NONE, FIRST, ALL). Defaults to FIRST.
 
-If you want to disable incremental loading, you can set the property `IncrementalLoading` to false.
-Then, you'll be able to be called back with all geometries and shaders loaded. Textures are always loaded asynchronously. For example, you can retrieve the real bounding infos of a mesh loaded when incremental loading is disabled.
+```JavaScript
+loader.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.NONE;
 ```
-BABYLON.GLTFFileLoader.IncrementalLoading = false; // true by default
+
+#### compileMaterials
+Set to true to compile materials before raising the success callback. Defaults to false.
+
+```JavaScript
+loader.compileMaterials = true;
+```
+
+#### useClipPlane
+Set to true to also compile materials with clip planes. Defaults to false.
+
+```JavaScript
+loader.useClipPlane = true;
 ```
 
-In order to work with homogeneous coordinates (that can be available with some converters and exporters):
+#### compileShadowGenerators
+Set to true to compile shadow generators before raising the success callback. Defaults to false.
+
+```JavaScript
+loader.compileShadowGenerators = true;
 ```
-BABYLON.GLTFFileLoader.HomogeneousCoordinates = true; // false by default
+
+#### onMeshLoaded
+Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+
+```JavaScript
+loader.onMeshLoaded = function (mesh) {
+    // do something with the mesh
+};
 ```
 
-# Supported Features
-* Load scenes (SceneLoader.Load and SceneLoader.Append)
-* Support of ImportMesh function
-* Import geometries
-    * From binary files
-    * From base64 buffers
-* Import lights (V1 only)
-* Import cameras
-* Import and set custom shaders (V1 only)
-    * Automatically bind attributes
-    * Automatically bind matrices
-    * Set uniforms
-* Import and set animations
-* Skinning (BETA, sometimes wrong on tricky models)
-    * Skeletons
-    * Hardware skinning (shaders support)
-    * Bones import
-* Handle dummy nodes (empty nodes)
-* PBR materials (V2 only)
+#### onTextureLoaded
+Raised when the loader creates a texture 
+after parsing the glTF properties of the texture.
 
-# Future Improvements
-* Test on more geometries
-* Test on more animated models
-* Test on more skinned models
-* Improve shaders support (V1 only) (glitches with samplers can appear in particular configurations)
-* Add support for morph targets (V2 only)
+```JavaScript
+loader.onTextureLoaded = function (texture) {
+    // do something with the texture
+};
+```
+
+#### onMaterialLoaded
+Raised when the loader creates a material after parsing the glTF properties of the material.
+
+```JavaScript
+loader.onMaterialLoaded = function (material) {
+    // do something with the material
+};
+```
+
+#### onComplete
+Raised when the asset is completely loaded, immediately before the loader is disposed.
+For assets with LODs, raised when all of the LODs are complete.
+For assets without LODs, raised when the model is complete, immediately after onSuccess.
+
+```JavaScript
+loader.onComplete = function () {
+    // do something when loading is complete
+};
+```
+
+#### dispose
+Disposes the loader, releases resources during load, and cancels any outstanding requests.
+
+```JavaScript
+// Cancel loading of the current glTF asset.
+loader.dispose();
+```

+ 80 - 13
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -2,17 +2,34 @@
 
 module BABYLON {
     export enum GLTFLoaderCoordinateSystemMode {
-        // Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene (scene.useRightHandedSystem).
-        // NOTE: When scene.useRightHandedSystem is false, an additional transform will be added to the root to transform the data from right-handed to left-handed.
+        /**
+         * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene.
+         */
         AUTO,
 
-        // The glTF right-handed data is not transformed in any form and is loaded directly.
-        PASS_THROUGH,
-
-        // Sets the useRightHandedSystem flag on the scene.
+        /**
+         * Sets the useRightHandedSystem flag on the scene.
+         */
         FORCE_RIGHT_HANDED,
     }
 
+    export enum GLTFLoaderAnimationStartMode {
+        /**
+         * No animation will start.
+         */
+        NONE,
+
+        /**
+         * The first animation will start.
+         */
+        FIRST,
+
+        /**
+         * All animations will start.
+         */
+        ALL,
+    }
+
     export interface IGLTFLoaderData {
         json: Object;
         bin: Nullable<ArrayBufferView>;
@@ -27,29 +44,76 @@ module BABYLON {
         public static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         public static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
 
-        // Common options
+        // #region Common options
+
+        /**
+         * Raised when the asset has been parsed.
+         * The data.json property stores the glTF JSON.
+         * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+         */
         public onParsed: (data: IGLTFLoaderData) => void;
 
-        // V1 options
-        public static HomogeneousCoordinates = false;
+        // #endregion
+
+        // #region V1 options
+
         public static IncrementalLoading = true;
 
-        // V2 options
+        public static HomogeneousCoordinates = false;
+
+        // #endregion
+
+        // #region V2 options
+
+        /**
+         * The coordinate system mode (AUTO, FORCE_RIGHT_HANDED).
+         */
         public coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+
+        /**
+         * The animation start mode (NONE, FIRST, ALL).
+         */
+        public animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+
+        /**
+         * Set to true to compile materials before raising the success callback.
+         */
         public compileMaterials = false;
-        public compileShadowGenerators = false;
+
+        /**
+         * Set to true to also compile materials with clip planes.
+         */
         public useClipPlane = false;
+
+        /**
+         * Set to true to compile shadow generators before raising the success callback.
+         */
+        public compileShadowGenerators = false;
+
+        /**
+         * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+         */
         public onMeshLoaded: (mesh: AbstractMesh) => void;
+
+        /**
+         * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+         */
         public onTextureLoaded: (texture: BaseTexture) => void;
+
+        /**
+         * Raised when the loader creates a material after parsing the glTF properties of the material.
+         */
         public onMaterialLoaded: (material: Material) => void;
 
         /**
-         * Raised when the asset is completely loaded, just before the loader is disposed.
+         * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
-         * For assets without LODs, raised when the model is complete just after onSuccess.
+         * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
         public onComplete: () => void;
 
+        // #endregion
+
         private _loader: IGLTFLoader;
 
         public name = "gltf";
@@ -59,6 +123,9 @@ module BABYLON {
             ".glb": { isBinary: true }
         };
 
+        /**
+         * Disposes the loader, releases resources during load, and cancels any outstanding requests.
+         */
         public dispose(): void {
             if (this._loader) {
                 this._loader.dispose();

+ 8 - 8
sandbox/index.js

@@ -1,4 +1,5 @@
 /// <reference path="../dist/preview release/babylon.d.ts" />
+/// <reference path="../dist/preview release/loaders/babylon.glTFFileLoader.d.ts" />
 
 if (BABYLON.Engine.isSupported()) {
     var canvas = document.getElementById("renderCanvas");
@@ -21,7 +22,7 @@ if (BABYLON.Engine.isSupported()) {
     var currentPluginName;
     var toExecuteAfterSceneCreation;
 
-    canvas.addEventListener("contextmenu", function(evt) {
+    canvas.addEventListener("contextmenu", function (evt) {
         evt.preventDefault();
     }, false);
 
@@ -33,14 +34,13 @@ if (BABYLON.Engine.isSupported()) {
 
     // Setting up some GLTF values
     BABYLON.GLTFFileLoader.IncrementalLoading = false;
-    BABYLON.SceneLoader.OnPluginActivatedObservable.add(function(plugin) {
+    BABYLON.SceneLoader.OnPluginActivatedObservable.add(function (plugin) {
         currentPluginName = plugin.name;
 
-        if (plugin.name !== "gltf") {
-            return;
+        if (plugin.name === "gltf" && plugin instanceof BABYLON.GLTFFileLoader) {
+            plugin.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.ALL;
+            //  plugin.compileMaterials = true;
         }
-
-        plugin.compileMaterials = true;
     });
 
     // Resize
@@ -100,7 +100,7 @@ if (BABYLON.Engine.isSupported()) {
             currentScene.activeCamera.pinchDeltaPercentage = 0.01;
         }
 
-        currentScene.activeCamera.attachControl(canvas); 
+        currentScene.activeCamera.attachControl(canvas);
 
         // Environment
         if (currentPluginName === "gltf") {
@@ -236,7 +236,7 @@ if (BABYLON.Engine.isSupported()) {
     }
 }
 
-function sizeScene () {
+function sizeScene() {
     let divInspWrapper = document.getElementsByClassName('insp-wrapper')[0];
     if (divInspWrapper) {
         let divFooter = document.getElementsByClassName('footer')[0];

+ 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]);
                     }

+ 1 - 1
src/Mesh/babylon.transformNode.ts

@@ -456,7 +456,7 @@ module BABYLON {
                 var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
                 var scale = Tmp.Vector3[1];
-                
+
                 if (this.parent && this.parent.computeWorldMatrix) {
                     this.parent.computeWorldMatrix(true);
                 }

+ 7 - 10
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,28 +3398,24 @@
                     // 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 {
                 // Animations
-                var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
+                var deltaTime = this.useConstantAnimationDeltaTime ? 16 : Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
                 this._animationRatio = deltaTime * (60.0 / 1000.0);
                 this._animate();
                 this.onAfterAnimationsObservable.notifyObservers(this);

二进制
tests/validation/ReferenceImages/GUI.png


二进制
tests/validation/ReferenceImages/charting.png


二进制
tests/validation/ReferenceImages/fresnel.png


二进制
tests/validation/ReferenceImages/gltf1CesiumMan.png


二进制
tests/validation/ReferenceImages/pbrrough.png


二进制
tests/validation/ReferenceImages/procedural.png


二进制
tests/validation/ReferenceImages/upVector.png


+ 14 - 9
tests/validation/config.json

@@ -318,6 +318,20 @@
       "referenceImage": "GUI.png"
     },
     {
+      "title": "Up Vector",
+      "renderCount": 20,
+      "playgroundId": "#2FNBTG#27",
+      "referenceImage": "upVector.png"
+    },
+    {
+      "title": "Procedural textures",
+      "renderCount": 10,
+      "scriptToRun": "/Demos/Procedural/proceduralTexture.js",
+      "functionToCall": "CreateProceduralTextureTestScene",
+      "referenceImage": "procedural.png",
+      "replace": "./land, https://cdn.rawgit.com/BabylonJS/Website/06ecbea7/Demos/Procedural/land"
+    },
+    {
       "title": "Water material (only visual check)",
       "renderCount": 10,
       "scriptToRun": "/Demos/WaterMaterial/water.js",
@@ -335,15 +349,6 @@
       "onlyVisual": true
     },
     {
-      "title": "Procedural textures (only visual check)",
-      "renderCount": 10,
-      "scriptToRun": "/Demos/Procedural/proceduralTexture.js",
-      "functionToCall": "CreateProceduralTextureTestScene",
-      "referenceImage": "procedural.png",
-      "replace": "./land, https://cdn.rawgit.com/BabylonJS/Website/06ecbea7/Demos/Procedural/land",
-      "onlyVisual": true
-    },
-    {
       "title": "Instanced Bones (only visual check)",
       "renderCount": 10,
       "scriptToRun": "/Demos/InstancedBones/bones2.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--;