sebastien 6 年之前
父节点
当前提交
ad2b3c386e
共有 100 个文件被更改,包括 31602 次插入30179 次删除
  1. 6946 6874
      Playground/babylon.d.txt
  2. 22079 22013
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 443 218
      dist/preview release/babylon.max.js
  5. 443 218
      dist/preview release/babylon.no-module.max.js
  6. 1 1
      dist/preview release/babylon.worker.js
  7. 443 218
      dist/preview release/es6.js
  8. 1 1
      dist/preview release/glTF2Interface/package.json
  9. 1 1
      dist/preview release/gui/babylon.gui.js
  10. 1 1
      dist/preview release/gui/babylon.gui.min.js
  11. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  12. 2 2
      dist/preview release/gui/package.json
  13. 7 7
      dist/preview release/inspector/babylon.inspector.bundle.js
  14. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  15. 5 5
      dist/preview release/inspector/package.json
  16. 1 1
      dist/preview release/inspector/readme.md
  17. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  18. 41 49
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  19. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  20. 41 49
      dist/preview release/loaders/babylon.glTFFileLoader.js
  21. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  22. 41 49
      dist/preview release/loaders/babylonjs.loaders.js
  23. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  24. 3 3
      dist/preview release/loaders/package.json
  25. 2 2
      dist/preview release/materialsLibrary/package.json
  26. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  27. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  28. 1 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  29. 2 2
      dist/preview release/postProcessesLibrary/package.json
  30. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  31. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  32. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  33. 3 3
      dist/preview release/serializers/package.json
  34. 1 1
      dist/preview release/viewer/babylon.viewer.js
  35. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  36. 8 0
      dist/preview release/what's new.md
  37. 1 1
      gui/src/2D/controls/container.ts
  38. 2 0
      gui/src/2D/controls/control.ts
  39. 5 5
      inspector/src/components/actionTabs/actionTabs.scss
  40. 12 1
      inspector/src/components/actionTabs/lineContainerComponent.tsx
  41. 2 2
      inspector/src/components/actionTabs/lines/color3LineComponent.tsx
  42. 5 2
      inspector/src/components/actionTabs/lines/fileButtonLineComponent.tsx
  43. 25 4
      inspector/src/components/actionTabs/lines/floatLineComponent.tsx
  44. 1 3
      inspector/src/components/actionTabs/lines/optionsLineComponent.tsx
  45. 1 0
      inspector/src/components/actionTabs/lines/radioLineComponent.tsx
  46. 41 13
      inspector/src/components/actionTabs/lines/sliderLineComponent.tsx
  47. 7 1
      inspector/src/components/actionTabs/lines/textInputLineComponent.tsx
  48. 3 3
      inspector/src/components/actionTabs/lines/textureLineComponent.tsx
  49. 10 2
      inspector/src/components/actionTabs/lines/vector3LineComponent.tsx
  50. 32 2
      inspector/src/components/actionTabs/tabs/debugTabComponent.tsx
  51. 63 10
      inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx
  52. 140 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animationGroupPropertyGridComponent.tsx
  53. 16 24
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/arcRotateCameraPropertyGridComponent.tsx
  54. 8 6
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/commonCameraPropertyGridComponent.tsx
  55. 5 13
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx
  56. 0 102
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/propertyGridTabComponent.tsx
  57. 5 3
      inspector/src/components/actionTabs/tabs/propertyGrids/fogPropertyGridComponent.tsx
  58. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/colorPickerPropertyGridComponent.tsx
  59. 20 18
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent.tsx
  60. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/controlPropertyGridComponent.tsx
  61. 13 11
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/inputTextPropertyGridComponent.tsx
  62. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/textBlockPropertyGridComponent.tsx
  63. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonLightPropertyGridComponent.tsx
  64. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx
  65. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx
  66. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/hemisphericLightPropertyGridComponent.tsx
  67. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/pointLightPropertyGridComponent.tsx
  68. 6 4
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/spotLightPropertyGridComponent.tsx
  69. 9 0
      inspector/src/components/actionTabs/tabs/propertyGrids/lockObject.ts
  70. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/backgroundMaterialPropertyGridComponent.tsx
  71. 3 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/commonMaterialPropertyGridComponent.tsx
  72. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/materialPropertyGridComponent.tsx
  73. 5 3
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx
  74. 41 9
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/standardMaterialPropertyGridComponent.tsx
  75. 54 15
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  76. 37 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx
  77. 2 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/transformNodePropertyGridComponent.tsx
  78. 60 7
      inspector/src/components/actionTabs/tabs/propertyGrids/scenePropertyGridComponent.tsx
  79. 3 3
      inspector/src/components/actionTabs/tabs/tools/gltfComponent.tsx
  80. 30 0
      inspector/src/components/sceneExplorer/entities/animationGroupTreeItemComponent.tsx
  81. 7 3
      inspector/src/components/sceneExplorer/entities/cameraTreeItemComponent.tsx
  82. 2 2
      inspector/src/components/sceneExplorer/entities/transformNodeTreeItemComponent.tsx
  83. 26 3
      inspector/src/components/sceneExplorer/sceneExplorer.scss
  84. 45 0
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  85. 15 7
      inspector/src/components/sceneExplorer/treeItemComponent.tsx
  86. 6 1
      inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx
  87. 25 37
      loaders/src/glTF/2.0/glTFLoader.ts
  88. 1 1
      package.json
  89. 1 0
      sandbox/index.css
  90. 38 25
      sandbox/index.js
  91. 28 0
      src/Animations/animationGroup.ts
  92. 20 0
      src/Bones/bone.ts
  93. 22 3
      src/Bones/skeleton.ts
  94. 50 46
      src/Debug/axesViewer.ts
  95. 29 15
      src/Debug/physicsViewer.ts
  96. 18 3
      src/Debug/skeletonViewer.ts
  97. 2 2
      src/Engines/engine.ts
  98. 23 14
      src/Gizmos/axisDragGizmo.ts
  99. 12 2
      src/Gizmos/gizmoManager.ts
  100. 0 0
      src/Loading/Plugins/babylonFileLoader.ts

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


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


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


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


文件差异内容过多而无法显示
+ 443 - 218
dist/preview release/babylon.no-module.max.js


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


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


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

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.0.0-alpha.7",
+    "version": "4.0.0-alpha.8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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


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


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


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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.0.0-alpha.7",
+    "version": "4.0.0-alpha.8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.7"
+        "babylonjs": "4.0.0-alpha.8"
     },
     "engines": {
         "node": "*"

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


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


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

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

+ 1 - 1
dist/preview release/inspector/readme.md

@@ -13,7 +13,7 @@ If you wish however to use a different version of the inspector or host it on yo
 
 The latest compiled js file is offered on our public CDN here:
 
-* https://preview.babylonjs.com/inspector/babylonjs.inspector.bundle.js
+* https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js
 
 ## NPM
 

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


+ 41 - 49
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -1114,6 +1114,19 @@ var BABYLON;
                         }));
                     }
                 }
+                // Link all Babylon bones for each glTF node with the corresponding Babylon transform node.
+                // A glTF joint is a pointer to a glTF node in the glTF node hierarchy similar to Unity3D.
+                if (this.gltf.nodes) {
+                    for (var _b = 0, _c = this.gltf.nodes; _b < _c.length; _b++) {
+                        var node = _c[_b];
+                        if (node._babylonTransformNode && node._babylonBones) {
+                            for (var _d = 0, _e = node._babylonBones; _d < _e.length; _d++) {
+                                var babylonBone = _e[_d];
+                                babylonBone.linkTransformNode(node._babylonTransformNode);
+                            }
+                        }
+                    }
+                }
                 promises.push(this._loadAnimationsAsync());
                 this.logClose();
                 return Promise.all(promises).then(function () { });
@@ -1125,9 +1138,6 @@ var BABYLON;
                         callback(babylonMesh);
                     }
                 }
-                else if (node._babylonTransformNode instanceof BABYLON.AbstractMesh) {
-                    callback(node._babylonTransformNode);
-                }
             };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = new Array();
@@ -1226,20 +1236,12 @@ var BABYLON;
                         }));
                     }
                     if (node.children) {
-                        var _loop_1 = function (index) {
+                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
+                            var index = _a[_i];
                             var childNode = ArrayItem.Get(context + "/children/" + index, _this.gltf.nodes, index);
                             promises.push(_this.loadNodeAsync("/nodes/" + childNode.index, childNode, function (childBabylonMesh) {
-                                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                                if (childNode.skin != undefined) {
-                                    childBabylonMesh.parent = _this._rootBabylonMesh;
-                                    return;
-                                }
                                 childBabylonMesh.parent = babylonTransformNode;
                             }));
-                        };
-                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
-                            var index = _a[_i];
-                            _loop_1(index);
                         }
                     }
                     assign(babylonTransformNode);
@@ -1276,16 +1278,16 @@ var BABYLON;
                     var primitive = mesh.primitives[0];
                     promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, name, node, mesh, primitive, function (babylonMesh) {
                         node._babylonTransformNode = babylonMesh;
+                        node._primitiveBabylonMeshes = [babylonMesh];
                     }));
                 }
                 else {
-                    var babylonTransformNode_1 = new BABYLON.TransformNode(name, this.babylonScene);
-                    node._babylonTransformNode = babylonTransformNode_1;
+                    node._babylonTransformNode = new BABYLON.TransformNode(name, this.babylonScene);
+                    node._primitiveBabylonMeshes = [];
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                         var primitive = primitives_1[_i];
                         promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, name + "_primitive" + primitive.index, node, mesh, primitive, function (babylonMesh) {
-                            babylonMesh.parent = babylonTransformNode_1;
-                            node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
+                            babylonMesh.parent = node._babylonTransformNode;
                             node._primitiveBabylonMeshes.push(babylonMesh);
                         }));
                     }
@@ -1513,13 +1515,13 @@ var BABYLON;
                     });
                 };
                 if (skin._data) {
-                    var data_1 = skin._data;
-                    return data_1.promise.then(function () {
-                        assignSkeleton(data_1.babylonSkeleton);
-                    });
+                    assignSkeleton(skin._data.babylonSkeleton);
+                    return skin._data.promise;
                 }
                 var skeletonId = "skeleton" + skin.index;
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
+                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                babylonSkeleton.overrideMesh = this._rootBabylonMesh;
                 this._loadBones(context, skin, babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
                 var promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
@@ -1684,11 +1686,6 @@ var BABYLON;
                     (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonTransformNode)) {
                     return Promise.resolve();
                 }
-                // Ignore animations targeting TRS of skinned nodes.
-                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                if (targetNode.skin != undefined && channel.target.path !== "weights" /* WEIGHTS */) {
-                    return Promise.resolve();
-                }
                 var sampler = ArrayItem.Get(context + "/sampler", animation.samplers, channel.sampler);
                 return this._loadAnimationSamplerAsync(animationContext + "/samplers/" + channel.sampler, sampler).then(function (data) {
                     var targetPath;
@@ -1788,7 +1785,7 @@ var BABYLON;
                         keys[frameIndex] = getNextKey(frameIndex);
                     }
                     if (targetPath === "influence") {
-                        var _loop_2 = function (targetIndex) {
+                        var _loop_1 = function (targetIndex) {
                             var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                             var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                             babylonAnimation.setKeys(keys.map(function (key) { return ({
@@ -1805,27 +1802,15 @@ var BABYLON;
                             });
                         };
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
-                            _loop_2(targetIndex);
+                            _loop_1(targetIndex);
                         }
                     }
                     else {
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        var babylonTransformNode = targetNode._babylonTransformNode;
-                        var babylonBones = targetNode._babylonBones;
-                        if (babylonBones) {
-                            var babylonAnimationTargets = [babylonTransformNode].concat(babylonBones);
-                            for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
-                                var babylonAnimationTarget = babylonAnimationTargets_1[_i];
-                                babylonAnimationTarget.animations.push(babylonAnimation);
-                            }
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
-                        }
-                        else {
-                            babylonTransformNode.animations.push(babylonAnimation);
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
-                        }
+                        targetNode._babylonTransformNode.animations.push(babylonAnimation);
+                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonTransformNode);
                     }
                 });
             };
@@ -2220,8 +2205,14 @@ var BABYLON;
                 this.logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? GLTFLoader._DefaultSampler : ArrayItem.Get(context + "/sampler", this.gltf.samplers, texture.sampler));
                 var samplerData = this._loadSampler("/samplers/" + sampler.index, sampler);
+                var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
+                var textureURL = null;
+                if (image.uri && !BABYLON.Tools.IsBase64(image.uri) && this.babylonScene.getEngine().textureFormatInUse) {
+                    // If an image uri and a texture format is set like (eg. KTX) load from url instead of blob to support texture format and fallback
+                    textureURL = this._uniqueRootUrl + image.uri;
+                }
                 var deferred = new BABYLON.Deferred();
-                var babylonTexture = new BABYLON.Texture(null, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
+                var babylonTexture = new BABYLON.Texture(textureURL, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
                     if (!_this._disposed) {
                         deferred.resolve();
                     }
@@ -2231,14 +2222,15 @@ var BABYLON;
                     }
                 });
                 promises.push(deferred.promise);
+                if (!textureURL) {
+                    promises.push(this.loadImageAsync("/images/" + image.index, image).then(function (data) {
+                        var name = image.uri || _this._fileName + "#image" + image.index;
+                        var dataUrl = "data:" + _this._uniqueRootUrl + name;
+                        babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
+                    }));
+                }
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
-                promises.push(this.loadImageAsync("/images/" + image.index, image).then(function (data) {
-                    var name = image.uri || _this._fileName + "#image" + image.index;
-                    var dataUrl = "data:" + _this._uniqueRootUrl + name;
-                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
-                }));
                 assign(babylonTexture);
                 this.logClose();
                 return Promise.all(promises).then(function () {

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


+ 41 - 49
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3322,6 +3322,19 @@ var BABYLON;
                         }));
                     }
                 }
+                // Link all Babylon bones for each glTF node with the corresponding Babylon transform node.
+                // A glTF joint is a pointer to a glTF node in the glTF node hierarchy similar to Unity3D.
+                if (this.gltf.nodes) {
+                    for (var _b = 0, _c = this.gltf.nodes; _b < _c.length; _b++) {
+                        var node = _c[_b];
+                        if (node._babylonTransformNode && node._babylonBones) {
+                            for (var _d = 0, _e = node._babylonBones; _d < _e.length; _d++) {
+                                var babylonBone = _e[_d];
+                                babylonBone.linkTransformNode(node._babylonTransformNode);
+                            }
+                        }
+                    }
+                }
                 promises.push(this._loadAnimationsAsync());
                 this.logClose();
                 return Promise.all(promises).then(function () { });
@@ -3333,9 +3346,6 @@ var BABYLON;
                         callback(babylonMesh);
                     }
                 }
-                else if (node._babylonTransformNode instanceof BABYLON.AbstractMesh) {
-                    callback(node._babylonTransformNode);
-                }
             };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = new Array();
@@ -3434,20 +3444,12 @@ var BABYLON;
                         }));
                     }
                     if (node.children) {
-                        var _loop_1 = function (index) {
+                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
+                            var index = _a[_i];
                             var childNode = ArrayItem.Get(context + "/children/" + index, _this.gltf.nodes, index);
                             promises.push(_this.loadNodeAsync("/nodes/" + childNode.index, childNode, function (childBabylonMesh) {
-                                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                                if (childNode.skin != undefined) {
-                                    childBabylonMesh.parent = _this._rootBabylonMesh;
-                                    return;
-                                }
                                 childBabylonMesh.parent = babylonTransformNode;
                             }));
-                        };
-                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
-                            var index = _a[_i];
-                            _loop_1(index);
                         }
                     }
                     assign(babylonTransformNode);
@@ -3484,16 +3486,16 @@ var BABYLON;
                     var primitive = mesh.primitives[0];
                     promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, name, node, mesh, primitive, function (babylonMesh) {
                         node._babylonTransformNode = babylonMesh;
+                        node._primitiveBabylonMeshes = [babylonMesh];
                     }));
                 }
                 else {
-                    var babylonTransformNode_1 = new BABYLON.TransformNode(name, this.babylonScene);
-                    node._babylonTransformNode = babylonTransformNode_1;
+                    node._babylonTransformNode = new BABYLON.TransformNode(name, this.babylonScene);
+                    node._primitiveBabylonMeshes = [];
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                         var primitive = primitives_1[_i];
                         promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, name + "_primitive" + primitive.index, node, mesh, primitive, function (babylonMesh) {
-                            babylonMesh.parent = babylonTransformNode_1;
-                            node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
+                            babylonMesh.parent = node._babylonTransformNode;
                             node._primitiveBabylonMeshes.push(babylonMesh);
                         }));
                     }
@@ -3721,13 +3723,13 @@ var BABYLON;
                     });
                 };
                 if (skin._data) {
-                    var data_1 = skin._data;
-                    return data_1.promise.then(function () {
-                        assignSkeleton(data_1.babylonSkeleton);
-                    });
+                    assignSkeleton(skin._data.babylonSkeleton);
+                    return skin._data.promise;
                 }
                 var skeletonId = "skeleton" + skin.index;
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
+                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                babylonSkeleton.overrideMesh = this._rootBabylonMesh;
                 this._loadBones(context, skin, babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
                 var promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
@@ -3892,11 +3894,6 @@ var BABYLON;
                     (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonTransformNode)) {
                     return Promise.resolve();
                 }
-                // Ignore animations targeting TRS of skinned nodes.
-                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                if (targetNode.skin != undefined && channel.target.path !== "weights" /* WEIGHTS */) {
-                    return Promise.resolve();
-                }
                 var sampler = ArrayItem.Get(context + "/sampler", animation.samplers, channel.sampler);
                 return this._loadAnimationSamplerAsync(animationContext + "/samplers/" + channel.sampler, sampler).then(function (data) {
                     var targetPath;
@@ -3996,7 +3993,7 @@ var BABYLON;
                         keys[frameIndex] = getNextKey(frameIndex);
                     }
                     if (targetPath === "influence") {
-                        var _loop_2 = function (targetIndex) {
+                        var _loop_1 = function (targetIndex) {
                             var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                             var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                             babylonAnimation.setKeys(keys.map(function (key) { return ({
@@ -4013,27 +4010,15 @@ var BABYLON;
                             });
                         };
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
-                            _loop_2(targetIndex);
+                            _loop_1(targetIndex);
                         }
                     }
                     else {
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        var babylonTransformNode = targetNode._babylonTransformNode;
-                        var babylonBones = targetNode._babylonBones;
-                        if (babylonBones) {
-                            var babylonAnimationTargets = [babylonTransformNode].concat(babylonBones);
-                            for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
-                                var babylonAnimationTarget = babylonAnimationTargets_1[_i];
-                                babylonAnimationTarget.animations.push(babylonAnimation);
-                            }
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
-                        }
-                        else {
-                            babylonTransformNode.animations.push(babylonAnimation);
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
-                        }
+                        targetNode._babylonTransformNode.animations.push(babylonAnimation);
+                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonTransformNode);
                     }
                 });
             };
@@ -4428,8 +4413,14 @@ var BABYLON;
                 this.logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? GLTFLoader._DefaultSampler : ArrayItem.Get(context + "/sampler", this.gltf.samplers, texture.sampler));
                 var samplerData = this._loadSampler("/samplers/" + sampler.index, sampler);
+                var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
+                var textureURL = null;
+                if (image.uri && !BABYLON.Tools.IsBase64(image.uri) && this.babylonScene.getEngine().textureFormatInUse) {
+                    // If an image uri and a texture format is set like (eg. KTX) load from url instead of blob to support texture format and fallback
+                    textureURL = this._uniqueRootUrl + image.uri;
+                }
                 var deferred = new BABYLON.Deferred();
-                var babylonTexture = new BABYLON.Texture(null, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
+                var babylonTexture = new BABYLON.Texture(textureURL, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
                     if (!_this._disposed) {
                         deferred.resolve();
                     }
@@ -4439,14 +4430,15 @@ var BABYLON;
                     }
                 });
                 promises.push(deferred.promise);
+                if (!textureURL) {
+                    promises.push(this.loadImageAsync("/images/" + image.index, image).then(function (data) {
+                        var name = image.uri || _this._fileName + "#image" + image.index;
+                        var dataUrl = "data:" + _this._uniqueRootUrl + name;
+                        babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
+                    }));
+                }
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
-                promises.push(this.loadImageAsync("/images/" + image.index, image).then(function (data) {
-                    var name = image.uri || _this._fileName + "#image" + image.index;
-                    var dataUrl = "data:" + _this._uniqueRootUrl + name;
-                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
-                }));
                 assign(babylonTexture);
                 this.logClose();
                 return Promise.all(promises).then(function () {

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


+ 41 - 49
dist/preview release/loaders/babylonjs.loaders.js

@@ -4384,6 +4384,19 @@ var BABYLON;
                         }));
                     }
                 }
+                // Link all Babylon bones for each glTF node with the corresponding Babylon transform node.
+                // A glTF joint is a pointer to a glTF node in the glTF node hierarchy similar to Unity3D.
+                if (this.gltf.nodes) {
+                    for (var _b = 0, _c = this.gltf.nodes; _b < _c.length; _b++) {
+                        var node = _c[_b];
+                        if (node._babylonTransformNode && node._babylonBones) {
+                            for (var _d = 0, _e = node._babylonBones; _d < _e.length; _d++) {
+                                var babylonBone = _e[_d];
+                                babylonBone.linkTransformNode(node._babylonTransformNode);
+                            }
+                        }
+                    }
+                }
                 promises.push(this._loadAnimationsAsync());
                 this.logClose();
                 return Promise.all(promises).then(function () { });
@@ -4395,9 +4408,6 @@ var BABYLON;
                         callback(babylonMesh);
                     }
                 }
-                else if (node._babylonTransformNode instanceof BABYLON.AbstractMesh) {
-                    callback(node._babylonTransformNode);
-                }
             };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = new Array();
@@ -4496,20 +4506,12 @@ var BABYLON;
                         }));
                     }
                     if (node.children) {
-                        var _loop_1 = function (index) {
+                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
+                            var index = _a[_i];
                             var childNode = ArrayItem.Get(context + "/children/" + index, _this.gltf.nodes, index);
                             promises.push(_this.loadNodeAsync("/nodes/" + childNode.index, childNode, function (childBabylonMesh) {
-                                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                                if (childNode.skin != undefined) {
-                                    childBabylonMesh.parent = _this._rootBabylonMesh;
-                                    return;
-                                }
                                 childBabylonMesh.parent = babylonTransformNode;
                             }));
-                        };
-                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
-                            var index = _a[_i];
-                            _loop_1(index);
                         }
                     }
                     assign(babylonTransformNode);
@@ -4546,16 +4548,16 @@ var BABYLON;
                     var primitive = mesh.primitives[0];
                     promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, name, node, mesh, primitive, function (babylonMesh) {
                         node._babylonTransformNode = babylonMesh;
+                        node._primitiveBabylonMeshes = [babylonMesh];
                     }));
                 }
                 else {
-                    var babylonTransformNode_1 = new BABYLON.TransformNode(name, this.babylonScene);
-                    node._babylonTransformNode = babylonTransformNode_1;
+                    node._babylonTransformNode = new BABYLON.TransformNode(name, this.babylonScene);
+                    node._primitiveBabylonMeshes = [];
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                         var primitive = primitives_1[_i];
                         promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, name + "_primitive" + primitive.index, node, mesh, primitive, function (babylonMesh) {
-                            babylonMesh.parent = babylonTransformNode_1;
-                            node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
+                            babylonMesh.parent = node._babylonTransformNode;
                             node._primitiveBabylonMeshes.push(babylonMesh);
                         }));
                     }
@@ -4783,13 +4785,13 @@ var BABYLON;
                     });
                 };
                 if (skin._data) {
-                    var data_1 = skin._data;
-                    return data_1.promise.then(function () {
-                        assignSkeleton(data_1.babylonSkeleton);
-                    });
+                    assignSkeleton(skin._data.babylonSkeleton);
+                    return skin._data.promise;
                 }
                 var skeletonId = "skeleton" + skin.index;
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
+                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                babylonSkeleton.overrideMesh = this._rootBabylonMesh;
                 this._loadBones(context, skin, babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
                 var promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
@@ -4954,11 +4956,6 @@ var BABYLON;
                     (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonTransformNode)) {
                     return Promise.resolve();
                 }
-                // Ignore animations targeting TRS of skinned nodes.
-                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                if (targetNode.skin != undefined && channel.target.path !== "weights" /* WEIGHTS */) {
-                    return Promise.resolve();
-                }
                 var sampler = ArrayItem.Get(context + "/sampler", animation.samplers, channel.sampler);
                 return this._loadAnimationSamplerAsync(animationContext + "/samplers/" + channel.sampler, sampler).then(function (data) {
                     var targetPath;
@@ -5058,7 +5055,7 @@ var BABYLON;
                         keys[frameIndex] = getNextKey(frameIndex);
                     }
                     if (targetPath === "influence") {
-                        var _loop_2 = function (targetIndex) {
+                        var _loop_1 = function (targetIndex) {
                             var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                             var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                             babylonAnimation.setKeys(keys.map(function (key) { return ({
@@ -5075,27 +5072,15 @@ var BABYLON;
                             });
                         };
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
-                            _loop_2(targetIndex);
+                            _loop_1(targetIndex);
                         }
                     }
                     else {
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        var babylonTransformNode = targetNode._babylonTransformNode;
-                        var babylonBones = targetNode._babylonBones;
-                        if (babylonBones) {
-                            var babylonAnimationTargets = [babylonTransformNode].concat(babylonBones);
-                            for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
-                                var babylonAnimationTarget = babylonAnimationTargets_1[_i];
-                                babylonAnimationTarget.animations.push(babylonAnimation);
-                            }
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
-                        }
-                        else {
-                            babylonTransformNode.animations.push(babylonAnimation);
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
-                        }
+                        targetNode._babylonTransformNode.animations.push(babylonAnimation);
+                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonTransformNode);
                     }
                 });
             };
@@ -5490,8 +5475,14 @@ var BABYLON;
                 this.logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? GLTFLoader._DefaultSampler : ArrayItem.Get(context + "/sampler", this.gltf.samplers, texture.sampler));
                 var samplerData = this._loadSampler("/samplers/" + sampler.index, sampler);
+                var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
+                var textureURL = null;
+                if (image.uri && !BABYLON.Tools.IsBase64(image.uri) && this.babylonScene.getEngine().textureFormatInUse) {
+                    // If an image uri and a texture format is set like (eg. KTX) load from url instead of blob to support texture format and fallback
+                    textureURL = this._uniqueRootUrl + image.uri;
+                }
                 var deferred = new BABYLON.Deferred();
-                var babylonTexture = new BABYLON.Texture(null, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
+                var babylonTexture = new BABYLON.Texture(textureURL, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
                     if (!_this._disposed) {
                         deferred.resolve();
                     }
@@ -5501,14 +5492,15 @@ var BABYLON;
                     }
                 });
                 promises.push(deferred.promise);
+                if (!textureURL) {
+                    promises.push(this.loadImageAsync("/images/" + image.index, image).then(function (data) {
+                        var name = image.uri || _this._fileName + "#image" + image.index;
+                        var dataUrl = "data:" + _this._uniqueRootUrl + name;
+                        babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
+                    }));
+                }
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
-                promises.push(this.loadImageAsync("/images/" + image.index, image).then(function (data) {
-                    var name = image.uri || _this._fileName + "#image" + image.index;
-                    var dataUrl = "data:" + _this._uniqueRootUrl + name;
-                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
-                }));
                 assign(babylonTexture);
                 this.logClose();
                 return Promise.all(promises).then(function () {

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


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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.0.0-alpha.7",
+    "version": "4.0.0-alpha.8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-alpha.7",
-        "babylonjs": "4.0.0-alpha.7"
+        "babylonjs-gltf2interface": "4.0.0-alpha.8",
+        "babylonjs": "4.0.0-alpha.8"
     },
     "engines": {
         "node": "*"

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

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

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


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


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


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

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

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

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

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


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


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

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

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


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


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

@@ -66,12 +66,19 @@
 - Make onscreen joystick's canvas public ([TrevorDev](https://github.com/TrevorDev))
 - Added `Tools.CustomRequestHeaders`, `Tools.UseCustomRequestHeaders`, `Tools.InjectCustomRequestHeaders` to send Custom Request Headers alongside XMLHttpRequest's i.e. when loading files (Tools.Loadfile) from resources requiring special headers like 'Authorization' ([susares](https://github.com/susares))
 - Added `.serialize` and `.Parse` functions in `ReflectionProbe` to retrieve reflection probes when parsing a previously serialized material ([julien-moreau](https://github.com/julien-moreau))
+- GizmoManager clearGizmoOnEmptyPointerEvent options and onAttachedToMeshObservable event ([TrevorDev](https://github.com/TrevorDev))
+- Added support for overriding the mesh used for the world matrix for a mesh with a skeleton ([bghgary](https://github.com/bghgary))
+- Added support for linking a bone to a transform node ([bghgary](https://github.com/bghgary))
+
 ### glTF Loader
 
 - Added support for mesh instancing for improved performance when multiple nodes point to the same mesh ([bghgary](https://github.com/bghgary))
 - Create `TransformNode` objects instead of `Mesh` objects for glTF nodes without geometry ([bghgary](https://github.com/bghgary))
 - Added glTF JSON pointers to metadata of nodes, materials, and textures ([bghgary](https://github.com/bghgary))
 - Load KTX textures in the gltf2 loader when textureFormat is set on engine ([TrevorDev](https://github.com/TrevorDev))
+- Skinned meshes now behave as intended by glTF ([bghgary](https://github.com/bghgary))
+  - Skinned meshes now set an override mesh instead of reparenting to the `__root__` transform node
+  - Loaded bones are linked with the transform node created for the corresponding glTF node
 
 ### glTF Serializer
 
@@ -80,6 +87,7 @@
 ### Materials Library
 
 ## Bug fixes
+- Fixed FileLoader's loading of a skybox, & added a parsed value for whether to create with PBR or STDMaterial ([Palmer-JC](https://github.com/Palmer-JC))
 - Removed bones from rootNodes where they should never have been ([Deltakosh](https://github.com/deltakosh))
 - Refocusing on input gui with pointer events ([TrevorDev](https://github.com/TrevorDev))
 - Gizmo scaling not consistent when camera is parented ([TrevorDev](https://github.com/TrevorDev))

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

@@ -164,7 +164,7 @@ export class Container extends Control {
      * @returns the current container
      */
     public clearControls(): Container {
-        let children = this._children.slice();
+        let children = this.children.slice();
 
         for (var child of children) {
             this.removeControl(child);

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

@@ -1039,10 +1039,12 @@ export class Control {
             return;
         }
 
+        context.save();
         context.strokeStyle = "#4affff";
         context.lineWidth = 2;
 
         this._renderHighlightSpecific(context);
+        context.restore();
     }
 
     /** @hidden */

+ 5 - 5
inspector/src/components/actionTabs/actionTabs.scss

@@ -293,12 +293,11 @@
 
                     button {
                         background: #222222;
-                        border: 0px;
+                        border: 1px solid rgb(51, 122, 183);
+                        margin: 5px 10px 5px 10px;
                         color:white;
                         padding: 4px 5px;
                         opacity: 0.9;
-                        width: 100%;
-                        height: 28px;
                     }
 
                     button:hover {
@@ -310,7 +309,7 @@
                     }   
                     
                     button:focus {
-                        border: 0px;
+                        border: 1px solid rgb(51, 122, 183);
                         outline: 0px;
                     }  
                 }
@@ -565,7 +564,7 @@
                     }                    
 
                     .command {
-                        border: 0px;
+                        border: 1px solid transparent;
                         background:transparent;
                         color: white;
                     }
@@ -583,6 +582,7 @@
                         width: 256px;
                         margin-top: 5px;
                         margin-bottom: 5px;
+                        border: 2px solid rgba(255, 255, 255, 0.4);
                     }
                 }
 

+ 12 - 1
inspector/src/components/actionTabs/lineContainerComponent.tsx

@@ -12,10 +12,21 @@ export class LineContainerComponent extends React.Component<ILineContainerCompon
     constructor(props: ILineContainerComponentProps) {
         super(props);
 
-        this.state = { isExpanded: !this.props.closed };
+        let initialState: boolean;
+
+        if (typeof (Storage) !== "undefined" && localStorage.getItem(this.props.title) !== null) {
+            initialState = localStorage.getItem(this.props.title) === "true";
+        } else {
+            initialState = !this.props.closed;
+        }
+
+        this.state = { isExpanded: initialState };
     }
 
     switchExpandedState(): void {
+        if (typeof (Storage) !== "undefined") {
+            localStorage.setItem(this.props.title, !this.state.isExpanded ? "true" : "false");
+        }
         this.setState({ isExpanded: !this.state.isExpanded });
     }
 

+ 2 - 2
inspector/src/components/actionTabs/lines/color3LineComponent.tsx

@@ -14,14 +14,14 @@ export class Color3LineComponent extends React.Component<IColor3LineComponentPro
     constructor(props: IColor3LineComponentProps) {
         super(props);
 
-        this.state = { color: this.props.target[this.props.propertyName] };
+        this.state = { color: this.props.target[this.props.propertyName].clone() };
     }
 
     shouldComponentUpdate(nextProps: IColor3LineComponentProps, nextState: { color: Color3 }) {
         const currentState = nextProps.target[nextProps.propertyName];
 
         if (!currentState.equals(nextState.color) || this._localChange) {
-            nextState.color = currentState;
+            nextState.color = currentState.clone();
             this._localChange = false;
             return true;
         }

+ 5 - 2
inspector/src/components/actionTabs/lines/fileButtonLineComponent.tsx

@@ -2,7 +2,8 @@ import * as React from "react";
 
 interface IFileButtonLineComponentProps {
     label: string,
-    onClick: (file: File) => void
+    onClick: (file: File) => void,
+    accept: string
 }
 
 export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
@@ -15,6 +16,8 @@ export class FileButtonLineComponent extends React.Component<IFileButtonLineComp
         if (files && files.length) {
             this.props.onClick(files[0]);
         }
+
+        evt.target.value = "";
     }
 
     render() {
@@ -23,7 +26,7 @@ export class FileButtonLineComponent extends React.Component<IFileButtonLineComp
                 <label htmlFor="file-upload" className="file-upload">
                     {this.props.label}
                 </label>
-                <input id="file-upload" type="file" accept=".dds, .env" onChange={evt => this.onChange(evt)} />
+                <input ref="upload" id="file-upload" type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} />
             </div>
         );
     }

+ 25 - 4
inspector/src/components/actionTabs/lines/floatLineComponent.tsx

@@ -1,12 +1,14 @@
 import * as React from "react";
 import { Observable } from "babylonjs";
 import { PropertyChangedEvent } from "../../propertyChangedEvent";
+import { LockObject } from "../tabs/propertyGrids/lockObject";
 
 interface IFloatLineComponentProps {
     label: string,
     target: any,
     propertyName: string,
-    step?: number,
+    lockObject?: LockObject,
+    onChange?: (newValue: number) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>,
     additionalClass?: string
 }
@@ -23,6 +25,11 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
         this._store = currentValue;
     }
 
+
+    componentWillUnmount() {
+        this.unlock();
+    }
+
     shouldComponentUpdate(nextProps: IFloatLineComponentProps, nextState: { value: string }) {
         if (this._localChange) {
             this._localChange = false;
@@ -38,6 +45,10 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
     }
 
     raiseOnPropertyChanged(newValue: number, previousValue: number) {
+        if (this.props.onChange) {
+            this.props.onChange(newValue);
+        }
+
         if (!this.props.onPropertyChangedObservable) {
             return;
         }
@@ -70,16 +81,26 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
         this._store = valueAsNumber;
     }
 
-    render() {
+    lock() {
+        if (this.props.lockObject) {
+            this.props.lockObject.lock = true;
+        }
+    }
+
+    unlock() {
+        if (this.props.lockObject) {
+            this.props.lockObject.lock = false;
+        }
+    }
 
-        const step = this.props.step !== undefined ? this.props.step : 0.1;
+    render() {
         return (
             <div className={this.props.additionalClass ? this.props.additionalClass + " floatLine" : "floatLine"}>
                 <div className="label">
                     {this.props.label}
                 </div>
                 <div className="value">
-                    <input className="numeric-input" value={this.state.value} onChange={evt => this.updateValue(evt.target.value)} step={step} />
+                    <input className="numeric-input" value={this.state.value} onBlur={() => this.unlock()} onFocus={() => this.lock()} onChange={evt => this.updateValue(evt.target.value)} />
                 </div>
             </div>
         );

+ 1 - 3
inspector/src/components/actionTabs/lines/optionsLineComponent.tsx

@@ -71,8 +71,6 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
     }
 
     render() {
-        var currentValue = this.props.target[this.props.propertyName];
-
         return (
             <div className="listLine">
                 <div className="label">
@@ -80,7 +78,7 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
 
                 </div>
                 <div className="options">
-                    <select onChange={evt => this.updateValue(evt.target.value)} defaultValue={currentValue}>
+                    <select onChange={evt => this.updateValue(evt.target.value)} value={this.state.value}>
                         {
                             this.props.options.map(option => {
                                 return (

+ 1 - 0
inspector/src/components/actionTabs/lines/radioLineComponent.tsx

@@ -26,6 +26,7 @@ export class RadioButtonLineComponent extends React.Component<IRadioButtonLineCo
     componentWillUnmount() {
         if (this._onSelectionChangedObserver) {
             this.props.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
+            this._onSelectionChangedObserver = null;
         }
     }
 

+ 41 - 13
inspector/src/components/actionTabs/lines/sliderLineComponent.tsx

@@ -4,11 +4,14 @@ import { PropertyChangedEvent } from "../../propertyChangedEvent";
 
 interface ISliderLineComponentProps {
     label: string,
-    target: any,
-    propertyName: string,
+    target?: any,
+    propertyName?: string,
     minimum: number,
     maximum: number,
     step: number,
+    directValue?: number,
+    onChange?: (value: number) => void,
+    onInput?: (value: number) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -17,11 +20,21 @@ export class SliderLineComponent extends React.Component<ISliderLineComponentPro
     constructor(props: ISliderLineComponentProps) {
         super(props);
 
-        this.state = { value: this.props.target[this.props.propertyName] };
+        if (this.props.directValue !== undefined) {
+            this.state = {
+                value: this.props.directValue
+            }
+        } else {
+            this.state = { value: this.props.target![this.props.propertyName!] };
+        }
     }
 
     shouldComponentUpdate(nextProps: ISliderLineComponentProps, nextState: { value: number }) {
-        const currentState = nextProps.target[nextProps.propertyName];
+        if (nextProps.directValue !== undefined) {
+            nextState.value = nextProps.directValue;
+            return true;
+        }
+        const currentState = nextProps.target![nextProps.propertyName!];
 
         if (currentState !== nextState.value || this._localChange) {
             nextState.value = currentState;
@@ -35,20 +48,33 @@ export class SliderLineComponent extends React.Component<ISliderLineComponentPro
         this._localChange = true;
         const newValue = parseFloat(newValueString);
 
-        if (this.props.onPropertyChangedObservable) {
-            this.props.onPropertyChangedObservable.notifyObservers({
-                object: this.props.target,
-                property: this.props.propertyName,
-                value: newValue,
-                initialValue: this.state.value
-            });
+        if (this.props.target) {
+            if (this.props.onPropertyChangedObservable) {
+                this.props.onPropertyChangedObservable.notifyObservers({
+                    object: this.props.target,
+                    property: this.props.propertyName!,
+                    value: newValue,
+                    initialValue: this.state.value
+                });
+            }
+
+            this.props.target[this.props.propertyName!] = newValue;
         }
 
-        this.props.target[this.props.propertyName] = newValue;
+        if (this.props.onChange) {
+            this.props.onChange(newValue);
+        }
 
         this.setState({ value: newValue });
     }
 
+    onInput(newValueString: any) {
+        const newValue = parseFloat(newValueString);
+        if (this.props.onInput) {
+            this.props.onInput(newValue);
+        }
+    }
+
     render() {
         return (
             <div className="sliderLine">
@@ -56,7 +82,9 @@ export class SliderLineComponent extends React.Component<ISliderLineComponentPro
                     {this.props.label}
                 </div>
                 <div className="slider">
-                    {this.state.value ? this.state.value.toFixed(2) : "0"}&nbsp;<input className="range" type="range" step={this.props.step} min={this.props.minimum} max={this.props.maximum} value={this.state.value} onChange={evt => this.onChange(evt.target.value)} />
+                    {this.state.value ? this.state.value.toFixed(2) : "0"}&nbsp;<input className="range" type="range" step={this.props.step} min={this.props.minimum} max={this.props.maximum} value={this.state.value}
+                        onInput={evt => this.onInput((evt.target as HTMLInputElement).value)}
+                        onChange={evt => this.onChange(evt.target.value)} />
                 </div>
             </div>
         );

+ 7 - 1
inspector/src/components/actionTabs/lines/textInputLineComponent.tsx

@@ -1,10 +1,12 @@
 import * as React from "react";
 import { Observable } from "babylonjs";
 import { PropertyChangedEvent } from "components/propertyChangedEvent";
+import { LockObject } from "../tabs/propertyGrids/lockObject";
 
 interface ITextInputLineComponentProps {
     label: string,
     target: any,
+    lockObject: LockObject,
     propertyName: string,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
@@ -18,6 +20,10 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
         this.state = { value: this.props.target[this.props.propertyName] || "" }
     }
 
+    componentWillUnmount() {
+        this.props.lockObject.lock = false;
+    }
+
     shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: { value: string }) {
         if (this._localChange) {
             this._localChange = false;
@@ -61,7 +67,7 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
                     {this.props.label}
                 </div>
                 <div className="value">
-                    <input value={this.state.value} onChange={evt => this.updateValue(evt.target.value)} />
+                    <input value={this.state.value} onBlur={() => this.props.lockObject.lock = false} onFocus={() => this.props.lockObject.lock = true} onChange={evt => this.updateValue(evt.target.value)} />
                 </div>
             </div>
         );

+ 3 - 3
inspector/src/components/actionTabs/lines/textureLineComponent.tsx

@@ -1,8 +1,8 @@
 import * as React from "react";
-import { Texture, PostProcess } from "babylonjs";
+import { BaseTexture, PostProcess, Texture } from "babylonjs";
 
 interface ITextureLineComponentProps {
-    texture: Texture,
+    texture: BaseTexture,
     width: number,
     height: number
 }
@@ -108,7 +108,7 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
             }
 
             //To flip image on Y axis.
-            if (texture.invertY || texture.isCube) {
+            if ((texture as Texture).invertY || texture.isCube) {
                 for (var i = 0; i < halfHeight; i++) {
                     for (var j = 0; j < numberOfChannelsByLine; j++) {
                         var currentCell = j + i * numberOfChannelsByLine;

+ 10 - 2
inspector/src/components/actionTabs/lines/vector3LineComponent.tsx

@@ -9,6 +9,7 @@ interface IVector3LineComponentProps {
     label: string,
     target: any,
     propertyName: string,
+    onChange?: (newvalue: Vector3) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -18,14 +19,14 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
     constructor(props: IVector3LineComponentProps) {
         super(props);
 
-        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName] }
+        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName].clone() }
     }
 
     shouldComponentUpdate(nextProps: IVector3LineComponentProps, nextState: { isExpanded: boolean, value: Vector3 }) {
         const nextPropsValue = nextProps.target[nextProps.propertyName];
 
         if (!nextPropsValue.equals(nextState.value) || this._localChange) {
-            nextState.value = nextPropsValue;
+            nextState.value = nextPropsValue.clone();
             this._localChange = false;
             return true;
         }
@@ -38,6 +39,10 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
     }
 
     raiseOnPropertyChanged(previousValue: Vector3) {
+        if (this.props.onChange) {
+            this.props.onChange(this.state.value);
+        }
+
         if (!this.props.onPropertyChangedObservable) {
             return;
         }
@@ -53,6 +58,7 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
         this._localChange = true;
 
         const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].x = value;
         this.state.value.x = value;
         this.setState({ value: this.state.value });
 
@@ -63,6 +69,7 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
         this._localChange = true;
 
         const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].y = value;
         this.state.value.y = value;
         this.setState({ value: this.state.value });
 
@@ -73,6 +80,7 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
         this._localChange = true;
 
         const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].z = value;
         this.state.value.z = value;
         this.setState({ value: this.state.value });
 

+ 32 - 2
inspector/src/components/actionTabs/tabs/debugTabComponent.tsx

@@ -6,13 +6,13 @@ import { GridPropertyGridComponent } from "./propertyGrids/gridPropertyGridCompo
 
 export class DebugTabComponent extends PaneComponent {
     private _skeletonViewersEnabled = false;
+    private _physicsViewersEnabled = false;
     private _skeletonViewers = new Array<BABYLON.Debug.SkeletonViewer>();
 
     constructor(props: IPaneComponentProps) {
         super(props);
     }
 
-
     componentWillMount() {
         const scene = this.props.scene;
 
@@ -20,6 +20,10 @@ export class DebugTabComponent extends PaneComponent {
             return;
         }
 
+        if (!scene.metadata) {
+            scene.metadata = {};
+        }
+
         for (var mesh of scene.meshes) {
             if (mesh.skeleton && mesh.metadata && mesh.metadata.skeletonViewer) {
                 this._skeletonViewers.push(mesh.metadata.skeletonViewer);
@@ -27,6 +31,7 @@ export class DebugTabComponent extends PaneComponent {
         }
 
         this._skeletonViewersEnabled = (this._skeletonViewers.length > 0);
+        this._physicsViewersEnabled = scene.metadata.physicsViewer != null;
     }
 
     componentWillUnmount() {
@@ -49,7 +54,7 @@ export class DebugTabComponent extends PaneComponent {
                     if (found) {
                         continue;
                     }
-                    var viewer = new BABYLON.Debug.SkeletonViewer(mesh.skeleton, mesh, scene, true, 0, BABYLON.UtilityLayerRenderer.DefaultUtilityLayer);
+                    var viewer = new BABYLON.Debug.SkeletonViewer(mesh.skeleton, mesh, scene, true, 0);
                     viewer.isEnabled = true;
                     this._skeletonViewers.push(viewer);
                     if (!mesh.metadata) {
@@ -68,6 +73,30 @@ export class DebugTabComponent extends PaneComponent {
         }
     }
 
+    switchPhysicsViewers() {
+        this._physicsViewersEnabled = !this._physicsViewersEnabled;
+        const scene = this.props.scene;
+
+        if (this._physicsViewersEnabled) {
+            const physicsViewer = new BABYLON.Debug.PhysicsViewer(scene);
+            scene.metadata.physicsViewer = physicsViewer;
+
+            for (var mesh of scene.meshes) {
+                if (mesh.physicsImpostor) {
+                    let debugMesh = physicsViewer.showImpostor(mesh.physicsImpostor);
+
+                    if (debugMesh) {
+                        debugMesh.metadata = { hidden: true };
+                        debugMesh.material!.metadata = { hidden: true };
+                    }
+                }
+            }
+        } else {
+            scene.metadata.physicsViewer.dispose();
+            scene.metadata.physicsViewer = null;
+        }
+    }
+
     render() {
         const scene = this.props.scene;
 
@@ -80,6 +109,7 @@ export class DebugTabComponent extends PaneComponent {
                 <LineContainerComponent title="HELPERS">
                     <GridPropertyGridComponent scene={scene} />
                     <CheckBoxLineComponent label="Bones" isSelected={() => this._skeletonViewersEnabled} onSelect={() => this.switchSkeletonViewers()} />
+                    <CheckBoxLineComponent label="Physics" isSelected={() => this._physicsViewersEnabled} onSelect={() => this.switchPhysicsViewers()} />
                 </LineContainerComponent>
                 <LineContainerComponent title="TEXTURE CHANNELS">
                     <CheckBoxLineComponent label="Diffuse" isSelected={() => BABYLON.StandardMaterial.DiffuseTextureEnabled} onSelect={() => BABYLON.StandardMaterial.DiffuseTextureEnabled = !BABYLON.StandardMaterial.DiffuseTextureEnabled} />

+ 63 - 10
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -1,6 +1,6 @@
 import * as React from "react";
 import { PaneComponent, IPaneComponentProps } from "../paneComponent";
-import { Mesh, TransformNode, Material, StandardMaterial, Texture, PBRMaterial, Scene, FreeCamera, ArcRotateCamera, HemisphericLight, PointLight, BackgroundMaterial } from "babylonjs";
+import { Mesh, TransformNode, Material, StandardMaterial, Texture, PBRMaterial, Scene, FreeCamera, ArcRotateCamera, HemisphericLight, PointLight, BackgroundMaterial, AnimationGroup } from "babylonjs";
 import { MaterialPropertyGridComponent } from "./propertyGrids/materials/materialPropertyGridComponent";
 import { StandardMaterialPropertyGridComponent } from "./propertyGrids/materials/standardMaterialPropertyGridComponent";
 import { TexturePropertyGridComponent } from "./propertyGrids/materials/texturePropertyGridComponent";
@@ -21,12 +21,31 @@ import { InputText } from "babylonjs-gui/2D/controls/inputText";
 import { InputTextPropertyGridComponent } from "./propertyGrids/gui/inputTextPropertyGridComponent";
 import { ColorPicker } from "babylonjs-gui";
 import { ColorPickerPropertyGridComponent } from "./propertyGrids/gui/colorPickerPropertyGridComponent";
+import { AnimationGroupGridComponent } from "./propertyGrids/animationGroupPropertyGridComponent";
+import { LockObject } from "./propertyGrids/lockObject";
 
 export class PropertyGridTabComponent extends PaneComponent {
+    private _timerIntervalId: number;
+    private _lockObject = new LockObject();
+
     constructor(props: IPaneComponentProps) {
         super(props);
     }
 
+    timerRefresh() {
+        if (!this._lockObject.lock) {
+            this.forceUpdate();
+        }
+    }
+
+    componentWillMount() {
+        this._timerIntervalId = window.setInterval(() => this.timerRefresh(), 500);
+    }
+
+    componentWillUnmount() {
+        window.clearInterval(this._timerIntervalId);
+    }
+
     render() {
         const entity = this.props.selectedEntity;
 
@@ -45,6 +64,7 @@ export class PropertyGridTabComponent extends PaneComponent {
                 const mesh = entity as Mesh;
                 if (mesh.getTotalVertices() > 0) {
                     return (<MeshPropertyGridComponent mesh={mesh}
+                        lockObject={this._lockObject}
                         onSelectionChangedObservable={this.props.onSelectionChangedObservable}
                         onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
                 }
@@ -52,18 +72,23 @@ export class PropertyGridTabComponent extends PaneComponent {
 
             if (className.indexOf("FreeCamera") !== -1) {
                 const freeCamera = entity as FreeCamera;
-                return (<FreeCameraPropertyGridComponent camera={freeCamera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<FreeCameraPropertyGridComponent camera={freeCamera}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className.indexOf("ArcRotateCamera") !== -1) {
                 const arcRotateCamera = entity as ArcRotateCamera;
-                return (<ArcRotateCameraPropertyGridComponent camera={arcRotateCamera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<ArcRotateCameraPropertyGridComponent camera={arcRotateCamera}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className === "HemisphericLight") {
                 const hemisphericLight = entity as HemisphericLight;
                 return (<HemisphericLightPropertyGridComponent
                     light={hemisphericLight}
+                    lockObject={this._lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
@@ -71,18 +96,22 @@ export class PropertyGridTabComponent extends PaneComponent {
                 const pointLight = entity as PointLight;
                 return (<PointLightPropertyGridComponent
                     light={pointLight}
+                    lockObject={this._lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className.indexOf("TransformNode") !== -1 || className.indexOf("Mesh") !== -1) {
                 const transformNode = entity as TransformNode;
-                return (<TransformNodePropertyGridComponent transformNode={transformNode} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<TransformNodePropertyGridComponent transformNode={transformNode}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className === "StandardMaterial") {
                 const material = entity as StandardMaterial;
                 return (<StandardMaterialPropertyGridComponent
                     material={material}
+                    lockObject={this._lockObject}
                     onSelectionChangedObservable={this.props.onSelectionChangedObservable}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
@@ -91,6 +120,7 @@ export class PropertyGridTabComponent extends PaneComponent {
                 const material = entity as PBRMaterial;
                 return (<PBRMaterialPropertyGridComponent
                     material={material}
+                    lockObject={this._lockObject}
                     onSelectionChangedObservable={this.props.onSelectionChangedObservable}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
@@ -99,42 +129,65 @@ export class PropertyGridTabComponent extends PaneComponent {
                 const material = entity as BackgroundMaterial;
                 return (<BackgroundMaterialPropertyGridComponent
                     material={material}
+                    lockObject={this._lockObject}
                     onSelectionChangedObservable={this.props.onSelectionChangedObservable}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
+            if (className === "AnimationGroup") {
+                const animationGroup = entity as AnimationGroup;
+                return (<AnimationGroupGridComponent
+                    animationGroup={animationGroup}
+                    scene={this.props.scene}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
             if (className.indexOf("Material") !== -1) {
                 const material = entity as Material;
-                return (<MaterialPropertyGridComponent material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<MaterialPropertyGridComponent material={material}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className.indexOf("Texture") !== -1) {
                 const texture = entity as Texture;
-                return (<TexturePropertyGridComponent texture={texture} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<TexturePropertyGridComponent texture={texture}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className === "TextBlock") {
                 const textBlock = entity as TextBlock;
-                return (<TextBlockPropertyGridComponent textBlock={textBlock} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<TextBlockPropertyGridComponent textBlock={textBlock}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className === "InputText") {
                 const inputText = entity as InputText;
-                return (<InputTextPropertyGridComponent inputText={inputText} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<InputTextPropertyGridComponent inputText={inputText}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (className === "ColorPicker") {
                 const colorPicker = entity as ColorPicker;
-                return (<ColorPickerPropertyGridComponent colorPicker={colorPicker} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<ColorPickerPropertyGridComponent colorPicker={colorPicker}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
             if (entity._host) {
                 const control = entity as Control;
-                return (<ControlPropertyGridComponent control={control} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+                return (<ControlPropertyGridComponent control={control}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
         } else if (entity.transformNodes) {
             const scene = entity as Scene;
             return (<ScenePropertyGridComponent scene={scene}
+                lockObject={this._lockObject}
                 onSelectionChangedObservable={this.props.onSelectionChangedObservable}
                 onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
         }

+ 140 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animationGroupPropertyGridComponent.tsx

@@ -0,0 +1,140 @@
+import * as React from "react";
+import { Observable, AnimationGroup, Scene, Nullable, Observer } from "babylonjs";
+import { PropertyChangedEvent } from "../../../propertyChangedEvent";
+import { ButtonLineComponent } from "../../lines/buttonLineComponent";
+import { LineContainerComponent } from "../../lineContainerComponent";
+import { TextLineComponent } from "../../lines/textLineComponent";
+import { SliderLineComponent } from "../../lines/sliderLineComponent";
+import { LockObject } from "./lockObject";
+
+interface IAnimationGroupGridComponentProps {
+    animationGroup: AnimationGroup,
+    scene: Scene,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class AnimationGroupGridComponent extends React.Component<IAnimationGroupGridComponentProps, { playButtonText: string, currentFrame: number }> {
+    private _onAnimationGroupPlayObserver: Nullable<Observer<AnimationGroup>>;
+    private _onAnimationGroupPauseObserver: Nullable<Observer<AnimationGroup>>;
+    private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
+
+    constructor(props: IAnimationGroupGridComponentProps) {
+        super(props);
+
+        const animationGroup = this.props.animationGroup;
+        this.state = { playButtonText: animationGroup.isPlaying ? "Pause" : "Play", currentFrame: 0 };
+    }
+
+    disconnect(animationGroup: AnimationGroup) {
+
+        if (this._onAnimationGroupPlayObserver) {
+            animationGroup.onAnimationGroupPlayObservable.remove(this._onAnimationGroupPlayObserver);
+            this._onAnimationGroupPlayObserver = null;
+        }
+
+        if (this._onAnimationGroupPauseObserver) {
+            animationGroup.onAnimationGroupPauseObservable.remove(this._onAnimationGroupPauseObserver);
+            this._onAnimationGroupPauseObserver = null;
+        }
+    }
+
+    connect(animationGroup: AnimationGroup) {
+        this._onAnimationGroupPlayObserver = animationGroup.onAnimationGroupPlayObservable.add(() => {
+            this.forceUpdate();
+        });
+
+        this._onAnimationGroupPauseObserver = animationGroup.onAnimationGroupPauseObservable.add(() => {
+            this.forceUpdate();
+        });
+
+        this.updateCurrentFrame(animationGroup);
+    }
+
+    updateCurrentFrame(animationGroup: AnimationGroup) {
+        var targetedAnimations = animationGroup.targetedAnimations;
+        if (targetedAnimations.length > 0) {
+            var runtimeAnimations = animationGroup.targetedAnimations[0].animation.runtimeAnimations;
+            if (runtimeAnimations.length > 0) {
+                this.setState({ currentFrame: runtimeAnimations[0].currentFrame });
+            } else {
+                this.setState({ currentFrame: 0 });
+            }
+        }
+    }
+
+    shouldComponentUpdate(nextProps: IAnimationGroupGridComponentProps): boolean {
+        if (this.props.animationGroup !== nextProps.animationGroup) {
+            this.disconnect(this.props.animationGroup);
+            this.connect(nextProps.animationGroup);
+        }
+        return true;
+    }
+
+    componentWillMount() {
+        this.connect(this.props.animationGroup);
+
+        this._onBeforeRenderObserver = this.props.scene.onBeforeRenderObservable.add(() => {
+            if (this.props.animationGroup.isPlaying) {
+                this.updateCurrentFrame(this.props.animationGroup);
+            }
+        });
+    }
+
+    componentWillUnmount() {
+        this.disconnect(this.props.animationGroup);
+
+        if (this._onBeforeRenderObserver) {
+            this.props.scene.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
+            this._onBeforeRenderObserver = null;
+        }
+    }
+
+    playOrPause() {
+        const animationGroup = this.props.animationGroup;
+
+        if (animationGroup.isPlaying) {
+            this.setState({ playButtonText: "Play" });
+            animationGroup.pause();
+        } else {
+            this.setState({ playButtonText: "Pause" });
+            this.props.scene.animationGroups.forEach(grp => grp.pause());
+            animationGroup.play(true);
+        }
+    }
+
+    onCurrentFrameChange(value: number) {
+        const animationGroup = this.props.animationGroup;
+
+        if (!animationGroup.isPlaying) {
+            animationGroup.play(true);
+            animationGroup.goToFrame(value);
+            animationGroup.pause();
+        } else {
+            animationGroup.goToFrame(value);
+        }
+
+        this.setState({ currentFrame: value });
+    }
+
+    render() {
+        const animationGroup = this.props.animationGroup;
+
+        const playButtonText = animationGroup.isPlaying ? "Pause" : "Play"
+
+        return (
+            <div className="pane">
+                <LineContainerComponent title="CONTROLS">
+                    <ButtonLineComponent label={playButtonText} onClick={() => this.playOrPause()} />
+                    <SliderLineComponent label="Speed ratio" minimum={0} maximum={10} step={0.1} target={animationGroup} propertyName="speedRatio" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent ref="timeline" label="Current frame" minimum={animationGroup.from} maximum={animationGroup.to} step={(animationGroup.to - animationGroup.from) / 100.0} directValue={this.state.currentFrame} onInput={value => this.onCurrentFrameChange(value)} />
+                </LineContainerComponent>
+                <LineContainerComponent title="INFOS">
+                    <TextLineComponent label="Animation count" value={animationGroup.targetedAnimations.length.toString()} />
+                    <TextLineComponent label="From" value={animationGroup.from.toFixed(2)} />
+                    <TextLineComponent label="To" value={animationGroup.to.toFixed(2)} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 16 - 24
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/arcRotateCameraPropertyGridComponent.tsx

@@ -7,57 +7,49 @@ import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
+import { LockObject } from "../lockObject";
 
 interface IArcRotateCameraPropertyGridComponentProps {
     camera: ArcRotateCamera,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
 export class ArcRotateCameraPropertyGridComponent extends React.Component<IArcRotateCameraPropertyGridComponentProps> {
-    private _timerIntervalId: number;
-
     constructor(props: IArcRotateCameraPropertyGridComponentProps) {
         super(props);
     }
 
-    componentWillMount() {
-        this._timerIntervalId = window.setInterval(() => this.forceUpdate(), 500);
-    }
-
-    componentWillUnmount() {
-        window.clearInterval(this._timerIntervalId);
-    }
-
     render() {
         const camera = this.props.camera;
 
         return (
             <div className="pane">
-                <CommonCameraPropertyGridComponent camera={camera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonCameraPropertyGridComponent lockObject={this.props.lockObject} camera={camera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="TRANSFORMS">
                     <SliderLineComponent label="Alpha" target={camera} propertyName="alpha" minimum={camera.lowerAlphaLimit || 0} maximum={camera.upperAlphaLimit || 2 * Math.PI} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Beta" target={camera} propertyName="beta" minimum={camera.lowerAlphaLimit || 0} maximum={camera.upperBetaLimit || 2 * Math.PI} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Radius" target={camera} propertyName="radius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Radius" target={camera} propertyName="radius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="CONTROLS" closed={true}>
-                    <FloatLineComponent label="Angular sensitivity X" target={camera} propertyName="angularSensibilityX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Angular sensitivity Y" target={camera} propertyName="angularSensibilityY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Panning sensitivity" target={camera} propertyName="panningSensibility" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Pinch delta percentage" target={camera} propertyName="pinchDeltaPercentage" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Wheel delta percentage" target={camera} propertyName="wheelDeltaPercentage" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Speed" target={camera} propertyName="speed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Angular sensitivity X" target={camera} propertyName="angularSensibilityX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Angular sensitivity Y" target={camera} propertyName="angularSensibilityY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Panning sensitivity" target={camera} propertyName="panningSensibility" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Pinch delta percentage" target={camera} propertyName="pinchDeltaPercentage" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Wheel delta percentage" target={camera} propertyName="wheelDeltaPercentage" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Speed" target={camera} propertyName="speed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="COLLISIONS" closed={true}>
                     <CheckBoxLineComponent label="Check collisions" target={camera} propertyName="checkCollisions" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Collision radius" target={camera} propertyName="collisionRadius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="LIMITS" closed={true}>
-                    <FloatLineComponent label="Lower alpha limit" target={camera} propertyName="lowerAlphaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Upper alpha limit" target={camera} propertyName="upperAlphaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Lower beta limit" target={camera} propertyName="lowerBetaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Upper beta limit" target={camera} propertyName="upperBetaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Lower radius limit" target={camera} propertyName="lowerRadiusLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Upper radius limit" target={camera} propertyName="upperRadiusLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Lower alpha limit" target={camera} propertyName="lowerAlphaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Upper alpha limit" target={camera} propertyName="upperAlphaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Lower beta limit" target={camera} propertyName="lowerBetaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Upper beta limit" target={camera} propertyName="upperBetaLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Lower radius limit" target={camera} propertyName="lowerRadiusLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Upper radius limit" target={camera} propertyName="upperRadiusLimit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="BEHAVIORS" closed={true}>
                     <CheckBoxLineComponent label="Auto rotation" target={camera} propertyName="useAutoRotationBehavior" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 8 - 6
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/commonCameraPropertyGridComponent.tsx

@@ -6,9 +6,11 @@ import { LineContainerComponent } from "../../../lineContainerComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { TextLineComponent } from "../../../lines/textLineComponent";
 import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+import { LockObject } from "../lockObject";
 
 interface ICommonCameraPropertyGridComponentProps {
     camera: Camera,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -32,8 +34,8 @@ export class CommonCameraPropertyGridComponent extends React.Component<ICommonCa
                 <TextLineComponent label="ID" value={camera.id} />
                 <TextLineComponent label="Unique ID" value={camera.uniqueId.toString()} />
                 <TextLineComponent label="Class" value={camera.getClassName()} />
-                <FloatLineComponent label="Near plane" target={camera} propertyName="minZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                <FloatLineComponent label="Far plane" target={camera} propertyName="maxZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <FloatLineComponent lockObject={this.props.lockObject} label="Near plane" target={camera} propertyName="minZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <FloatLineComponent lockObject={this.props.lockObject} label="Far plane" target={camera} propertyName="maxZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <SliderLineComponent label="Inertia" target={camera} propertyName="inertia" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <OptionsLineComponent label="Mode" options={modeOptions} target={camera} propertyName="mode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => this.setState({ mode: value })} />
                 {
@@ -42,19 +44,19 @@ export class CommonCameraPropertyGridComponent extends React.Component<ICommonCa
                 }
                 {
                     camera.mode === BABYLON.Camera.ORTHOGRAPHIC_CAMERA &&
-                    <FloatLineComponent label="Left" target={camera} propertyName="orthoLeft" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Left" target={camera} propertyName="orthoLeft" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 }
                 {
                     camera.mode === BABYLON.Camera.ORTHOGRAPHIC_CAMERA &&
-                    <FloatLineComponent label="Top" target={camera} propertyName="orthoTop" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Top" target={camera} propertyName="orthoTop" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 }
                 {
                     camera.mode === BABYLON.Camera.ORTHOGRAPHIC_CAMERA &&
-                    <FloatLineComponent label="Right" target={camera} propertyName="orthoRight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Right" target={camera} propertyName="orthoRight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 }
                 {
                     camera.mode === BABYLON.Camera.ORTHOGRAPHIC_CAMERA &&
-                    <FloatLineComponent label="Bottom" target={camera} propertyName="orthoBottom" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Bottom" target={camera} propertyName="orthoBottom" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 }
 
             </LineContainerComponent>

+ 5 - 13
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx

@@ -6,39 +6,31 @@ import { LineContainerComponent } from "../../../lineContainerComponent";
 import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { LockObject } from "../lockObject";
 
 interface IFreeCameraPropertyGridComponentProps {
     camera: FreeCamera,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
 export class FreeCameraPropertyGridComponent extends React.Component<IFreeCameraPropertyGridComponentProps> {
-    private _timerIntervalId: number;
-
     constructor(props: IFreeCameraPropertyGridComponentProps) {
         super(props);
     }
 
-    componentWillMount() {
-        this._timerIntervalId = window.setInterval(() => this.forceUpdate(), 500);
-    }
-
-    componentWillUnmount() {
-        window.clearInterval(this._timerIntervalId);
-    }
-
     render() {
         const camera = this.props.camera;
 
         return (
             <div className="pane">
-                <CommonCameraPropertyGridComponent camera={camera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonCameraPropertyGridComponent lockObject={this.props.lockObject} camera={camera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="TRANSFORMS">
                     <Vector3LineComponent label="Position" target={camera} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="CONTROLS" closed={true}>
-                    <FloatLineComponent label="Angular sensitivity" target={camera} propertyName="angularSensibility" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Speed" target={camera} propertyName="speed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Angular sensitivity" target={camera} propertyName="angularSensibility" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Speed" target={camera} propertyName="speed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="COLLISIONS" closed={true}>
                     <CheckBoxLineComponent label="Check collisions" target={camera} propertyName="checkCollisions" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 0 - 102
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/propertyGridTabComponent.tsx

@@ -1,102 +0,0 @@
-import * as React from "react";
-import { PaneComponent, IPaneComponentProps } from "../../../paneComponent";
-import { Mesh, TransformNode, Material, StandardMaterial, Texture, PBRMaterial, Scene, FreeCamera, ArcRotateCamera, HemisphericLight, PointLight } from "babylonjs";
-import { MaterialPropertyGridComponent } from "../materials/materialPropertyGridComponent";
-import { StandardMaterialPropertyGridComponent } from "../materials/standardMaterialPropertyGridComponent";
-import { TexturePropertyGridComponent } from "../materials/texturePropertyGridComponent";
-import { PBRMaterialPropertyGridComponent } from "../materials/pbrMaterialPropertyGridComponent";
-import { ScenePropertyGridComponent } from "../scenePropertyGridComponent";
-import { HemisphericLightPropertyGridComponent } from "../lights/hemisphericLightPropertyGridComponent";
-import { PointLightPropertyGridComponent } from "../lights/pointLightPropertyGridComponent";
-import { FreeCameraPropertyGridComponent } from "./freeCameraPropertyGridComponent";
-import { ArcRotateCameraPropertyGridComponent } from "./arcRotateCameraPropertyGridComponent";
-import { MeshPropertyGridComponent } from "../meshes/meshPropertyGridComponent";
-import { TransformNodePropertyGridComponent } from "../meshes/transformNodePropertyGridComponent";
-
-export class PropertyGridTabComponent extends PaneComponent {
-    constructor(props: IPaneComponentProps) {
-        super(props);
-    }
-
-    render() {
-        const entity = this.props.selectedEntity;
-
-        if (!entity) {
-            return null;
-        }
-
-        if (entity.getClassName) {
-            const className = entity.getClassName();
-
-            if (className.indexOf("Mesh") !== -1) {
-                const mesh = entity as Mesh;
-                if (mesh.getTotalVertices() > 0) {
-                    return (<MeshPropertyGridComponent mesh={mesh}
-                        onSelectionChangedObservable={this.props.onSelectionChangedObservable}
-                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-                }
-            }
-
-            if (className.indexOf("FreeCamera") !== -1) {
-                const freeCamera = entity as FreeCamera;
-                return (<FreeCameraPropertyGridComponent camera={freeCamera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className.indexOf("ArcRotateCamera") !== -1) {
-                const arcRotateCamera = entity as ArcRotateCamera;
-                return (<ArcRotateCameraPropertyGridComponent camera={arcRotateCamera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className === "HemisphericLight") {
-                const hemisphericLight = entity as HemisphericLight;
-                return (<HemisphericLightPropertyGridComponent
-                    light={hemisphericLight}
-                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className === "PointLight") {
-                const pointLight = entity as PointLight;
-                return (<PointLightPropertyGridComponent
-                    light={pointLight}
-                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className.indexOf("TransformNode") !== -1 || className.indexOf("Mesh") !== -1) {
-                const transformNode = entity as TransformNode;
-                return (<TransformNodePropertyGridComponent transformNode={transformNode} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className === "StandardMaterial") {
-                const material = entity as StandardMaterial;
-                return (<StandardMaterialPropertyGridComponent
-                    material={material}
-                    onSelectionChangedObservable={this.props.onSelectionChangedObservable}
-                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className === "PBRMaterial") {
-                const material = entity as PBRMaterial;
-                return (<PBRMaterialPropertyGridComponent
-                    material={material}
-                    onSelectionChangedObservable={this.props.onSelectionChangedObservable}
-                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className.indexOf("Material") !== -1) {
-                const material = entity as Material;
-                return (<MaterialPropertyGridComponent material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-            if (className.indexOf("Texture") !== -1) {
-                const texture = entity as Texture;
-                return (<TexturePropertyGridComponent texture={texture} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-            }
-
-        } else if (entity.transformNodes) {
-            const scene = entity as Scene;
-            return (<ScenePropertyGridComponent scene={scene} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
-        }
-
-        return null;
-    }
-}

+ 5 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/fogPropertyGridComponent.tsx

@@ -4,9 +4,11 @@ import { PropertyChangedEvent } from "../../../propertyChangedEvent";
 import { Color3LineComponent } from "../../lines/color3LineComponent";
 import { FloatLineComponent } from "../../lines/floatLineComponent";
 import { OptionsLineComponent } from "../../lines/optionsLineComponent";
+import { LockObject } from "./lockObject";
 
 interface IFogPropertyGridComponentProps {
     scene: Scene,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -36,15 +38,15 @@ export class FogPropertyGridComponent extends React.Component<IFogPropertyGridCo
                 }
                 {
                     (this.state.mode === BABYLON.Scene.FOGMODE_EXP || this.state.mode === BABYLON.Scene.FOGMODE_EXP2) &&
-                    <FloatLineComponent label="Fog density" target={scene} propertyName="fogDensity" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Fog density" target={scene} propertyName="fogDensity" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 }
                 {
                     this.state.mode === BABYLON.Scene.FOGMODE_LINEAR &&
-                    <FloatLineComponent label="Fog start" target={scene} propertyName="fogStart" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Fog start" target={scene} propertyName="fogStart" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 }
                 {
                     this.state.mode === BABYLON.Scene.FOGMODE_LINEAR &&
-                    <FloatLineComponent label="Fog end" target={scene} propertyName="fogEnd" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Fog end" target={scene} propertyName="fogEnd" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 }
             </div>
         );

+ 3 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/gui/colorPickerPropertyGridComponent.tsx

@@ -5,9 +5,11 @@ import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridC
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { ColorPicker } from "babylonjs-gui/2D/controls/colorpicker";
 import { Color3LineComponent } from "../../../lines/color3LineComponent";
+import { LockObject } from "../lockObject";
 
 interface IColorPickerPropertyGridComponentProps {
     colorPicker: ColorPicker,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -21,7 +23,7 @@ export class ColorPickerPropertyGridComponent extends React.Component<IColorPick
 
         return (
             <div className="pane">
-                <CommonControlPropertyGridComponent control={colorPicker} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={colorPicker} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="COLORPICKER">
                     <Color3LineComponent label="Color" target={colorPicker} propertyName="value" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>

+ 20 - 18
inspector/src/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent.tsx

@@ -7,9 +7,11 @@ import { Control } from "babylonjs-gui/2D/controls/control";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { LockObject } from "../lockObject";
 
 interface ICommonControlPropertyGridComponentProps {
     control: Control,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -26,31 +28,31 @@ export class CommonControlPropertyGridComponent extends React.Component<ICommonC
                 <LineContainerComponent title="GENERAL">
                     <TextLineComponent label="Class" value={control.getClassName()} />
                     <SliderLineComponent label="Alpha" target={control} propertyName="alpha" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Color" target={control} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Background" target={control} propertyName="background" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Color" target={control} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Background" target={control} propertyName="background" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="POSITION">
-                    <TextInputLineComponent label="Left" target={control} propertyName="left" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Top" target={control} propertyName="top" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Width" target={control} propertyName="width" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Height" target={control} propertyName="height" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Padding left" target={control} propertyName="paddingLeft" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Padding top" target={control} propertyName="paddingTop" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Padding right" target={control} propertyName="paddingRight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Padding bottom" target={control} propertyName="paddingBottom" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Left" target={control} propertyName="left" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Top" target={control} propertyName="top" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Width" target={control} propertyName="width" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Height" target={control} propertyName="height" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Padding left" target={control} propertyName="paddingLeft" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Padding top" target={control} propertyName="paddingTop" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Padding right" target={control} propertyName="paddingRight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Padding bottom" target={control} propertyName="paddingBottom" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="TRANSFORMATION" closed={true}>
-                    <FloatLineComponent label="ScaleX" target={control} propertyName="scaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="ScaleY" target={control} propertyName="scaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="ScaleX" target={control} propertyName="scaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="ScaleY" target={control} propertyName="scaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Rotation" target={control} propertyName="rotation" minimum={0} maximum={2 * Math.PI} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Transform center X" target={control} propertyName="transformCenterX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Transform center Y" target={control} propertyName="transformCenterY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Transform center X" target={control} propertyName="transformCenterX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Transform center Y" target={control} propertyName="transformCenterY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="FONT" closed={true}>
-                    <TextInputLineComponent label="Family" target={control} propertyName="fontFamily" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Size" target={control} propertyName="fontSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Weight" target={control} propertyName="fontWeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Style" target={control} propertyName="fontStyle" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Family" target={control} propertyName="fontFamily" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Size" target={control} propertyName="fontSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Weight" target={control} propertyName="fontWeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Style" target={control} propertyName="fontStyle" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
             </div>
         );

+ 3 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/gui/controlPropertyGridComponent.tsx

@@ -3,9 +3,11 @@ import { Observable } from "babylonjs";
 import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
 import { Control } from "babylonjs-gui/2D/controls/control";
 import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
 
 interface IControlPropertyGridComponentProps {
     control: Control,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -19,7 +21,7 @@ export class ControlPropertyGridComponent extends React.Component<IControlProper
 
         return (
             <div className="pane">
-                <CommonControlPropertyGridComponent control={control} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={control} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </div>
         );
     }

+ 13 - 11
inspector/src/components/actionTabs/tabs/propertyGrids/gui/inputTextPropertyGridComponent.tsx

@@ -8,9 +8,11 @@ import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { LockObject } from "../lockObject";
 
 interface IInputTextPropertyGridComponentProps {
     inputText: InputText,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -24,21 +26,21 @@ export class InputTextPropertyGridComponent extends React.Component<IInputTextPr
 
         return (
             <div className="pane">
-                <CommonControlPropertyGridComponent control={inputText} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={inputText} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="INPUTTEXT">
-                    <TextInputLineComponent label="Text" target={inputText} propertyName="text" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Prompt" target={inputText} propertyName="promptMessage" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Max width" target={inputText} propertyName="maxWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Highlight color" target={inputText} propertyName="textHighlightColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Text" target={inputText} propertyName="text" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Prompt" target={inputText} propertyName="promptMessage" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Max width" target={inputText} propertyName="maxWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Highlight color" target={inputText} propertyName="textHighlightColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Highligher opacity" minimum={0} maximum={1} step={0.01} target={inputText} propertyName="highligherOpacity" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="On focus select all" target={inputText} propertyName="onFocusSelectAll" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Focused background" target={inputText} propertyName="focusedBackground" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Max width" target={inputText} propertyName="maxWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Margin" target={inputText} propertyName="margin" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Focused background" target={inputText} propertyName="focusedBackground" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Max width" target={inputText} propertyName="maxWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Margin" target={inputText} propertyName="margin" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Auto stretch width" target={inputText} propertyName="autoStretchWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Thickness" target={inputText} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Placeholder text" target={inputText} propertyName="placeholderText" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent label="Placeholder color" target={inputText} propertyName="placeholderColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={inputText} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Placeholder text" target={inputText} propertyName="placeholderText" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Placeholder color" target={inputText} propertyName="placeholderColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
             </div>
         );

+ 4 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/gui/textBlockPropertyGridComponent.tsx

@@ -5,9 +5,11 @@ import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridC
 import { TextBlock } from "babylonjs-gui";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { LockObject } from "../lockObject";
 
 interface ITextBlockPropertyGridComponentProps {
     textBlock: TextBlock,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -21,9 +23,9 @@ export class TextBlockPropertyGridComponent extends React.Component<ITextBlockPr
 
         return (
             <div className="pane">
-                <CommonControlPropertyGridComponent control={textBlock} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={textBlock} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="TEXTBLOCK">
-                    <TextInputLineComponent label="Text" target={textBlock} propertyName="text" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Text" target={textBlock} propertyName="text" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
             </div>
         );

+ 3 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonLightPropertyGridComponent.tsx

@@ -4,9 +4,11 @@ import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { TextLineComponent } from "../../../lines/textLineComponent";
+import { LockObject } from "../lockObject";
 
 interface ICommonLightPropertyGridComponentProps {
     light: Light,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -23,7 +25,7 @@ export class CommonLightPropertyGridComponent extends React.Component<ICommonLig
                 <TextLineComponent label="ID" value={light.id} />
                 <TextLineComponent label="Unique ID" value={light.uniqueId.toString()} />
                 <TextLineComponent label="Class" value={light.getClassName()} />
-                <FloatLineComponent label="Intensity" target={light} propertyName="intensity" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <FloatLineComponent lockObject={this.props.lockObject} label="Intensity" target={light} propertyName="intensity" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </LineContainerComponent>
         );
     }

+ 4 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx

@@ -4,9 +4,11 @@ import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { LockObject } from "../lockObject";
 
 interface ICommonShadowLightPropertyGridComponentProps {
     light: IShadowLight,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -21,8 +23,8 @@ export class CommonShadowLightPropertyGridComponent extends React.Component<ICom
         return (
             <LineContainerComponent title="SHADOWS">
                 <CheckBoxLineComponent label="Shadows enabled" target={light} propertyName="shadowEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                <FloatLineComponent label="Shadows near plane" target={light} propertyName="shadowMinZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                <FloatLineComponent label="Shadows far plane" target={light} propertyName="shadowMaxZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <FloatLineComponent lockObject={this.props.lockObject} label="Shadows near plane" target={light} propertyName="shadowMinZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <FloatLineComponent lockObject={this.props.lockObject} label="Shadows far plane" target={light} propertyName="shadowMaxZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </LineContainerComponent>
         );
     }

+ 4 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx

@@ -6,9 +6,11 @@ import { LineContainerComponent } from "../../../lineContainerComponent";
 import { Color3LineComponent } from "../../../lines/color3LineComponent";
 import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
 import { CommonShadowLightPropertyGridComponent } from "./commonShadowLightPropertyGridComponent";
+import { LockObject } from "../lockObject";
 
 interface IDirectionalLightPropertyGridComponentProps {
     light: DirectionalLight,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -22,14 +24,14 @@ export class DirectionalLightPropertyGridComponent extends React.Component<IDire
 
         return (
             <div className="pane">
-                <CommonLightPropertyGridComponent light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonLightPropertyGridComponent lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="SETUP">
                     <Color3LineComponent label="Diffuse" target={light} propertyName="diffuse" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Specular" target={light} propertyName="specular" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Position" target={light} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Direction" target={light} propertyName="direction" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
-                <CommonShadowLightPropertyGridComponent light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonShadowLightPropertyGridComponent lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </div>
         );
     }

+ 3 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/lights/hemisphericLightPropertyGridComponent.tsx

@@ -5,9 +5,11 @@ import { CommonLightPropertyGridComponent } from "./commonLightPropertyGridCompo
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { Color3LineComponent } from "../../../lines/color3LineComponent";
 import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
+import { LockObject } from "../lockObject";
 
 interface IHemisphericLightPropertyGridComponentProps {
     light: HemisphericLight,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -21,7 +23,7 @@ export class HemisphericLightPropertyGridComponent extends React.Component<IHemi
 
         return (
             <div className="pane">
-                <CommonLightPropertyGridComponent light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonLightPropertyGridComponent lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="SETUP">
                     <Color3LineComponent label="Diffuse" target={light} propertyName="diffuse" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Ground" target={light} propertyName="groundColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 4 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/lights/pointLightPropertyGridComponent.tsx

@@ -6,9 +6,11 @@ import { LineContainerComponent } from "../../../lineContainerComponent";
 import { Color3LineComponent } from "../../../lines/color3LineComponent";
 import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
 import { CommonShadowLightPropertyGridComponent } from "./commonShadowLightPropertyGridComponent";
+import { LockObject } from "../lockObject";
 
 interface IPointLightPropertyGridComponentProps {
     light: PointLight,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -22,13 +24,13 @@ export class PointLightPropertyGridComponent extends React.Component<IPointLight
 
         return (
             <div className="pane">
-                <CommonLightPropertyGridComponent light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonLightPropertyGridComponent lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="SETUP">
                     <Color3LineComponent label="Diffuse" target={light} propertyName="diffuse" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Specular" target={light} propertyName="specular" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Position" target={light} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
-                <CommonShadowLightPropertyGridComponent light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonShadowLightPropertyGridComponent lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </div>
         );
     }

+ 6 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/lights/spotLightPropertyGridComponent.tsx

@@ -7,9 +7,11 @@ import { Color3LineComponent } from "../../../lines/color3LineComponent";
 import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { CommonShadowLightPropertyGridComponent } from "./commonShadowLightPropertyGridComponent";
+import { LockObject } from "../lockObject";
 
 interface ISpotLightPropertyGridComponentProps {
     light: SpotLight,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -23,16 +25,16 @@ export class SpotLightPropertyGridComponent extends React.Component<ISpotLightPr
 
         return (
             <div className="pane">
-                <CommonLightPropertyGridComponent light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonLightPropertyGridComponent lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="SETUP">
                     <Color3LineComponent label="Diffuse" target={light} propertyName="diffuse" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Specular" target={light} propertyName="specular" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Position" target={light} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Direction" target={light} propertyName="direction" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Angle" target={light} propertyName="angle" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="Exponent" target={light} propertyName="exponent" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Angle" target={light} propertyName="angle" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Exponent" target={light} propertyName="exponent" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
-                <CommonShadowLightPropertyGridComponent light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonShadowLightPropertyGridComponent lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </div>
         );
     }

+ 9 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/lockObject.ts

@@ -0,0 +1,9 @@
+/**
+ * Class used to provide lock mechanism
+ */
+export class LockObject {
+    /**
+     * Gets or set if the lock is engaged
+     */
+    public lock = false;
+}

+ 3 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/backgroundMaterialPropertyGridComponent.tsx

@@ -7,9 +7,11 @@ import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { CommonMaterialPropertyGridComponent } from "./commonMaterialPropertyGridComponent";
 import { TextureLinkLineComponent } from "../../../lines/textureLinkLineComponent";
+import { LockObject } from "../lockObject";
 
 interface IBackgroundMaterialPropertyGridComponentProps {
     material: BackgroundMaterial,
+    lockObject: LockObject,
     onSelectionChangedObservable?: Observable<any>,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
@@ -41,7 +43,7 @@ export class BackgroundMaterialPropertyGridComponent extends React.Component<IBa
 
         return (
             <div className="pane">
-                <CommonMaterialPropertyGridComponent material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonMaterialPropertyGridComponent lockObject={this.props.lockObject} material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="LIGHTING & COLORS">
                     <Color3LineComponent label="Primary" target={material} propertyName="primaryColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Shadow level" target={material} propertyName="primaryColorShadowLevel" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 3 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/commonMaterialPropertyGridComponent.tsx

@@ -6,9 +6,11 @@ import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { TextLineComponent } from "../../../lines/textLineComponent";
 import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+import { LockObject } from "../lockObject";
 
 interface ICommonMaterialPropertyGridComponentProps {
     material: Material,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -65,6 +67,7 @@ export class CommonMaterialPropertyGridComponent extends React.Component<ICommon
                         <OptionsLineComponent label="Transparency mode" options={transparencyModeOptions} target={material} propertyName="transparencyMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => this.setState({ transparencyMode: value })} />
                     }
                     <OptionsLineComponent label="Alpha mode" options={alphaModeOptions} target={material} propertyName="alphaMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => this.setState({ alphaMode: value })} />
+                    <CheckBoxLineComponent label="Separate culling pass" target={material} propertyName="separateCullingPass" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
             </div>
         );

+ 3 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/materialPropertyGridComponent.tsx

@@ -2,9 +2,11 @@ import * as React from "react";
 import { Material, Observable } from "babylonjs";
 import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
 import { CommonMaterialPropertyGridComponent } from "./commonMaterialPropertyGridComponent";
+import { LockObject } from "../lockObject";
 
 interface IMaterialPropertyGridComponentProps {
     material: Material,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -18,7 +20,7 @@ export class MaterialPropertyGridComponent extends React.Component<IMaterialProp
 
         return (
             <div className="pane">
-                <CommonMaterialPropertyGridComponent material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonMaterialPropertyGridComponent lockObject={this.props.lockObject} material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </div>
         );
     }

+ 5 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx

@@ -7,9 +7,11 @@ import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { CommonMaterialPropertyGridComponent } from "./commonMaterialPropertyGridComponent";
 import { TextureLinkLineComponent } from "../../../lines/textureLinkLineComponent";
+import { LockObject } from "../lockObject";
 
 interface IPBRMaterialPropertyGridComponentProps {
     material: PBRMaterial,
+    lockObject: LockObject,
     onSelectionChangedObservable?: Observable<any>,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
@@ -49,7 +51,7 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
 
         return (
             <div className="pane">
-                <CommonMaterialPropertyGridComponent material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonMaterialPropertyGridComponent lockObject={this.props.lockObject} material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 {this.renderTextures()}
                 <LineContainerComponent title="LIGHTING & COLORS">
                     <Color3LineComponent label="Albedo" target={material} propertyName="albedoColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -58,10 +60,10 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                     <Color3LineComponent label="Ambient" target={material} propertyName="ambientColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="LEVELS" closed={true}>
-                    <SliderLineComponent label="Environment intensity" target={material} propertyName="environmentIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Environment" target={material} propertyName="environmentIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Metallic" target={material} propertyName="metallic" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Micro-surface" target={material} propertyName="microSurface" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <SliderLineComponent label="Specular intensity" target={material} propertyName="specularIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Specular" target={material} propertyName="specularIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {
                         material.bumpTexture &&
                         <SliderLineComponent label="Bump strength" target={material.bumpTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 41 - 9
inspector/src/components/actionTabs/tabs/propertyGrids/materials/standardMaterialPropertyGridComponent.tsx

@@ -6,9 +6,11 @@ import { Color3LineComponent } from "../../../lines/color3LineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { CommonMaterialPropertyGridComponent } from "./commonMaterialPropertyGridComponent";
 import { TextureLinkLineComponent } from "../../../lines/textureLinkLineComponent";
+import { LockObject } from "../lockObject";
 
 interface IStandardMaterialPropertyGridComponentProps {
     material: StandardMaterial,
+    lockObject: LockObject,
     onSelectionChangedObservable?: Observable<any>,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
@@ -30,19 +32,11 @@ export class StandardMaterialPropertyGridComponent extends React.Component<IStan
         return (
             <LineContainerComponent title="TEXTURES">
                 <TextureLinkLineComponent label="Diffuse" texture={material.diffuseTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                {
-                    material.diffuseTexture &&
-                    <SliderLineComponent label="Diffuse level" target={material.diffuseTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                }
                 <TextureLinkLineComponent label="Specular" texture={material.specularTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Reflection" texture={material.reflectionTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Refraction" texture={material.refractionTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Bump" texture={material.bumpTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                {
-                    material.bumpTexture &&
-                    <SliderLineComponent label="Bump level" target={material.bumpTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                }
                 <TextureLinkLineComponent label="Opacity" texture={material.opacityTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Ambient" texture={material.ambientTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
@@ -55,7 +49,7 @@ export class StandardMaterialPropertyGridComponent extends React.Component<IStan
 
         return (
             <div className="pane">
-                <CommonMaterialPropertyGridComponent material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <CommonMaterialPropertyGridComponent lockObject={this.props.lockObject} material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 {this.renderTextures()}
                 <LineContainerComponent title="LIGHTING & COLORS">
                     <Color3LineComponent label="Diffuse" target={material} propertyName="diffuseColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -64,6 +58,44 @@ export class StandardMaterialPropertyGridComponent extends React.Component<IStan
                     <Color3LineComponent label="Emissive" target={material} propertyName="emissiveColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Ambient" target={material} propertyName="ambientColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
+                <LineContainerComponent title="LEVELS" closed={true}>
+                    {
+                        material.diffuseTexture &&
+                        <SliderLineComponent label="Diffuse level" target={material.diffuseTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.specularTexture &&
+                        <SliderLineComponent label="Specular level" target={material.specularTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.reflectionTexture &&
+                        <SliderLineComponent label="Reflection level" target={material.reflectionTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.refractionTexture &&
+                        <SliderLineComponent label="Refraction level" target={material.refractionTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.emissiveTexture &&
+                        <SliderLineComponent label="Emissive level" target={material.emissiveTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.bumpTexture &&
+                        <SliderLineComponent label="Bump level" target={material.bumpTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.opacityTexture &&
+                        <SliderLineComponent label="Opacity level" target={material.opacityTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.ambientTexture &&
+                        <SliderLineComponent label="Ambient level" target={material.ambientTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.lightmapTexture &&
+                        <SliderLineComponent label="Lightmap level" target={material.lightmapTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                </LineContainerComponent>
             </div>
         );
     }

+ 54 - 15
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -1,5 +1,5 @@
 import * as React from "react";
-import { Texture, Observable } from "babylonjs";
+import { Texture, BaseTexture, CubeTexture, Observable } from "babylonjs";
 import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
@@ -9,9 +9,12 @@ import { TextureLineComponent } from "../../../lines/textureLineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { AdvancedDynamicTexture } from "babylonjs-gui";
 import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+import { FileButtonLineComponent } from "../../../lines/fileButtonLineComponent";
+import { LockObject } from "../lockObject";
 
 interface ITexturePropertyGridComponentProps {
-    texture: Texture,
+    texture: BaseTexture,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -20,6 +23,28 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         super(props);
     }
 
+    updateTexture(file: File) {
+        const texture = this.props.texture;
+        BABYLON.Tools.ReadFile(file, (data) => {
+            var blob = new Blob([data], { type: "octet/stream" });
+            var url = URL.createObjectURL(blob);
+
+            if (texture.isCube) {
+                let extension: string | undefined = undefined;
+                if (file.name.toLowerCase().indexOf(".dds") > 0) {
+                    extension = ".dds";
+                } else if (file.name.toLowerCase().indexOf(".env") > 0) {
+                    extension = ".env";
+                }
+
+                (texture as CubeTexture).updateURL(url, extension, () => this.forceUpdate());
+            } else {
+                (texture as Texture).updateURL(url, null, () => this.forceUpdate());
+            }
+
+        }, undefined, true);
+    }
+
     render() {
         const texture = this.props.texture;
         const adtTexture = texture instanceof AdvancedDynamicTexture ? texture as AdvancedDynamicTexture : null;
@@ -34,8 +59,11 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
             <div className="pane">
                 <LineContainerComponent title="PREVIEW">
                     <TextureLineComponent texture={texture} width={256} height={256} />
+                    <FileButtonLineComponent label="Replace texture" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
                 </LineContainerComponent>
                 <LineContainerComponent title="GENERAL">
+                    <TextLineComponent label="Unique ID" value={texture.uniqueId.toString()} />
+                    <TextLineComponent label="Class" value={texture.getClassName()} />
                     <TextLineComponent label="Has alpha" value={texture.hasAlpha ? "Yes" : "No"} />
                     <TextLineComponent label="Is 3D" value={texture.is3D ? "Yes" : "No"} />
                     <TextLineComponent label="Is cube" value={texture.isCube ? "Yes" : "No"} />
@@ -43,31 +71,42 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                     <TextLineComponent label="Has mipmaps" value={!texture.noMipmap ? "Yes" : "No"} />
                     <SliderLineComponent label="UV set" target={texture} propertyName="coordinatesIndex" minimum={0} maximum={3} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {
-                        texture.updateSamplingMode && 
+                        texture.updateSamplingMode &&
                         <OptionsLineComponent label="Sampling" options={samplingMode} target={texture} noDirectUpdate={true} propertyName="samplingMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => texture.updateSamplingMode(value)} />
                     }
                 </LineContainerComponent>
                 {
                     adtTexture &&
                     <LineContainerComponent title="ADVANCED TEXTURE PROPERTIES">
-                        <SliderLineComponent label="Render scale" minimum={0.1} maximum={5} target={adtTexture} propertyName="renderScale" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <SliderLineComponent label="Render scale" minimum={0.1} maximum={5} step={0.1} target={adtTexture} propertyName="renderScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         <CheckBoxLineComponent label="Premultiply alpha" target={adtTexture} propertyName="premulAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <FloatLineComponent label="Ideal width" target={adtTexture} propertyName="idealWidth" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <FloatLineComponent label="Ideal height" target={adtTexture} propertyName="idealHeight" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal width" target={adtTexture} propertyName="idealWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal height" target={adtTexture} propertyName="idealHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         <CheckBoxLineComponent label="Use smallest ideal" target={adtTexture} propertyName="useSmallestIdeal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         <CheckBoxLineComponent label="Render at ideal size" target={adtTexture} propertyName="renderAtIdealSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     </LineContainerComponent>
                 }
                 <LineContainerComponent title="TRANSFORM">
-                    <FloatLineComponent label="U offset" target={texture} propertyName="uOffset" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V offset" target={texture} propertyName="vOffset" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V scale" target={texture} propertyName="uScale" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V scale" target={texture} propertyName="vScale" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="U angle" target={texture} propertyName="uAng" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V angle" target={texture} propertyName="vAng" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="W angle" target={texture} propertyName="wAng" step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <CheckBoxLineComponent label="Clamp U" isSelected={() => texture.wrapU === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapU = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
-                    <CheckBoxLineComponent label="Clamp V" isSelected={() => texture.wrapV === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapV = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
+                    {
+                        !texture.isCube &&
+                        <div>
+                            <FloatLineComponent lockObject={this.props.lockObject} label="U offset" target={texture} propertyName="uOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="V offset" target={texture} propertyName="vOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="V scale" target={texture} propertyName="uScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="V scale" target={texture} propertyName="vScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="U angle" target={texture} propertyName="uAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="V angle" target={texture} propertyName="vAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="W angle" target={texture} propertyName="wAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Clamp U" isSelected={() => texture.wrapU === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapU = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
+                            <CheckBoxLineComponent label="Clamp V" isSelected={() => texture.wrapV === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapV = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
+                        </div>
+                    }
+                    {
+                        texture.isCube &&
+                        <div>
+                            <SliderLineComponent label="Rotation Y" minimum={0} maximum={2 * Math.PI} step={0.1} target={texture} propertyName="rotationY" />
+                        </div>
+                    }
                 </LineContainerComponent>
             </div>
         );

+ 37 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx

@@ -8,9 +8,12 @@ import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { QuaternionLineComponent } from "../../../lines/quaternionLineComponent";
 import { AxesViewerComponent } from "./axesViewerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { LockObject } from "../lockObject";
 
 interface IMeshPropertyGridComponentProps {
     mesh: Mesh,
+    lockObject: LockObject,
     onSelectionChangedObservable?: Observable<any>,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
@@ -107,6 +110,30 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         this.props.onSelectionChangedObservable.notifyObservers(mesh.material)
     }
 
+    convertPhysicsTypeToString(): string {
+        const mesh = this.props.mesh;
+        switch (mesh.physicsImpostor!.type) {
+            case BABYLON.PhysicsImpostor.NoImpostor:
+                return "No impostor";
+            case BABYLON.PhysicsImpostor.SphereImpostor:
+                return "Sphere";
+            case BABYLON.PhysicsImpostor.BoxImpostor:
+                return "Box";
+            case BABYLON.PhysicsImpostor.PlaneImpostor:
+                return "Plane";
+            case BABYLON.PhysicsImpostor.MeshImpostor:
+                return "Mesh";
+            case BABYLON.PhysicsImpostor.CylinderImpostor:
+                return "Cylinder";
+            case BABYLON.PhysicsImpostor.ParticleImpostor:
+                return "Particle";
+            case BABYLON.PhysicsImpostor.HeightmapImpostor:
+                return "Heightmap";
+        }
+
+        return "Unknown";
+    }
+
     render() {
         const mesh = this.props.mesh;
         const scene = mesh.getScene();
@@ -145,6 +172,7 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
                 </LineContainerComponent>
                 <LineContainerComponent title="DISPLAY" closed={true}>
                     <SliderLineComponent label="Visibility" target={mesh} propertyName="visibility" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Alpha index" target={mesh} propertyName="alphaIndex" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Receive shadows" target={mesh} propertyName="receiveShadows" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {
                         mesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind) &&
@@ -175,6 +203,15 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
                     <TextLineComponent label="has matrix weights" value={mesh.isVerticesDataPresent(BABYLON.VertexBuffer.MatricesWeightsKind) ? "Yes" : "No"} />
                     <TextLineComponent label="has matrix indices" value={mesh.isVerticesDataPresent(BABYLON.VertexBuffer.MatricesIndicesKind) ? "Yes" : "No"} />
                 </LineContainerComponent>
+                {
+                    mesh.physicsImpostor != null &&
+                    <LineContainerComponent title="PHYSICS" closed={true}>
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Mass" target={mesh.physicsImpostor} propertyName="mass" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Friction" target={mesh.physicsImpostor} propertyName="friction" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Restitution" target={mesh.physicsImpostor} propertyName="restitution" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <TextLineComponent label="Type" value={this.convertPhysicsTypeToString()} />
+                    </LineContainerComponent>
+                }
                 <LineContainerComponent title="DEBUG" closed={true}>
                     <CheckBoxLineComponent label="Show bounding box" target={mesh} propertyName="showBoundingBox" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {

+ 2 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/transformNodePropertyGridComponent.tsx

@@ -7,9 +7,11 @@ import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
 import { TextLineComponent } from "../../../lines/textLineComponent";
 import { QuaternionLineComponent } from "../../../lines/quaternionLineComponent";
 import { AxesViewerComponent } from "./axesViewerComponent";
+import { LockObject } from "../lockObject";
 
 interface ITransformNodePropertyGridComponentProps {
     transformNode: TransformNode,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 

+ 60 - 7
inspector/src/components/actionTabs/tabs/propertyGrids/scenePropertyGridComponent.tsx

@@ -1,5 +1,5 @@
 import * as React from "react";
-import { Observable, Scene, BaseTexture, Nullable } from "babylonjs";
+import { Observable, Scene, BaseTexture, Nullable, Vector3 } from "babylonjs";
 import { PropertyChangedEvent } from "../../../propertyChangedEvent";
 import { LineContainerComponent } from "../../lineContainerComponent";
 import { RadioButtonLineComponent } from "../../lines/radioLineComponent";
@@ -8,15 +8,22 @@ import { CheckBoxLineComponent } from "../../lines/checkBoxLineComponent";
 import { FogPropertyGridComponent } from "./fogPropertyGridComponent";
 import { FileButtonLineComponent } from "../../lines/fileButtonLineComponent";
 import { TextureLinkLineComponent } from "../../lines/textureLinkLineComponent";
+import { Vector3LineComponent } from "../../lines/vector3LineComponent";
+import { FloatLineComponent } from "../../lines/floatLineComponent";
+import { SliderLineComponent } from "../../lines/sliderLineComponent";
+import { OptionsLineComponent } from "../../lines/optionsLineComponent";
+import { LockObject } from "./lockObject";
 
 interface IScenePropertyGridComponentProps {
     scene: Scene,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>,
     onSelectionChangedObservable?: Observable<any>
 }
 
 export class ScenePropertyGridComponent extends React.Component<IScenePropertyGridComponentProps> {
     private _storedEnvironmentTexture: Nullable<BaseTexture>;
+    private _renderingModeGroupObservable = new BABYLON.Observable<RadioButtonLineComponent>();
 
     constructor(props: IScenePropertyGridComponentProps) {
         super(props);
@@ -71,17 +78,47 @@ export class ScenePropertyGridComponent extends React.Component<IScenePropertyGr
         }, undefined, true);
     }
 
+    updateGravity(newValue: Vector3) {
+        const scene = this.props.scene;
+        const physicsEngine = scene.getPhysicsEngine()!;
+
+        physicsEngine.setGravity(newValue);
+    }
+
+    updateTimeStep(newValue: number) {
+        const scene = this.props.scene;
+        const physicsEngine = scene.getPhysicsEngine()!;
+
+        physicsEngine.setTimeStep(newValue);
+    }
+
     render() {
         const scene = this.props.scene;
 
-        const renderingModeGroupObservable = new BABYLON.Observable<RadioButtonLineComponent>();
+
+        const physicsEngine = scene.getPhysicsEngine();
+        let dummy: Nullable<{ gravity: Vector3, timeStep: number }> = null;
+
+        if (physicsEngine) {
+            dummy = {
+                gravity: physicsEngine.gravity,
+                timeStep: physicsEngine.getTimeStep()
+            }
+        }
+
+        const imageProcessing = scene.imageProcessingConfiguration;
+
+        var toneMappingOptions = [
+            { label: "Standard", value: BABYLON.ImageProcessingConfiguration.TONEMAPPING_STANDARD },
+            { label: "ACES", value: BABYLON.ImageProcessingConfiguration.TONEMAPPING_ACES }
+        ]
 
         return (
             <div className="pane">
                 <LineContainerComponent title="RENDERING MODE">
-                    <RadioButtonLineComponent onSelectionChangedObservable={renderingModeGroupObservable} label="Point" isSelected={() => scene.forcePointsCloud} onSelect={() => this.setRenderingModes(true, false)} />
-                    <RadioButtonLineComponent onSelectionChangedObservable={renderingModeGroupObservable} label="Wireframe" isSelected={() => scene.forceWireframe} onSelect={() => this.setRenderingModes(false, true)} />
-                    <RadioButtonLineComponent onSelectionChangedObservable={renderingModeGroupObservable} label="Solid" isSelected={() => !scene.forcePointsCloud && !scene.forceWireframe} onSelect={() => this.setRenderingModes(false, false)} />
+                    <RadioButtonLineComponent onSelectionChangedObservable={this._renderingModeGroupObservable} label="Point" isSelected={() => scene.forcePointsCloud} onSelect={() => this.setRenderingModes(true, false)} />
+                    <RadioButtonLineComponent onSelectionChangedObservable={this._renderingModeGroupObservable} label="Wireframe" isSelected={() => scene.forceWireframe} onSelect={() => this.setRenderingModes(false, true)} />
+                    <RadioButtonLineComponent onSelectionChangedObservable={this._renderingModeGroupObservable} label="Solid" isSelected={() => !scene.forcePointsCloud && !scene.forceWireframe} onSelect={() => this.setRenderingModes(false, false)} />
                 </LineContainerComponent>
                 <LineContainerComponent title="ENVIRONMENT">
                     <Color3LineComponent label="Clear color" target={scene} propertyName="clearColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -92,8 +129,24 @@ export class ScenePropertyGridComponent extends React.Component<IScenePropertyGr
                         scene.environmentTexture &&
                         <TextureLinkLineComponent label="Env. texture" texture={scene.environmentTexture} onSelectionChangedObservable={this.props.onSelectionChangedObservable} />
                     }
-                    <FileButtonLineComponent label="Update environment texture" onClick={(file) => this.updateEnvironmentTexture(file)} />
-                    <FogPropertyGridComponent scene={scene} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FileButtonLineComponent label="Update environment texture" onClick={(file) => this.updateEnvironmentTexture(file)} accept=".dds, .env" />
+                    <FogPropertyGridComponent lockObject={this.props.lockObject} scene={scene} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent title="IMAGE PROCESSING">
+                    <SliderLineComponent minimum={0} maximum={4} step={0.1} label="Contrast" target={imageProcessing} propertyName="contrast" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent minimum={0} maximum={4} step={0.1} label="Exposure" target={imageProcessing} propertyName="exposure" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Tone mapping" target={imageProcessing} propertyName="toneMappingEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Tone mapping type" options={toneMappingOptions} target={imageProcessing} propertyName="toneMappingType" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => this.setState({ mode: value })} />
+                </LineContainerComponent>
+                {
+                    dummy !== null &&
+                    <LineContainerComponent title="PHYSICS" closed={true}>
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Time step" target={dummy} propertyName="timeStep" onChange={newValue => this.updateTimeStep(newValue)} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <Vector3LineComponent label="Gravity" target={dummy} propertyName="gravity" onChange={newValue => this.updateGravity(newValue)} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    </LineContainerComponent>
+                }
+                <LineContainerComponent title="COLLISIONS" closed={true}>
+                    <Vector3LineComponent label="Gravity" target={scene} propertyName="gravity" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
             </div>
         );

+ 3 - 3
inspector/src/components/actionTabs/tabs/tools/gltfComponent.tsx

@@ -76,11 +76,11 @@ export class GLTFComponent extends React.Component<IGLTFComponentProps> {
             <LineContainerComponent title="GLTF VALIDATION" closed={!issues.numErrors && !issues.numWarnings}>
                 {
                     issues.numErrors !== 0 &&
-                    <MessageLineComponent text="Your file has some validation issues" icon={faTimesCircle} color="Red"/>
+                    <MessageLineComponent text="Your file has some validation issues" icon={faTimesCircle} color="Red" />
                 }
                 {
                     issues.numErrors === 0 &&
-                    <MessageLineComponent text="Your file is a valid glTF file" icon={faCheck} color="Green"/>
+                    <MessageLineComponent text="Your file is a valid glTF file" icon={faCheck} color="Green" />
                 }
                 <TextLineComponent label="Errors" value={issues.numErrors.toString()} />
                 <TextLineComponent label="Warnings" value={issues.numWarnings.toString()} />
@@ -122,7 +122,7 @@ export class GLTFComponent extends React.Component<IGLTFComponentProps> {
                 </LineContainerComponent>
                 <LineContainerComponent title="GLTF EXTENSIONS" closed={true}>
                     <CheckBoxLineComponent label="MSFT_lod" isSelected={() => extensionStates["MSFT_lod"].enabled} onSelect={value => extensionStates["MSFT_lod"].enabled = value} />
-                    <FloatLineComponent label="Maximum LODs" target={extensionStates["MSFT_lod"]} propertyName="maxLODsToLoad" step={1} additionalClass="gltf-extension-property" />
+                    <FloatLineComponent label="Maximum LODs" target={extensionStates["MSFT_lod"]} propertyName="maxLODsToLoad" additionalClass="gltf-extension-property" />
                     <CheckBoxLineComponent label="MSFT_minecraftMesh" isSelected={() => extensionStates["MSFT_minecraftMesh"].enabled} onSelect={value => extensionStates["MSFT_minecraftMesh"].enabled = value} />
                     <CheckBoxLineComponent label="MSFT_sRGBFactors" isSelected={() => extensionStates["MSFT_sRGBFactors"].enabled} onSelect={value => extensionStates["MSFT_sRGBFactors"].enabled = value} />
                     <CheckBoxLineComponent label="MSFT_audio_emitter" isSelected={() => extensionStates["MSFT_audio_emitter"].enabled} onSelect={value => extensionStates["MSFT_audio_emitter"].enabled = value} />

+ 30 - 0
inspector/src/components/sceneExplorer/entities/animationGroupTreeItemComponent.tsx

@@ -0,0 +1,30 @@
+import { IExplorerExtensibilityGroup, AnimationGroup } from "babylonjs";
+import { faFilm } from '@fortawesome/free-solid-svg-icons';
+import { TreeItemLabelComponent } from "../treeItemLabelComponent";
+import { ExtensionsComponent } from "../extensionsComponent";
+import * as React from "react";
+
+interface IAnimationGroupItemComponentProps {
+    animationGroup: AnimationGroup,
+    extensibilityGroups?: IExplorerExtensibilityGroup[],
+    onClick: () => void
+}
+
+export class AnimationGroupItemComponent extends React.Component<IAnimationGroupItemComponentProps> {
+    constructor(props: IAnimationGroupItemComponentProps) {
+        super(props);
+    }
+
+
+    render() {
+        const animationGroup = this.props.animationGroup;
+        return (
+            <div className="animationGroupTools">
+                <TreeItemLabelComponent label={animationGroup.name} onClick={() => this.props.onClick()} icon={faFilm} color="cornflowerblue" />
+                {
+                    <ExtensionsComponent target={animationGroup} extensibilityGroups={this.props.extensibilityGroups} />
+                }
+            </div>
+        )
+    }
+}

+ 7 - 3
inspector/src/components/sceneExplorer/entities/cameraTreeItemComponent.tsx

@@ -57,13 +57,17 @@ export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComp
 
     render() {
         const isActiveElement = this.state.isActive ? <FontAwesomeIcon icon={faVideo} /> : <FontAwesomeIcon icon={faVideo} className="isNotActive" />;
+        const scene = this.props.camera.getScene()!;
 
         return (
             <div className="cameraTools">
                 <TreeItemLabelComponent label={this.props.camera.name} onClick={() => this.props.onClick()} icon={faCamera} color="green" />
-                <div className="activeCamera icon" onClick={() => this.setActive()} title="Set as main camera">
-                    {isActiveElement}
-                </div>
+                {
+                    (!scene.activeCameras || scene.activeCameras.length === 0) &&
+                    <div className="activeCamera icon" onClick={() => this.setActive()} title="Set as main camera">
+                        {isActiveElement}
+                    </div>
+                }
                 <ExtensionsComponent target={this.props.camera} extensibilityGroups={this.props.extensibilityGroups} />
             </div>
         )

+ 2 - 2
inspector/src/components/sceneExplorer/entities/transformNodeTreeItemComponent.tsx

@@ -1,5 +1,5 @@
 import { TransformNode, IExplorerExtensibilityGroup } from "babylonjs";
-import { faCube } from '@fortawesome/free-solid-svg-icons';
+import { faCodeBranch } from '@fortawesome/free-solid-svg-icons';
 import { TreeItemLabelComponent } from "../treeItemLabelComponent";
 import { ExtensionsComponent } from "../extensionsComponent";
 import * as React from "react";
@@ -20,7 +20,7 @@ export class TransformNodeItemComponent extends React.Component<ITransformNodeIt
         const transformNode = this.props.transformNode;
         return (
             <div className="transformNodeTools">
-                <TreeItemLabelComponent label={transformNode.name} onClick={() => this.props.onClick()} icon={faCube} color="cornflowerblue" />
+                <TreeItemLabelComponent label={transformNode.name} onClick={() => this.props.onClick()} icon={faCodeBranch} color="cornflowerblue" />
                 {
                     <ExtensionsComponent target={transformNode} extensibilityGroups={this.props.extensibilityGroups} />
                 }

+ 26 - 3
inspector/src/components/sceneExplorer/sceneExplorer.scss

@@ -135,10 +135,11 @@
 
             .text {
                 grid-column: 1;
-                display: flex;
-                align-items: center;
+                display: grid;
+                grid-template-columns: 20px 1fr;
 
                 .arrow {
+                    grid-column: 1;
                     margin-left: 0px;
                     color: white;
                     cursor: pointer;
@@ -146,10 +147,18 @@
                     margin-right: 6px;
                     opacity: 0.5;
                 }
+
+                .text-value {
+                    grid-column: 2;
+                    display: flex;
+                    align-items: center;
+                }
             }
 
-            .expandAll {
+            .expandAll {                
+                opacity: 0.5;
                 grid-column: 2;
+                margin-right: 10px;  
             }
         }
         
@@ -300,6 +309,20 @@
             }
         }
 
+        .animationGroupTools {
+            grid-column: 2;
+            width: 100%;
+            display: grid;
+            grid-template-columns: 1fr auto 5px;
+            align-items: center;
+            min-width: 0;
+           
+            .extensions {
+                width: 20px;
+                grid-column: 2;
+            }
+        }
+
         .meshTools {
             grid-column: 2;
             width: 100%;

+ 45 - 0
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -43,12 +43,21 @@ interface ISceneExplorerComponentProps {
 export class SceneExplorerComponent extends React.Component<ISceneExplorerComponentProps, { filter: Nullable<string>, selectedEntity: any, scene: Scene }> {
     private _onSelectionChangeObserver: Nullable<Observer<any>>;
     private _onNewSceneAddedObserver: Nullable<Observer<Scene>>;
+
     private _once = true;
 
+    private sceneMutationFunc: () => void;
+
     constructor(props: ISceneExplorerComponentProps) {
         super(props);
 
         this.state = { filter: null, selectedEntity: null, scene: this.props.scene };
+
+        this.sceneMutationFunc = this.processMutation.bind(this);
+    }
+
+    processMutation() {
+        this.forceUpdate();
     }
 
     componentWillMount() {
@@ -67,6 +76,22 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
         if (this._onNewSceneAddedObserver) {
             BABYLON.Engine.LastCreatedEngine!.onNewSceneAddedObservable.remove(this._onNewSceneAddedObserver);
         }
+
+        const scene = this.state.scene;
+
+        scene.onNewCameraAddedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onNewLightAddedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onNewMaterialAddedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onNewMeshAddedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onNewTextureAddedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onNewTransformNodeAddedObservable.removeCallback(this.sceneMutationFunc);
+
+        scene.onMeshRemovedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onCameraRemovedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onLightRemovedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onMaterialRemovedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onTransformNodeRemovedObservable.removeCallback(this.sceneMutationFunc);
+        scene.onTextureRemovedObservable.removeCallback(this.sceneMutationFunc);
     }
 
     filterContent(filter: string) {
@@ -179,6 +204,10 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                     guiElements && guiElements.length > 0 &&
                     <TreeItemComponent extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.state.selectedEntity} items={guiElements} label="GUI" offset={1} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} filter={this.state.filter} />
                 }
+                {
+                    scene.animationGroups.length > 0 &&
+                    <TreeItemComponent extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.state.selectedEntity} items={scene.animationGroups} label="Animation groups" offset={1} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} filter={this.state.filter} />
+                }
             </div>
         )
     }
@@ -214,6 +243,22 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
 
         if (this._once) {
             this._once = false;
+            const scene = this.state.scene;
+
+            scene.onNewCameraAddedObservable.add(this.sceneMutationFunc);
+            scene.onNewLightAddedObservable.add(this.sceneMutationFunc);
+            scene.onNewMaterialAddedObservable.add(this.sceneMutationFunc);
+            scene.onNewMeshAddedObservable.add(this.sceneMutationFunc);
+            scene.onNewTextureAddedObservable.add(this.sceneMutationFunc);
+            scene.onNewTransformNodeAddedObservable.add(this.sceneMutationFunc);
+
+            scene.onMeshRemovedObservable.add(this.sceneMutationFunc);
+            scene.onCameraRemovedObservable.add(this.sceneMutationFunc);
+            scene.onLightRemovedObservable.add(this.sceneMutationFunc);
+            scene.onMaterialRemovedObservable.add(this.sceneMutationFunc);
+            scene.onTransformNodeRemovedObservable.add(this.sceneMutationFunc);
+            scene.onTextureRemovedObservable.add(this.sceneMutationFunc);
+
             // A bit hacky but no other way to force the initial width to 300px and not auto
             setTimeout(() => {
                 const element = document.getElementById("sceneExplorer");

+ 15 - 7
inspector/src/components/sceneExplorer/treeItemComponent.tsx

@@ -28,9 +28,12 @@ class TreeItemExpandableHeaderComponent extends React.Component<ITreeItemExpanda
         return (
             <div className="expandableHeader">
                 <div className="text">
-                    <span className="arrow icon" onClick={() => this.props.onClick()}>
+                    <div className="arrow icon" onClick={() => this.props.onClick()}>
                         {chevron}
-                    </span> {this.props.label}
+                    </div> 
+                    <div className="text-value">
+                        {this.props.label}
+                    </div>
                 </div>
                 <div className="expandAll icon" onClick={() => this.expandAll()} title={this.props.isExpanded ? "Collapse all" : "Expand all"}>
                     {expandAll}
@@ -51,10 +54,15 @@ class TreeItemRootHeaderComponent extends React.Component<ITreeItemRootHeaderCom
 
     render() {
         return (
-            <div>
-                <span className="arrow icon">
-                    <FontAwesomeIcon icon={faBan} />
-                </span> {this.props.label}
+            <div className="expandableHeader">
+                <div className="text">
+                    <div className="arrow icon">
+                        <FontAwesomeIcon icon={faBan} />
+                    </div>
+                    <div className="text-value">
+                        {this.props.label}
+                    </div>
+                </div>
             </div>
         )
     }
@@ -151,7 +159,7 @@ export class TreeItemComponent extends React.Component<ITreeItemComponentProps,
                 {
                     sortedItems.map(item => {
                         return (
-                            <TreeItemSelectableComponent mustExpand={this.state.mustExpand} extensibilityGroups={this.props.extensibilityGroups} key={item.uniqueId} offset={this.props.offset + 2} selectedEntity={this.props.selectedEntity} entity={item} onSelectionChangedObservable={this.props.onSelectionChangedObservable} filter={this.props.filter} />
+                            <TreeItemSelectableComponent mustExpand={this.state.mustExpand} extensibilityGroups={this.props.extensibilityGroups} key={item.uniqueId} offset={this.props.offset + 1} selectedEntity={this.props.selectedEntity} entity={item} onSelectionChangedObservable={this.props.onSelectionChangedObservable} filter={this.props.filter} />
                         );
                     })
                 }

+ 6 - 1
inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx

@@ -1,4 +1,4 @@
-import { AbstractMesh, Camera, Light, Material, Texture, TransformNode, IExplorerExtensibilityGroup, Observable } from "babylonjs";
+import { AbstractMesh, Camera, Light, Material, Texture, TransformNode, IExplorerExtensibilityGroup, Observable, AnimationGroup } from "babylonjs";
 import { MeshTreeItemComponent } from "./entities/meshTreeItemComponent";
 import { CameraTreeItemComponent } from "./entities/cameraTreeItemComponent";
 import { LightTreeItemComponent } from "./entities/lightTreeItemComponent";
@@ -11,6 +11,7 @@ import * as React from "react";
 import { ControlTreeItemComponent } from "./entities/gui/controlTreeItemComponent";
 import { Control, AdvancedDynamicTexture } from "babylonjs-gui";
 import { AdvancedDynamicTextureTreeItemComponent } from "./entities/gui/advancedDynamicTextureTreeItemComponent";
+import { AnimationGroupItemComponent } from "./entities/animationGroupTreeItemComponent";
 
 interface ITreeItemSpecializedComponentProps {
     label: string,
@@ -68,6 +69,10 @@ export class TreeItemSpecializedComponent extends React.Component<ITreeItemSpeci
                 return (<AdvancedDynamicTextureTreeItemComponent onSelectionChangedObservable={this.props.onSelectionChangedObservable} extensibilityGroups={this.props.extensibilityGroups} texture={entity as AdvancedDynamicTexture} onClick={() => this.onClick()} />);
             }
 
+            if (className === "AnimationGroup") {
+                return (<AnimationGroupItemComponent extensibilityGroups={this.props.extensibilityGroups} animationGroup={entity as AnimationGroup} onClick={() => this.onClick()} />);
+            }
+
             if (className.indexOf("Texture") !== -1) {
                 return (<TextureTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} texture={entity as Texture} onClick={() => this.onClick()} />);
             }

+ 25 - 37
loaders/src/glTF/2.0/glTFLoader.ts

@@ -432,6 +432,18 @@ import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoad
                 }
             }
 
+            // Link all Babylon bones for each glTF node with the corresponding Babylon transform node.
+            // A glTF joint is a pointer to a glTF node in the glTF node hierarchy similar to Unity3D.
+            if (this.gltf.nodes) {
+                for (const node of this.gltf.nodes) {
+                    if (node._babylonTransformNode && node._babylonBones) {
+                        for (const babylonBone of node._babylonBones) {
+                            babylonBone.linkTransformNode(node._babylonTransformNode);
+                        }
+                    }
+                }
+            }
+
             promises.push(this._loadAnimationsAsync());
 
             this.logClose();
@@ -445,9 +457,6 @@ import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoad
                     callback(babylonMesh);
                 }
             }
-            else if (node._babylonTransformNode instanceof AbstractMesh) {
-                callback(node._babylonTransformNode);
-            }
         }
 
         private _getMeshes(): AbstractMesh[] {
@@ -561,12 +570,6 @@ import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoad
                     for (const index of node.children) {
                         const childNode = ArrayItem.Get(`${context}/children/${index}`, this.gltf.nodes, index);
                         promises.push(this.loadNodeAsync(`/nodes/${childNode.index}`, childNode, (childBabylonMesh) => {
-                            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                            if (childNode.skin != undefined) {
-                                childBabylonMesh.parent = this._rootBabylonMesh;
-                                return;
-                            }
-
                             childBabylonMesh.parent = babylonTransformNode;
                         }));
                     }
@@ -616,16 +619,16 @@ import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoad
                 const primitive = mesh.primitives[0];
                 promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, name, node, mesh, primitive, (babylonMesh) => {
                     node._babylonTransformNode = babylonMesh;
+                    node._primitiveBabylonMeshes = [babylonMesh];
                 }));
             }
             else {
-                const babylonTransformNode = new TransformNode(name, this.babylonScene);
-                node._babylonTransformNode = babylonTransformNode;
+                node._babylonTransformNode = new TransformNode(name, this.babylonScene);
+                node._primitiveBabylonMeshes = [];
                 for (const primitive of primitives) {
                     promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, `${name}_primitive${primitive.index}`, node, mesh, primitive, (babylonMesh) => {
-                        babylonMesh.parent = babylonTransformNode;
-                        node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
-                        node._primitiveBabylonMeshes.push(babylonMesh);
+                        babylonMesh.parent = node._babylonTransformNode!;
+                        node._primitiveBabylonMeshes!.push(babylonMesh);
                     }));
                 }
             }
@@ -893,14 +896,16 @@ import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoad
             };
 
             if (skin._data) {
-                const data = skin._data;
-                return data.promise.then(() => {
-                    assignSkeleton(data.babylonSkeleton);
-                });
+                assignSkeleton(skin._data.babylonSkeleton);
+                return skin._data.promise;
             }
 
             const skeletonId = `skeleton${skin.index}`;
             const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
+
+            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+            babylonSkeleton.overrideMesh = this._rootBabylonMesh;
+
             this._loadBones(context, skin, babylonSkeleton);
             assignSkeleton(babylonSkeleton);
 
@@ -1102,12 +1107,6 @@ import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoad
                 return Promise.resolve();
             }
 
-            // Ignore animations targeting TRS of skinned nodes.
-            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-            if (targetNode.skin != undefined && channel.target.path !== AnimationChannelTargetPath.WEIGHTS) {
-                return Promise.resolve();
-            }
-
             const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler);
             return this._loadAnimationSamplerAsync(`${animationContext}/samplers/${channel.sampler}`, sampler).then((data) => {
                 let targetPath: string;
@@ -1234,19 +1233,8 @@ import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoad
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     babylonAnimation.setKeys(keys);
 
-                    const babylonTransformNode = targetNode._babylonTransformNode!;
-                    const babylonBones = targetNode._babylonBones;
-                    if (babylonBones) {
-                        const babylonAnimationTargets = [babylonTransformNode, ...babylonBones];
-                        for (const babylonAnimationTarget of babylonAnimationTargets) {
-                            (<any>babylonAnimationTarget.animations).push(babylonAnimation);
-                        }
-                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
-                    }
-                    else {
-                        babylonTransformNode.animations.push(babylonAnimation);
-                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
-                    }
+                    targetNode._babylonTransformNode!.animations.push(babylonAnimation);
+                    babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonTransformNode!);
                 }
             });
         }

+ 1 - 1
package.json

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

+ 1 - 0
sandbox/index.css

@@ -189,6 +189,7 @@ a {
     color: white;
     width: calc(100% - 168px);
     min-height: 30px;
+    font-size: 14px;
 }
 
 .row {

+ 38 - 25
sandbox/index.js

@@ -17,6 +17,7 @@ var playBtn = document.getElementById("playBtn");
 var slider = document.getElementById("slider");
 var footer = document.getElementById("footer");
 var canvas = document.getElementById("renderCanvas");
+var canvasZone = document.getElementById("canvasZone");
 
 var indexOf = location.href.indexOf("?");
 if (indexOf !== -1) {
@@ -44,7 +45,7 @@ if (indexOf !== -1) {
 
 if (kiosk) {
     footer.style.display = "none";
-    canvas.style.height = "100%";
+    canvasZone.style.height = "100%";
 }
 
 if (BABYLON.Engine.isSupported()) {
@@ -373,6 +374,27 @@ dropdownBtn.addEventListener("click", function() {
     }
 });
 
+function selectCurrentGroup(group, index, animation) {
+    if (currentGroupIndex !== undefined) {
+        document.getElementById(formatId(currentGroup.name + "-" + currentGroupIndex)).classList.remove("active");
+    }
+    playBtn.classList.remove("play");
+    playBtn.classList.add("pause");
+
+    // start the new animation group
+    currentGroup = group;
+    currentGroupIndex = index;
+    animation.classList.add("active");
+    dropdownLabel.innerHTML = currentGroup.name;
+    dropdownLabel.title = currentGroup.name;
+
+    // set the slider
+    slider.setAttribute("min", currentGroup.from);
+    slider.setAttribute("max", currentGroup.to);
+    currentSliderValue = currentGroup.from;
+    slider.value = currentGroup.from;
+}
+
 function createDropdownLink(group, index) {
     var animation = document.createElement("a");
     animation.innerHTML = group.name;
@@ -382,55 +404,46 @@ function createDropdownLink(group, index) {
         // stop the current animation group
         currentGroup.reset();
         currentGroup.stop();
-        document.getElementById(formatId(currentGroup.name + "-" + currentGroupIndex)).classList.remove("active");
-        playBtn.classList.remove("play");
-        playBtn.classList.add("pause");
-
-        // start the new animation group
-        currentGroup = group;
-        currentGroupIndex = index;
-        currentGroup.start(true);
-        this.classList.add("active");
-        dropdownLabel.innerHTML = currentGroup.name;
-        dropdownLabel.title = currentGroup.name;
-
-        // set the slider
-        slider.setAttribute("min", currentGroup.from);
-        slider.setAttribute("max", currentGroup.to);
-        currentSliderValue = currentGroup.from;
-        slider.value = currentGroup.from;
+
+        group.play(true);
 
         // hide the content of the dropdown
         displayDropdownContent(false);
     });
     dropdownContent.appendChild(animation);
+
+    group.onAnimationGroupPlayObservable.add(function(grp) {
+        selectCurrentGroup(grp, index, animation);
+    });
+
+    group.onAnimationGroupPauseObservable.add(function(grp) {
+        playBtn.classList.add("play");
+        playBtn.classList.remove("pause");
+    });
 }
 
 // event on the play/pause button
 playBtn.addEventListener("click", function() {
     // click on the button to run the animation
     if (this.classList.contains("play")) {
-        this.classList.remove("play");
-        this.classList.add("pause");
-        var currentFrame = slider.value;
         currentGroup.play(true);
     }
     // click on the button to pause the animation
     else {
-        this.classList.add("play");
-        this.classList.remove("pause");
         currentGroup.pause();
     }
 });
 
 // event on the slider
 slider.addEventListener("input", function() {
+    var value = parseFloat(this.value);
+
     if (playBtn.classList.contains("play")) {
         currentGroup.play(true);
-        currentGroup.goToFrame(this.value);
+        currentGroup.goToFrame(value);
         currentGroup.pause();
     } else {
-        currentGroup.goToFrame(this.value);
+        currentGroup.goToFrame(value);
     }
 });
 

+ 28 - 0
src/Animations/animationGroup.ts

@@ -30,9 +30,15 @@ import { Engine } from "Engines/engine";
         private _from = Number.MAX_VALUE;
         private _to = -Number.MAX_VALUE;
         private _isStarted: boolean;
+        private _isPaused: boolean;
         private _speedRatio = 1;
 
         /**
+         * Gets or sets the unique id of the node
+         */
+        public uniqueId: number;
+
+        /**
          * This observable will notify when one animation have ended.
          */
         public onAnimationEndObservable = new Observable<TargetedAnimation>();
@@ -48,6 +54,11 @@ import { Engine } from "Engines/engine";
         public onAnimationGroupPauseObservable = new Observable<AnimationGroup>();
 
         /**
+         * This observable will notify when all animations are playing.
+         */
+        public onAnimationGroupPlayObservable = new Observable<AnimationGroup>();
+
+        /**
          * Gets the first frame
          */
         public get from(): number {
@@ -69,6 +80,13 @@ import { Engine } from "Engines/engine";
         }
 
         /**
+         * Gets a value indicating that the current group is playing
+         */
+        public get isPlaying(): boolean {
+            return this._isStarted && !this._isPaused;
+        }
+
+        /**
          * Gets or sets the speed ratio to use for all animations
          */
         public get speedRatio(): number {
@@ -117,6 +135,7 @@ import { Engine } from "Engines/engine";
             public name: string,
             scene: Nullable<Scene> = null) {
             this._scene = scene || Engine.LastCreatedScene!;
+            this.uniqueId = this._scene.getUniqueId();
 
             this._scene.animationGroups.push(this);
         }
@@ -222,6 +241,9 @@ import { Engine } from "Engines/engine";
             }
 
             this._isStarted = true;
+            this._isPaused = false;
+
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
 
             return this;
         }
@@ -235,6 +257,8 @@ import { Engine } from "Engines/engine";
                 return this;
             }
 
+            this._isPaused = true;
+
             for (var index = 0; index < this._animatables.length; index++) {
                 let animatable = this._animatables[index];
                 animatable.pause();
@@ -266,6 +290,8 @@ import { Engine } from "Engines/engine";
                 this.start(loop, this._speedRatio);
             }
 
+            this._isPaused = false;
+
             return this;
         }
 
@@ -300,6 +326,8 @@ import { Engine } from "Engines/engine";
                 animatable.restart();
             }
 
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
+
             return this;
         }
 

+ 20 - 0
src/Bones/bone.ts

@@ -55,6 +55,9 @@ import { Node } from "node";
         private _needToCompose = false;
 
         /** @hidden */
+        public _linkedTransformNode: Nullable<TransformNode> = null;
+
+        /** @hidden */
         get _matrix(): Matrix {
             this._compose();
             return this._localMatrix;
@@ -202,6 +205,23 @@ import { Node } from "node";
             return this._absoluteTransform;
         }
 
+        /**
+         * Links with the given transform node.
+         * The local matrix of this bone is copied from the transform node every frame.
+         * @param transformNode defines the transform node to link to
+         */
+        public linkTransformNode(transformNode: Nullable<TransformNode>): void {
+            if (this._linkedTransformNode) {
+                this._skeleton._numBonesWithLinkedTransformNode--;
+            }
+
+            this._linkedTransformNode = transformNode;
+
+            if (this._linkedTransformNode) {
+                this._skeleton._numBonesWithLinkedTransformNode++;
+            }
+        }
+
         // Properties (matches AbstractMesh properties)
 
         /** Gets or sets current position (in local space) */

+ 22 - 3
src/Bones/skeleton.ts

@@ -20,17 +20,21 @@ import { Logger } from "Misc/logger";
      */
     export class Skeleton implements IAnimatable {
         /**
-         * Gets the list of child bones
+         * Defines the list of child bones
          */
         public bones = new Array<Bone>();
         /**
-         * Gets an estimate of the dimension of the skeleton at rest
+         * Defines an estimate of the dimension of the skeleton at rest
          */
         public dimensionsAtRest: Vector3;
         /**
-         * Gets a boolean indicating if the root matrix is provided by meshes or by the current skeleton (this is the default value)
+         * Defines a boolean indicating if the root matrix is provided by meshes or by the current skeleton (this is the default value)
          */
         public needInitialSkinMatrix = false;
+        /**
+         * Defines a mesh that override the matrix used to get the world matrix (null by default).
+         */
+        public overrideMesh: Nullable<AbstractMesh> = null;
 
         /**
          * Gets the list of animations attached to this skeleton
@@ -52,6 +56,9 @@ import { Logger } from "Misc/logger";
 
         private _canUseTextureForBones = false;
 
+        /** @hidden */
+        public _numBonesWithLinkedTransformNode = 0;
+
         /**
          * Specifies if the skeleton should be serialized
          */
@@ -385,6 +392,18 @@ import { Logger } from "Misc/logger";
          * Build all resources required to render a skeleton
          */
         public prepare(): void {
+            // Update the local matrix of bones with linked transform nodes.
+            if (this._numBonesWithLinkedTransformNode > 0) {
+                for (const bone of this.bones) {
+                    if (bone._linkedTransformNode) {
+                        // Computing the world matrix also computes the local matrix.
+                        bone._linkedTransformNode.computeWorldMatrix();
+                        bone._matrix = bone._linkedTransformNode._localMatrix;
+                        bone.markAsDirty();
+                    }
+                }
+            }
+
             if (!this._isDirty) {
                 return;
             }

+ 50 - 46
src/Debug/axesViewer.ts

@@ -8,14 +8,9 @@ import { LinesMesh } from "Meshes/linesMesh";
      * The Axes viewer will show 3 axes in a specific point in space
      */
     export class AxesViewer {
-
-        private _xline = [Vector3.Zero(), Vector3.Zero()];
-        private _yline = [Vector3.Zero(), Vector3.Zero()];
-        private _zline = [Vector3.Zero(), Vector3.Zero()];
-
-        private _xmesh: Nullable<LinesMesh>;
-        private _ymesh: Nullable<LinesMesh>;
-        private _zmesh: Nullable<LinesMesh>;
+        private _xmesh: Nullable<AbstractMesh>;
+        private _ymesh: Nullable<AbstractMesh>;
+        private _zmesh: Nullable<AbstractMesh>;
 
         /**
          * Gets the hosting scene
@@ -27,46 +22,64 @@ import { LinesMesh } from "Meshes/linesMesh";
         public scaleLines = 1;
 
         /** Gets the mesh used to render x-axis */
-        public get xAxisMesh(): Nullable<LinesMesh> {
+        public get xAxisMesh(): Nullable<AbstractMesh> {
             return this._xmesh;
         }
 
         /** Gets the mesh used to render x-axis */
-        public get yAxisMesh(): Nullable<LinesMesh> {
+        public get yAxisMesh(): Nullable<AbstractMesh> {
             return this._ymesh;
         }
 
         /** Gets the mesh used to render x-axis */
-        public get zAxisMesh(): Nullable<LinesMesh> {
+        public get zAxisMesh(): Nullable<AbstractMesh> {
             return this._zmesh;
         }
 
+        private static _recursiveChangeRenderingGroupId(mesh: AbstractMesh, id: number) {
+            mesh.renderingGroupId = id;
+            mesh.getChildMeshes().forEach((m) => {
+                AxesViewer._recursiveChangeRenderingGroupId(m, id);
+            });
+        }
+
         /**
          * Creates a new AxesViewer
          * @param scene defines the hosting scene
          * @param scaleLines defines a number used to scale line length (1 by default)
          */
         constructor(scene: Scene, scaleLines = 1) {
-
             this.scaleLines = scaleLines;
 
-            this._xmesh = Mesh.CreateLines("xline", this._xline, scene, true);
-            this._ymesh = Mesh.CreateLines("yline", this._yline, scene, true);
-            this._zmesh = Mesh.CreateLines("zline", this._zline, scene, true);
+            var greenColoredMaterial = new BABYLON.StandardMaterial("", scene);
+            greenColoredMaterial.disableLighting = true;
+            greenColoredMaterial.emissiveColor = BABYLON.Color3.Green().scale(0.5);
 
-            this._xmesh.renderingGroupId = 2;
-            this._ymesh.renderingGroupId = 2;
-            this._zmesh.renderingGroupId = 2;
+            var redColoredMaterial = new BABYLON.StandardMaterial("", scene);
+            redColoredMaterial.disableLighting = true;
+            redColoredMaterial.emissiveColor = BABYLON.Color3.Red().scale(0.5);
 
-            this._xmesh.material.checkReadyOnlyOnce = true;
-            this._xmesh.color = new Color3(1, 0, 0);
-            this._ymesh.material.checkReadyOnlyOnce = true;
-            this._ymesh.color = new Color3(0, 1, 0);
-            this._zmesh.material.checkReadyOnlyOnce = true;
-            this._zmesh.color = new Color3(0, 0, 1);
+            var blueColoredMaterial = new BABYLON.StandardMaterial("", scene);
+            blueColoredMaterial.disableLighting = true;
+            blueColoredMaterial.emissiveColor = BABYLON.Color3.Blue().scale(0.5);
 
-            this.scene = scene;
+            this._xmesh = BABYLON.AxisDragGizmo._CreateArrow(scene, redColoredMaterial);
+            this._ymesh = BABYLON.AxisDragGizmo._CreateArrow(scene, greenColoredMaterial);
+            this._zmesh = BABYLON.AxisDragGizmo._CreateArrow(scene, blueColoredMaterial);
+
+            this._xmesh.rotationQuaternion = new BABYLON.Quaternion();
+            this._xmesh.scaling.scaleInPlace(4);
+            this._ymesh.rotationQuaternion = new BABYLON.Quaternion();
+            this._ymesh.scaling.scaleInPlace(4);
+            this._zmesh.rotationQuaternion = new BABYLON.Quaternion();
+            this._zmesh.scaling.scaleInPlace(4);
+
+            AxesViewer._recursiveChangeRenderingGroupId(this._xmesh, 2);
+            AxesViewer._recursiveChangeRenderingGroupId(this._ymesh, 2);
+            AxesViewer._recursiveChangeRenderingGroupId(this._zmesh, 2);
 
+            this.scene = scene;
+            this.update(new BABYLON.Vector3(), Vector3.Right(), Vector3.Up(), Vector3.Forward());
         }
 
         /**
@@ -77,36 +90,27 @@ import { LinesMesh } from "Meshes/linesMesh";
          * @param zaxis defines the z axis of the viewer
          */
         public update(position: Vector3, xaxis: Vector3, yaxis: Vector3, zaxis: Vector3): void {
-
-            var scaleLines = this.scaleLines;
-
             if (this._xmesh) {
                 this._xmesh.position.copyFrom(position);
+
+                var cross = Vector3.Cross(Vector3.Forward(), xaxis);
+                this._xmesh.rotationQuaternion!.set(cross.x, cross.y, cross.z, 1 + Vector3.Dot(Vector3.Forward(), xaxis));
+                this._xmesh.rotationQuaternion!.normalize();
             }
             if (this._ymesh) {
                 this._ymesh.position.copyFrom(position);
+
+                var cross = Vector3.Cross(Vector3.Forward(), yaxis);
+                this._ymesh.rotationQuaternion!.set(cross.x, cross.y, cross.z, 1 + Vector3.Dot(Vector3.Forward(), yaxis));
+                this._ymesh.rotationQuaternion!.normalize();
             }
             if (this._zmesh) {
                 this._zmesh.position.copyFrom(position);
-            }
 
-            var point2 = this._xline[1];
-            point2.x = xaxis.x * scaleLines;
-            point2.y = xaxis.y * scaleLines;
-            point2.z = xaxis.z * scaleLines;
-            Mesh.CreateLines("", this._xline, null, false, this._xmesh);
-
-            point2 = this._yline[1];
-            point2.x = yaxis.x * scaleLines;
-            point2.y = yaxis.y * scaleLines;
-            point2.z = yaxis.z * scaleLines;
-            Mesh.CreateLines("", this._yline, null, false, this._ymesh);
-
-            point2 = this._zline[1];
-            point2.x = zaxis.x * scaleLines;
-            point2.y = zaxis.y * scaleLines;
-            point2.z = zaxis.z * scaleLines;
-            Mesh.CreateLines("", this._zline, null, false, this._zmesh);
+                var cross = Vector3.Cross(Vector3.Forward(), zaxis);
+                this._zmesh.rotationQuaternion!.set(cross.x, cross.y, cross.z, 1 + Vector3.Dot(Vector3.Forward(), zaxis));
+                this._zmesh.rotationQuaternion!.normalize();
+            }
 
         }
 

+ 29 - 15
src/Debug/physicsViewer.ts

@@ -26,6 +26,7 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
         /** @hidden */
         protected _physicsEnginePlugin: Nullable<IPhysicsEnginePlugin>;
         private _renderFunction: () => void;
+        private _utilityLayer: Nullable<UtilityLayerRenderer>;
 
         private _debugBoxMesh: Mesh;
         private _debugSphereMesh: Mesh;
@@ -42,11 +43,14 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
             if (physicEngine) {
                 this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
             }
+
+            this._utilityLayer = new UtilityLayerRenderer(this._scene, false);
+            this._utilityLayer.pickUtilitySceneFirst = false;
+            this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
         }
 
         /** @hidden */
         protected _updateDebugMeshes(): void {
-
             var plugin = this._physicsEnginePlugin;
 
             for (var i = 0; i < this._numMeshes; i++) {
@@ -66,26 +70,26 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
                     }
                 }
             }
-
         }
 
         /**
          * Renders a specified physic impostor
          * @param impostor defines the impostor to render
+         * @returns the new debug mesh used to render the impostor
          */
-        public showImpostor(impostor: PhysicsImpostor): void {
+        public showImpostor(impostor: PhysicsImpostor): Nullable<AbstractMesh> {
 
             if (!this._scene) {
-                return;
+                return null;
             }
 
             for (var i = 0; i < this._numMeshes; i++) {
                 if (this._impostors[i] == impostor) {
-                    return;
+                    return null;
                 }
             }
 
-            var debugMesh = this._getDebugMesh(impostor, this._scene);
+            var debugMesh = this._getDebugMesh(impostor);
 
             if (debugMesh) {
                 this._impostors[this._numMeshes] = impostor;
@@ -99,6 +103,7 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
                 this._numMeshes++;
             }
 
+            return debugMesh;
         }
 
         /**
@@ -107,11 +112,12 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
          */
         public hideImpostor(impostor: Nullable<PhysicsImpostor>) {
 
-            if (!impostor || !this._scene) {
+            if (!impostor || !this._scene || !this._utilityLayer) {
                 return;
             }
 
             var removed = false;
+            const utilityLayerScene = this._utilityLayer.utilityLayerScene;
 
             for (var i = 0; i < this._numMeshes; i++) {
                 if (this._impostors[i] == impostor) {
@@ -121,7 +127,7 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
                         continue;
                     }
 
-                    this._scene.removeMesh(mesh);
+                    utilityLayerScene.removeMesh(mesh);
                     mesh.dispose();
                     this._numMeshes--;
                     if (this._numMeshes > 0) {
@@ -148,6 +154,8 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
             if (!this._debugMaterial) {
                 this._debugMaterial = new StandardMaterial('', scene);
                 this._debugMaterial.wireframe = true;
+                this._debugMaterial.emissiveColor = Color3.White();
+                this._debugMaterial.disableLighting = true;
             }
 
             return this._debugMaterial;
@@ -156,10 +164,8 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
         private _getDebugBoxMesh(scene: Scene): AbstractMesh {
             if (!this._debugBoxMesh) {
                 this._debugBoxMesh = MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
-                this._debugBoxMesh.renderingGroupId = 1;
                 this._debugBoxMesh.rotationQuaternion = Quaternion.Identity();
                 this._debugBoxMesh.material = this._getDebugMaterial(scene);
-                scene.removeMesh(this._debugBoxMesh);
             }
 
             return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
@@ -168,23 +174,26 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
         private _getDebugSphereMesh(scene: Scene): AbstractMesh {
             if (!this._debugSphereMesh) {
                 this._debugSphereMesh = MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
-                this._debugSphereMesh.renderingGroupId = 1;
                 this._debugSphereMesh.rotationQuaternion = Quaternion.Identity();
                 this._debugSphereMesh.material = this._getDebugMaterial(scene);
-                scene.removeMesh(this._debugSphereMesh);
             }
 
             return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
         }
 
-        private _getDebugMesh(impostor: PhysicsImpostor, scene: Scene): Nullable<AbstractMesh> {
+        private _getDebugMesh(impostor: PhysicsImpostor): Nullable<AbstractMesh> {
+            if (!this._utilityLayer) {
+                return null;
+            }
+
             var mesh: Nullable<AbstractMesh> = null;
+            const utilityLayerScene = this._utilityLayer.utilityLayerScene;
 
             if (impostor.type == PhysicsImpostor.BoxImpostor) {
-                mesh = this._getDebugBoxMesh(scene);
+                mesh = this._getDebugBoxMesh(utilityLayerScene);
                 impostor.getBoxSizeToRef(mesh.scaling);
             } else if (impostor.type == PhysicsImpostor.SphereImpostor) {
-                mesh = this._getDebugSphereMesh(scene);
+                mesh = this._getDebugSphereMesh(utilityLayerScene);
                 var radius = impostor.getRadius();
                 mesh.scaling.x = radius * 2;
                 mesh.scaling.y = radius * 2;
@@ -213,5 +222,10 @@ import { PhysicsImpostor } from "Physics/physicsImpostor";
             this._impostors.length = 0;
             this._scene = null;
             this._physicsEnginePlugin = null;
+
+            if (this._utilityLayer) {
+                this._utilityLayer.dispose();
+                this._utilityLayer = null;
+            }
         }
     }

+ 18 - 3
src/Debug/skeletonViewer.ts

@@ -21,6 +21,7 @@ import { UtilityLayerRenderer } from "Rendering/utilityLayerRenderer";
         private _debugMesh: Nullable<LinesMesh>;
         private _isEnabled = false;
         private _renderFunction: () => void;
+        private _utilityLayer: Nullable<UtilityLayerRenderer>;
 
         /**
          * Returns the mesh used to render the bones
@@ -36,7 +37,6 @@ import { UtilityLayerRenderer } from "Rendering/utilityLayerRenderer";
          * @param scene defines the hosting scene
          * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
          * @param renderingGroupId defines the rendering group id to use with the viewer
-         * @param utilityLayerRenderer defines an optional utility layer to render the helper on
          */
         constructor(
             /** defines the skeleton to render */
@@ -49,10 +49,13 @@ import { UtilityLayerRenderer } from "Rendering/utilityLayerRenderer";
             /** defines the rendering group id to use with the viewer */
             public renderingGroupId = 1,
             /** defines an optional utility layer to render the helper on */
-            public utilityLayerRenderer?: UtilityLayerRenderer
         ) {
             this._scene = scene;
 
+            this._utilityLayer = new UtilityLayerRenderer(this._scene, false);
+            this._utilityLayer.pickUtilitySceneFirst = false;
+            this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
+
             this.update();
 
             this._renderFunction = this.update.bind(this);
@@ -142,6 +145,10 @@ import { UtilityLayerRenderer } from "Rendering/utilityLayerRenderer";
 
         /** Update the viewer to sync with current skeleton state */
         public update() {
+            if (!this._utilityLayer) {
+                return;
+            }
+
             if (this.autoUpdateBonesMatrices) {
                 this.skeleton.computeAbsoluteTransforms();
             }
@@ -151,7 +158,7 @@ import { UtilityLayerRenderer } from "Rendering/utilityLayerRenderer";
             } else {
                 this._getLinesForBonesWithLength(this.skeleton.bones, this.mesh.getWorldMatrix());
             }
-            const targetScene = this.utilityLayerRenderer ? this.utilityLayerRenderer.utilityLayerScene : this._scene;
+            const targetScene = this._utilityLayer.utilityLayerScene;
 
             if (!this._debugMesh) {
                 this._debugMesh = MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
@@ -165,10 +172,18 @@ import { UtilityLayerRenderer } from "Rendering/utilityLayerRenderer";
 
         /** Release associated resources */
         public dispose() {
+
+            this.isEnabled = false;
+
             if (this._debugMesh) {
                 this.isEnabled = false;
                 this._debugMesh.dispose();
                 this._debugMesh = null;
             }
+
+            if (this._utilityLayer) {
+                this._utilityLayer.dispose();
+                this._utilityLayer = null;
+            }
         }
     }

+ 2 - 2
src/Engines/engine.ts

@@ -516,7 +516,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "4.0.0-alpha.7";
+            return "4.0.0-alpha.8";
         }
 
         /**
@@ -4365,7 +4365,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
                     loader!.loadData(data as ArrayBuffer, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed) => {
                         if (loadFailed) {
                             onInternalError("TextureLoader failed to load data");
-                        }else {
+                        } else {
                             this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, () => {
                                 done();
                                 return false;

+ 23 - 14
src/Gizmos/axisDragGizmo.ts

@@ -12,6 +12,7 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "States";
 import { Gizmo } from "./gizmo";
 import { UtilityLayerRenderer } from "Rendering/utilityLayerRenderer";
 import { StandardMaterial } from "Materials/standardMaterial";
+import { Scene } from "scene";
     /**
      * Single axis drag gizmo
      */
@@ -30,6 +31,27 @@ import { StandardMaterial } from "Materials/standardMaterial";
          * * snapDistance is the the change in distance
          */
         public onSnapObservable = new Observable<{snapDistance: number}>();
+
+        /** @hidden */
+        public static _CreateArrow(scene: Scene, material: StandardMaterial) {
+            var arrow = new AbstractMesh("", scene);
+            var arrowMesh = MeshBuilder.CreateCylinder("yPosMesh", {diameterTop: 0, height: 1.5, diameterBottom: 0.75, tessellation: 96}, scene);
+            var arrowTail = MeshBuilder.CreateLines("yPosMesh", {points: [new Vector3(0, 0, 0), new Vector3(0, 1.1, 0)]}, scene);
+            arrowTail.color = material.emissiveColor;
+            arrow.addChild(arrowMesh);
+            arrow.addChild(arrowTail);
+
+            // Position arrow pointing in its drag axis
+            arrowMesh.scaling.scaleInPlace(0.05);
+            arrowMesh.material = material;
+            arrowMesh.rotation.x = Math.PI / 2;
+            arrowMesh.position.z += 0.3;
+            arrowTail.scaling.scaleInPlace(0.26);
+            arrowTail.rotation.x = Math.PI / 2;
+            arrowTail.material = material;
+            return arrow;
+        }
+
         /**
          * Creates an AxisDragGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -49,21 +71,8 @@ import { StandardMaterial } from "Materials/standardMaterial";
             hoverMaterial.emissiveColor = color.add(new Color3(0.3, 0.3, 0.3));
 
             // Build mesh on root node
-            var arrow = new AbstractMesh("", gizmoLayer.utilityLayerScene);
-            var arrowMesh = MeshBuilder.CreateCylinder("yPosMesh", {diameterTop: 0, height: 1.5, diameterBottom: 0.75, tessellation: 96}, gizmoLayer.utilityLayerScene);
-            var arrowTail = MeshBuilder.CreateLines("yPosMesh", {points: [new Vector3(0, 0, 0), new Vector3(0, 1.1, 0)]}, gizmoLayer.utilityLayerScene);
-            arrowTail.color = coloredMaterial.emissiveColor;
-            arrow.addChild(arrowMesh);
-            arrow.addChild(arrowTail);
+            var arrow = AxisDragGizmo._CreateArrow(gizmoLayer.utilityLayerScene, coloredMaterial);
 
-            // Position arrow pointing in its drag axis
-            arrowMesh.scaling.scaleInPlace(0.05);
-            arrowMesh.material = coloredMaterial;
-            arrowMesh.rotation.x = Math.PI / 2;
-            arrowMesh.position.z += 0.3;
-            arrowTail.scaling.scaleInPlace(0.26);
-            arrowTail.rotation.x = Math.PI / 2;
-            arrowTail.material = coloredMaterial;
             arrow.lookAt(this._rootMesh.position.subtract(dragAxis));
             arrow.scaling.scaleInPlace(1 / 3);
 

+ 12 - 2
src/Gizmos/gizmoManager.ts

@@ -24,6 +24,10 @@ import { BoundingBoxGizmo } from "./boundingBoxGizmo";
          * Gizmo's created by the gizmo manager, gizmo will be null until gizmo has been enabled for the first time
          */
         public gizmos: {positionGizmo: Nullable<PositionGizmo>, rotationGizmo: Nullable<RotationGizmo>, scaleGizmo: Nullable<ScaleGizmo>, boundingBoxGizmo: Nullable<BoundingBoxGizmo>};
+        /** When true, the gizmo will be detached from the current object when a pointer down occurs with an empty picked mesh */
+        public clearGizmoOnEmptyPointerEvent = false;
+        /** Fires an event when the manager is attached to a mesh */
+        public onAttachedToMeshObservable = new Observable<Nullable<AbstractMesh>>();
         private _gizmosEnabled = {positionGizmo: false, rotationGizmo: false, scaleGizmo: false, boundingBoxGizmo: false};
         private _pointerObserver: Nullable<Observer<PointerInfo>> = null;
         private _attachedMesh: Nullable<AbstractMesh> = null;
@@ -85,10 +89,14 @@ import { BoundingBoxGizmo } from "./boundingBoxGizmo";
                                 this.attachToMesh(node);
                             }
                         } else {
-                            this.attachToMesh(null);
+                            if (this.clearGizmoOnEmptyPointerEvent) {
+                                this.attachToMesh(null);
+                            }
                         }
                     }else {
-                        this.attachToMesh(null);
+                        if (this.clearGizmoOnEmptyPointerEvent) {
+                            this.attachToMesh(null);
+                        }
                     }
                 }
             });
@@ -112,6 +120,7 @@ import { BoundingBoxGizmo } from "./boundingBoxGizmo";
             if (this.boundingBoxGizmoEnabled && this._attachedMesh) {
                 this._attachedMesh.addBehavior(this.boundingBoxDragBehavior);
             }
+            this.onAttachedToMeshObservable.notifyObservers(mesh);
         }
 
         /**
@@ -197,5 +206,6 @@ import { BoundingBoxGizmo } from "./boundingBoxGizmo";
             this._defaultKeepDepthUtilityLayer.dispose();
             this._defaultUtilityLayer.dispose();
             this.boundingBoxDragBehavior.detach();
+            this.onAttachedToMeshObservable.clear();
         }
     }

+ 0 - 0
src/Loading/Plugins/babylonFileLoader.ts


部分文件因为文件数量过多而无法显示