Selaa lähdekoodia

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into ammoPlugin

Trevor Baron 6 vuotta sitten
vanhempi
commit
88d70a0ca6
100 muutettua tiedostoa jossa 24075 lisäystä ja 57972 poistoa
  1. 10353 10253
      Playground/babylon.d.txt
  2. 10369 10269
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 533 238
      dist/preview release/babylon.max.js
  5. 533 238
      dist/preview release/babylon.no-module.max.js
  6. 1 1
      dist/preview release/babylon.worker.js
  7. 533 238
      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. 12 36091
      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. 41 49
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  18. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  19. 41 49
      dist/preview release/loaders/babylon.glTFFileLoader.js
  20. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  21. 41 49
      dist/preview release/loaders/babylonjs.loaders.js
  22. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  23. 3 3
      dist/preview release/loaders/package.json
  24. 2 2
      dist/preview release/materialsLibrary/package.json
  25. 2 2
      dist/preview release/postProcessesLibrary/package.json
  26. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  27. 3 3
      dist/preview release/serializers/package.json
  28. 1 1
      dist/preview release/viewer/babylon.viewer.js
  29. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  30. 13 0
      dist/preview release/what's new.md
  31. 1 1
      gui/src/2D/controls/checkbox.ts
  32. 1 1
      gui/src/2D/controls/container.ts
  33. 2 0
      gui/src/2D/controls/control.ts
  34. 2 2
      inspector/package.json
  35. 25 5
      inspector/src/components/actionTabs/actionTabs.scss
  36. 12 1
      inspector/src/components/actionTabs/lineContainerComponent.tsx
  37. 2 2
      inspector/src/components/actionTabs/lines/color3LineComponent.tsx
  38. 5 2
      inspector/src/components/actionTabs/lines/fileButtonLineComponent.tsx
  39. 38 8
      inspector/src/components/actionTabs/lines/floatLineComponent.tsx
  40. 16 0
      inspector/src/components/actionTabs/lines/messageLineComponent.tsx
  41. 1 3
      inspector/src/components/actionTabs/lines/optionsLineComponent.tsx
  42. 1 0
      inspector/src/components/actionTabs/lines/radioLineComponent.tsx
  43. 41 13
      inspector/src/components/actionTabs/lines/sliderLineComponent.tsx
  44. 25 8
      inspector/src/components/actionTabs/lines/textInputLineComponent.tsx
  45. 7 3
      inspector/src/components/actionTabs/lines/textureLineComponent.tsx
  46. 12 12
      inspector/src/components/actionTabs/lines/textureLinkLineComponent.tsx
  47. 10 2
      inspector/src/components/actionTabs/lines/vector3LineComponent.tsx
  48. 38 8
      inspector/src/components/actionTabs/tabs/debugTabComponent.tsx
  49. 128 11
      inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx
  50. 140 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animationGroupPropertyGridComponent.tsx
  51. 16 24
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/arcRotateCameraPropertyGridComponent.tsx
  52. 8 6
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/commonCameraPropertyGridComponent.tsx
  53. 5 13
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx
  54. 0 102
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/propertyGridTabComponent.tsx
  55. 5 3
      inspector/src/components/actionTabs/tabs/propertyGrids/fogPropertyGridComponent.tsx
  56. 4 4
      inspector/src/components/actionTabs/tabs/propertyGrids/gridPropertyGridComponent.tsx
  57. 35 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/checkboxPropertyGridComponent.tsx
  58. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/colorPickerPropertyGridComponent.tsx
  59. 43 18
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent.tsx
  60. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/controlPropertyGridComponent.tsx
  61. 33 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/ellipsePropertyGridComponent.tsx
  62. 42 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/imageBasedSliderPropertyGridComponent.tsx
  63. 52 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/imagePropertyGridComponent.tsx
  64. 13 11
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/inputTextPropertyGridComponent.tsx
  65. 55 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/linePropertyGridComponent.tsx
  66. 38 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/radioButtonPropertyGridComponent.tsx
  67. 34 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/rectanglePropertyGridComponent.tsx
  68. 44 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/sliderPropertyGridComponent.tsx
  69. 19 2
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/textBlockPropertyGridComponent.tsx
  70. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonLightPropertyGridComponent.tsx
  71. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx
  72. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx
  73. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/hemisphericLightPropertyGridComponent.tsx
  74. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/pointLightPropertyGridComponent.tsx
  75. 6 4
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/spotLightPropertyGridComponent.tsx
  76. 9 0
      inspector/src/components/actionTabs/tabs/propertyGrids/lockObject.ts
  77. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/backgroundMaterialPropertyGridComponent.tsx
  78. 3 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/commonMaterialPropertyGridComponent.tsx
  79. 3 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/materialPropertyGridComponent.tsx
  80. 5 3
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx
  81. 41 9
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/standardMaterialPropertyGridComponent.tsx
  82. 54 15
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  83. 66 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/axesViewerComponent.tsx
  84. 53 14
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx
  85. 6 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/transformNodePropertyGridComponent.tsx
  86. 60 7
      inspector/src/components/actionTabs/tabs/propertyGrids/scenePropertyGridComponent.tsx
  87. 15 7
      inspector/src/components/actionTabs/tabs/tools/gltfComponent.tsx
  88. 30 0
      inspector/src/components/sceneExplorer/entities/animationGroupTreeItemComponent.tsx
  89. 7 3
      inspector/src/components/sceneExplorer/entities/cameraTreeItemComponent.tsx
  90. 24 24
      inspector/src/components/sceneExplorer/entities/meshTreeItemComponent.tsx
  91. 10 10
      inspector/src/components/sceneExplorer/entities/sceneTreeItemComponent.tsx
  92. 2 2
      inspector/src/components/sceneExplorer/entities/transformNodeTreeItemComponent.tsx
  93. 51 11
      inspector/src/components/sceneExplorer/sceneExplorer.scss
  94. 52 7
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  95. 39 16
      inspector/src/components/sceneExplorer/treeItemComponent.tsx
  96. 7 6
      inspector/src/components/sceneExplorer/treeItemSelectableComponent.tsx
  97. 6 1
      inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx
  98. 3 3
      inspector/src/tools.ts
  99. 41 46
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  100. 0 0
      package.json

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 10353 - 10253
Playground/babylon.d.txt


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 10369 - 10269
dist/preview release/babylon.d.ts


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/babylon.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 533 - 238
dist/preview release/babylon.max.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 533 - 238
dist/preview release/babylon.no-module.max.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/babylon.worker.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 533 - 238
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"

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/gui/babylon.gui.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 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": "*"

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 12 - 36091
dist/preview release/inspector/babylon.inspector.bundle.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 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
 

+ 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 () {

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 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 () {

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 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 () {

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 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": "*"

+ 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": "*"

+ 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": "*"

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 2 - 2
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -66,11 +66,20 @@
 - 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))
+- Factored out `setDirection` function from `lookAt` for 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
 
@@ -79,6 +88,8 @@
 ### Materials Library
 
 ## Bug fixes
+- Fixed anaglyph mode for Free and Universal cameras ([Deltakosh](https://github.com/deltakosh))
+- 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))
@@ -90,6 +101,7 @@
 - VR helper would rotate non vr camera while in VR ([TrevorDev](https://github.com/TrevorDev))
 - PointerDragBahavior using Mesh as base type, causing type-checking problems with AbstractMesh ([Poolminer](https://github.com/Poolminer/))
 - TransformNode lookAt not working in world space when node's parent has rotation ([TrevorDev](https://github.com/TrevorDev))
+- MakeNotPickableAndWrapInBoundingBox had unexpected behavior when input had scaling of 0 on an axis ([TrevorDev](https://github.com/TrevorDev))
 
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
@@ -102,6 +114,7 @@
 - Fixed effect layer compatibility with multi materials ([Sebavan](https://github.com/Sebavan))
 - Added a `DeepImmutable<T>` type to specifiy that a referenced object should be considered recursively immutable, meaning that all its properties are `readonly` and that if a property is a reference to an object, this object is also recursively immutable. ([barroij](https://github.com/barroij))
 - Fixed `VideoTexture` poster property when autoplay is turned off.
+- Fixed position and rotation of plane mesh created by MeshBuilder.CreatePlane when specifying a source plane ([sable](https://github.com/thscott), [bghgary](https://github.com/bghgary))
 
 ### Viewer
 

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

@@ -88,7 +88,7 @@ export class Checkbox extends Control {
     }
 
     protected _getTypeName(): string {
-        return "CheckBox";
+        return "Checkbox";
     }
 
     /** @hidden */

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

+ 2 - 2
inspector/package.json

@@ -39,8 +39,8 @@
         "mini-css-extract-plugin": "~0.4.4",
         "node-sass": "~4.10.0",
         "re-resizable": "~4.9.1",
-        "react": "~16.5.2",
-        "react-dom": "~16.5.2",
+        "react": "~16.6.1",
+        "react-dom": "~16.6.1",
         "sass-loader": "~7.1.0",
         "split.js": "^1.5.9",
         "style-loader": "~0.23.1",

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

@@ -171,6 +171,26 @@
                     opacity: 0.6;
                 }
 
+                .iconMessageLine {
+                    padding-left: 5px;
+                    height: 30px;
+                    display: grid;
+                    grid-template-columns: 30px 1fr;
+
+                    .icon {
+                        grid-column: 1;
+                        display: grid;
+                        align-items: center;
+                        justify-items: center;
+                    }
+
+                    .value {
+                        grid-column: 2;
+                        display: flex;
+                        align-items: center;
+                    }
+                }
+
                 .textLine {
                     padding-left: 5px;
                     height: 30px;
@@ -273,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 {
@@ -290,7 +309,7 @@
                     }   
                     
                     button:focus {
-                        border: 0px;
+                        border: 1px solid rgb(51, 122, 183);
                         outline: 0px;
                     }  
                 }
@@ -545,7 +564,7 @@
                     }                    
 
                     .command {
-                        border: 0px;
+                        border: 1px solid transparent;
                         background:transparent;
                         color: white;
                     }
@@ -563,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>
         );
     }

+ 38 - 8
inspector/src/components/actionTabs/lines/floatLineComponent.tsx

@@ -1,12 +1,15 @@
 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,
+    isInteger?: boolean,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>,
     additionalClass?: string
 }
@@ -19,10 +22,15 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
         super(props);
 
         let currentValue = this.props.target[this.props.propertyName];
-        this.state = { value: currentValue ? currentValue.toFixed(3) : "0" }
+        this.state = { value: currentValue ? (this.props.isInteger ? currentValue.toFixed(0) : currentValue.toFixed(3)) : "0" }
         this._store = currentValue;
     }
 
+
+    componentWillUnmount() {
+        this.unlock();
+    }
+
     shouldComponentUpdate(nextProps: IFloatLineComponentProps, nextState: { value: string }) {
         if (this._localChange) {
             this._localChange = false;
@@ -30,14 +38,20 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
         }
 
         const newValue = nextProps.target[nextProps.propertyName];
-        if (newValue && newValue !== nextState.value) {
-            nextState.value = newValue.toFixed(3);
+        const newValueString = newValue ? this.props.isInteger ? newValue.toFixed(0) : newValue.toFixed(3) : "0";
+
+        if (newValueString !== nextState.value) {
+            nextState.value = newValueString;
             return true;
         }
         return false;
     }
 
     raiseOnPropertyChanged(newValue: number, previousValue: number) {
+        if (this.props.onChange) {
+            this.props.onChange(newValue);
+        }
+
         if (!this.props.onPropertyChangedObservable) {
             return;
         }
@@ -55,7 +69,13 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
             return;
         }
 
-        let valueAsNumber = parseFloat(valueString);
+        let valueAsNumber: number
+
+        if (this.props.isInteger) {
+            valueAsNumber = parseInt(valueString);
+        } else {
+            valueAsNumber = parseFloat(valueString);
+        }
 
         this._localChange = true;
         this.setState({ value: valueString });
@@ -70,16 +90,26 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
         this._store = valueAsNumber;
     }
 
-    render() {
+    lock() {
+        if (this.props.lockObject) {
+            this.props.lockObject.lock = true;
+        }
+    }
 
-        const step = this.props.step !== undefined ? this.props.step : 0.1;
+    unlock() {
+        if (this.props.lockObject) {
+            this.props.lockObject.lock = false;
+        }
+    }
+
+    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>
         );

+ 16 - 0
inspector/src/components/actionTabs/lines/messageLineComponent.tsx

@@ -1,8 +1,11 @@
 import * as React from "react";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 
 interface IMessageLineComponentProps {
     text: string,
     color?: string,
+    icon?: IconProp
 }
 
 export class MessageLineComponent extends React.Component<IMessageLineComponentProps> {
@@ -11,6 +14,19 @@ export class MessageLineComponent extends React.Component<IMessageLineComponentP
     }
 
     render() {
+        if (this.props.icon) {
+            return (
+                <div className="iconMessageLine">
+                    <div className="icon" style={{ color: this.props.color ? this.props.color : "" }}>
+                        <FontAwesomeIcon icon={this.props.icon}/>
+                    </div>
+                    <div className="value" title={this.props.text}>
+                        {this.props.text}
+                    </div>
+                </div>
+            );
+        }
+
         return (
             <div className="messageLine">
                 <div className="value" title={this.props.text} style={{ color: this.props.color ? this.props.color : "" }}>

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

+ 25 - 8
inspector/src/components/actionTabs/lines/textInputLineComponent.tsx

@@ -1,11 +1,15 @@
 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,
-    propertyName: string,
+    lockObject: LockObject,
+    target?: any,
+    propertyName?: string,
+    value?: string,
+    onChange?: (value: string) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -15,7 +19,11 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
     constructor(props: ITextInputLineComponentProps) {
         super(props);
 
-        this.state = { value: this.props.target[this.props.propertyName] || "" }
+        this.state = { value: this.props.value || this.props.target[this.props.propertyName!] || "" }
+    }
+
+    componentWillUnmount() {
+        this.props.lockObject.lock = false;
     }
 
     shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: { value: string }) {
@@ -24,7 +32,7 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
             return true;
         }
 
-        const newValue = nextProps.target[nextProps.propertyName];
+        const newValue = nextProps.value || nextProps.target[nextProps.propertyName!];
         if (newValue !== nextState.value) {
             nextState.value = newValue || "";
             return true;
@@ -33,12 +41,18 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
     }
 
     raiseOnPropertyChanged(newValue: string, previousValue: string) {
+        if (this.props.onChange) {
+            this.props.onChange(newValue);
+            return;
+        }
+
         if (!this.props.onPropertyChangedObservable) {
             return;
         }
+
         this.props.onPropertyChangedObservable.notifyObservers({
             object: this.props.target,
-            property: this.props.propertyName,
+            property: this.props.propertyName!,
             value: newValue,
             initialValue: previousValue
         });
@@ -47,11 +61,14 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
     updateValue(value: string) {
 
         this._localChange = true;
-        const store = this.props.target[this.props.propertyName];
+        const store = this.props.value || this.props.target[this.props.propertyName!];
         this.setState({ value: value });
 
         this.raiseOnPropertyChanged(value, store);
-        this.props.target[this.props.propertyName] = value;
+
+        if (this.props.propertyName) {
+            this.props.target[this.props.propertyName] = value;
+        }
     }
 
     render() {
@@ -61,7 +78,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>
         );

+ 7 - 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
 }
@@ -20,6 +20,10 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
         }
     }
 
+    shouldComponentUpdate(nextProps: ITextureLineComponentProps): boolean {
+        return (nextProps.texture !== this.props.texture);
+    }
+
     componentDidMount() {
         this.updatePreview();
     }
@@ -108,7 +112,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;

+ 12 - 12
inspector/src/components/actionTabs/lines/textureLinkLineComponent.tsx

@@ -21,7 +21,7 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
         const material = this.props.material;
         const texture = this.props.texture;
 
-        this.state = { isDebugSelected: material && material.metadata && material.metadata.debugTexture === texture };
+        this.state = { isDebugSelected: material && material.reservedDataStore && material.reservedDataStore.debugTexture === texture };
     }
 
 
@@ -51,8 +51,8 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
         }
         const scene = material.getScene();
 
-        if (material.metadata && material.metadata.debugTexture === texture) {
-            const debugMaterial = material.metadata.debugMaterial;
+        if (material.reservedDataStore && material.reservedDataStore.debugTexture === texture) {
+            const debugMaterial = material.reservedDataStore.debugMaterial;
 
             for (var mesh of scene.meshes) {
                 if (mesh.material === debugMaterial) {
@@ -60,8 +60,8 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
                 }
             }
             debugMaterial.dispose();
-            material.metadata.debugTexture = null;
-            material.metadata.debugMaterial = null;
+            material.reservedDataStore.debugTexture = null;
+            material.reservedDataStore.debugMaterial = null;
 
             this.setState({ isDebugSelected: false });
             return;
@@ -69,8 +69,8 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
 
         let checkMaterial = material;
         let needToDisposeCheckMaterial = false;
-        if (material.metadata && material.metadata.debugTexture) {
-            checkMaterial = material.metadata.debugMaterial;
+        if (material.reservedDataStore && material.reservedDataStore.debugTexture) {
+            checkMaterial = material.reservedDataStore.debugMaterial;
             needToDisposeCheckMaterial = true;
         }
 
@@ -79,7 +79,7 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
         debugMaterial.sideOrientation = material.sideOrientation;
         debugMaterial.emissiveTexture = texture!;
         debugMaterial.forceDepthWrite = true;
-        debugMaterial.metadata = { hidden: true };
+        debugMaterial.reservedDataStore = { hidden: true };
 
         for (var mesh of scene.meshes) {
             if (mesh.material === checkMaterial) {
@@ -87,12 +87,12 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
             }
         }
 
-        if (!material.metadata) {
-            material.metadata = {};
+        if (!material.reservedDataStore) {
+            material.reservedDataStore = {};
         }
 
-        material.metadata.debugTexture = texture;
-        material.metadata.debugMaterial = debugMaterial;
+        material.reservedDataStore.debugTexture = texture;
+        material.reservedDataStore.debugMaterial = debugMaterial;
 
         if (this.props.onDebugSelectionChangeObservable) {
             this.props.onDebugSelectionChangeObservable.notifyObservers(texture!);

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

+ 38 - 8
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,13 +20,18 @@ export class DebugTabComponent extends PaneComponent {
             return;
         }
 
+        if (!scene.reservedDataStore) {
+            scene.reservedDataStore = {};
+        }
+
         for (var mesh of scene.meshes) {
-            if (mesh.skeleton && mesh.metadata && mesh.metadata.skeletonViewer) {
-                this._skeletonViewers.push(mesh.metadata.skeletonViewer);
+            if (mesh.skeleton && mesh.reservedDataStore && mesh.reservedDataStore.skeletonViewer) {
+                this._skeletonViewers.push(mesh.reservedDataStore.skeletonViewer);
             }
         }
 
         this._skeletonViewersEnabled = (this._skeletonViewers.length > 0);
+        this._physicsViewersEnabled = scene.reservedDataStore.physicsViewer != null;
     }
 
     componentWillUnmount() {
@@ -49,18 +54,18 @@ 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) {
-                        mesh.metadata = {};
+                    if (!mesh.reservedDataStore) {
+                        mesh.reservedDataStore = {};
                     }
-                    mesh.metadata.skeletonViewer = viewer;
+                    mesh.reservedDataStore.skeletonViewer = viewer;
                 }
             }
         } else {
             for (var index = 0; index < this._skeletonViewers.length; index++) {
-                this._skeletonViewers[index].mesh.metadata.skeletonViewer = null;
+                this._skeletonViewers[index].mesh.reservedDataStore.skeletonViewer = null;
                 this._skeletonViewers[index].dispose();
             }
             this._skeletonViewers = [];
@@ -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.reservedDataStore.physicsViewer = physicsViewer;
+
+            for (var mesh of scene.meshes) {
+                if (mesh.physicsImpostor) {
+                    let debugMesh = physicsViewer.showImpostor(mesh.physicsImpostor);
+
+                    if (debugMesh) {
+                        debugMesh.reservedDataStore = { hidden: true };
+                        debugMesh.material!.reservedDataStore = { hidden: true };
+                    }
+                }
+            }
+        } else {
+            scene.reservedDataStore.physicsViewer.dispose();
+            scene.reservedDataStore.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} />

+ 128 - 11
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";
@@ -19,14 +19,41 @@ import { TextBlockPropertyGridComponent } from "./propertyGrids/gui/textBlockPro
 import { TextBlock } from "babylonjs-gui/2D/controls/textBlock";
 import { InputText } from "babylonjs-gui/2D/controls/inputText";
 import { InputTextPropertyGridComponent } from "./propertyGrids/gui/inputTextPropertyGridComponent";
-import { ColorPicker } from "babylonjs-gui";
+import { ColorPicker, Image, Slider, ImageBasedSlider, Rectangle, Ellipse, Checkbox, RadioButton, Line } from "babylonjs-gui";
 import { ColorPickerPropertyGridComponent } from "./propertyGrids/gui/colorPickerPropertyGridComponent";
+import { AnimationGroupGridComponent } from "./propertyGrids/animationGroupPropertyGridComponent";
+import { LockObject } from "./propertyGrids/lockObject";
+import { ImagePropertyGridComponent } from "./propertyGrids/gui/imagePropertyGridComponent";
+import { SliderPropertyGridComponent } from "./propertyGrids/gui/sliderPropertyGridComponent";
+import { ImageBasedSliderPropertyGridComponent } from "./propertyGrids/gui/imageBasedSliderPropertyGridComponent";
+import { RectanglePropertyGridComponent } from "./propertyGrids/gui/rectanglePropertyGridComponent";
+import { EllipsePropertyGridComponent } from "./propertyGrids/gui/ellipsePropertyGridComponent";
+import { CheckboxPropertyGridComponent } from "./propertyGrids/gui/checkboxPropertyGridComponent";
+import { RadioButtonPropertyGridComponent } from "./propertyGrids/gui/radioButtonPropertyGridComponent";
+import { LinePropertyGridComponent } from "./propertyGrids/gui/linePropertyGridComponent";
 
 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 +72,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 +80,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 +104,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 +128,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 +137,121 @@ 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 (className === "Image") {
+                const image = entity as Image;
+                return (<ImagePropertyGridComponent image={image}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Slider") {
+                const slider = entity as Slider;
+                return (<SliderPropertyGridComponent slider={slider}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "ImageBasedSlider") {
+                const imageBasedSlider = entity as ImageBasedSlider;
+                return (<ImageBasedSliderPropertyGridComponent imageBasedSlider={imageBasedSlider}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Rectangle") {
+                const rectangle = entity as Rectangle;
+                return (<RectanglePropertyGridComponent rectangle={rectangle}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Ellipse") {
+                const ellipse = entity as Ellipse;
+                return (<EllipsePropertyGridComponent ellipse={ellipse}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Checkbox") {
+                const checkbox = entity as Checkbox;
+                return (<CheckboxPropertyGridComponent checkbox={checkbox}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "RadioButton") {
+                const radioButton = entity as RadioButton;
+                return (<RadioButtonPropertyGridComponent radioButton={radioButton}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Line") {
+                const line = entity as Line;
+                return (<LinePropertyGridComponent line={line}
+                    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>
         );

+ 4 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/gridPropertyGridComponent.tsx

@@ -18,7 +18,7 @@ export class GridPropertyGridComponent extends React.Component<IGridPropertyGrid
         const scene = BABYLON.UtilityLayerRenderer.DefaultKeepDepthUtilityLayer.utilityLayerScene;
 
         for (var mesh of scene.meshes) {
-            if (mesh.metadata && mesh.metadata.isInspectorGrid) {
+            if (mesh.reservedDataStore && mesh.reservedDataStore.isInspectorGrid) {
                 this._gridMesh = mesh;
                 this.setState({ isEnabled: true });
                 return;
@@ -43,12 +43,12 @@ export class GridPropertyGridComponent extends React.Component<IGridPropertyGrid
             var depth = (extend.max.z - extend.min.z) * 5.0;
 
             this._gridMesh = BABYLON.Mesh.CreateGround("grid", 1.0, 1.0, 1, scene);
-            if (!this._gridMesh.metadata) {
-                this._gridMesh.metadata = {};
+            if (!this._gridMesh.reservedDataStore) {
+                this._gridMesh.reservedDataStore = {};
             }
             this._gridMesh.scaling.x = Math.max(width, depth);
             this._gridMesh.scaling.z = this._gridMesh.scaling.x;
-            this._gridMesh.metadata.isInspectorGrid = true;
+            this._gridMesh.reservedDataStore.isInspectorGrid = true;
             this._gridMesh.isPickable = false;
 
             var groundMaterial = new (BABYLON as any).GridMaterial("GridMaterial", scene);

+ 35 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/checkboxPropertyGridComponent.tsx

@@ -0,0 +1,35 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Checkbox } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+
+interface ICheckboxPropertyGridComponentProps {
+    checkbox: Checkbox,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class CheckboxPropertyGridComponent extends React.Component<ICheckboxPropertyGridComponentProps> {
+    constructor(props: ICheckboxPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const checkbox = this.props.checkbox;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={checkbox} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="CHECKBOX">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Check size ratio" target={checkbox} propertyName="checkSizeRatio" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Checked" target={checkbox} propertyName="isChecked" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </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>

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

@@ -7,9 +7,12 @@ 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";
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
 
 interface ICommonControlPropertyGridComponentProps {
     control: Control,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -21,36 +24,58 @@ export class CommonControlPropertyGridComponent extends React.Component<ICommonC
     render() {
         const control = this.props.control;
 
+        var horizontalOptions = [
+            { label: "Left", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT },
+            { label: "Right", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT },
+            { label: "Center", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER },
+        ];
+
+        var verticalOptions = [
+            { label: "Top", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP },
+            { label: "Bottom", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM },
+            { label: "Center", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER },
+        ];
+
         return (
             <div>
                 <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} />
+                    {
+                        control.color &&
+                        <TextInputLineComponent lockObject={this.props.lockObject} label="Color" target={control} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        (control as any).background &&
+                        <TextInputLineComponent lockObject={this.props.lockObject} label="Background" target={control} propertyName="background" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                </LineContainerComponent>
+                <LineContainerComponent title="ALIGNMENT">
+                    <OptionsLineComponent label="Horizontal" options={horizontalOptions} target={control} propertyName="horizontalAlignment" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Vertical" options={verticalOptions} target={control} propertyName="verticalAlignment" 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>
         );
     }

+ 33 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/ellipsePropertyGridComponent.tsx

@@ -0,0 +1,33 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Ellipse } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+
+interface IEllipsePropertyGridComponentProps {
+    ellipse: Ellipse,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class EllipsePropertyGridComponent extends React.Component<IEllipsePropertyGridComponentProps> {
+    constructor(props: IEllipsePropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const ellipse = this.props.ellipse;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={ellipse} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="ELLIPSE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={ellipse} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 42 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/imageBasedSliderPropertyGridComponent.tsx

@@ -0,0 +1,42 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { LockObject } from "../lockObject";
+import { ImageBasedSlider } from "babylonjs-gui";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface IImageBasedSliderPropertyGridComponentProps {
+    imageBasedSlider: ImageBasedSlider,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class ImageBasedSliderPropertyGridComponent extends React.Component<IImageBasedSliderPropertyGridComponentProps> {
+    constructor(props: IImageBasedSliderPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const imageBasedSlider = this.props.imageBasedSlider;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={imageBasedSlider} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="IMAGE BASED SLIDER">
+                    <CheckBoxLineComponent label="Display thumb" target={imageBasedSlider} propertyName="displayThumb" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Vertical" target={imageBasedSlider} propertyName="isVertical" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Thumb clamped" target={imageBasedSlider} propertyName="isThumbClamped" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar offset" target={imageBasedSlider} propertyName="barOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Thumb width" target={imageBasedSlider} propertyName="thumbWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Minimum" target={imageBasedSlider} propertyName="minimum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Maximum" target={imageBasedSlider} propertyName="maximum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Value" target={imageBasedSlider} propertyName="value" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 52 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/imagePropertyGridComponent.tsx

@@ -0,0 +1,52 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { LockObject } from "../lockObject";
+import { Image } from "babylonjs-gui";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+
+interface IImagePropertyGridComponentProps {
+    image: Image,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class ImagePropertyGridComponent extends React.Component<IImagePropertyGridComponentProps> {
+    constructor(props: IImagePropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const image = this.props.image;
+
+        var stretchOptions = [
+            { label: "None", value: BABYLON.GUI.Image.STRETCH_NONE },
+            { label: "Fill", value: BABYLON.GUI.Image.STRETCH_FILL },
+            { label: "Uniform", value: BABYLON.GUI.Image.STRETCH_UNIFORM },
+            { label: "Extend", value: BABYLON.GUI.Image.STRETCH_EXTEND }
+        ];
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={image} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="IMAGE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source left" target={image} propertyName="sourceLeft" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source top" target={image} propertyName="sourceTop" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source width" target={image} propertyName="sourceWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source height" target={image} propertyName="sourceHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Autoscale" target={image} propertyName="autoScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Stretch" options={stretchOptions} target={image} propertyName="stretch" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => this.setState({ mode: value })} />
+                </LineContainerComponent>
+                <LineContainerComponent title="ANIMATION SHEET">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Cell Id" isInteger={true} target={image} propertyName="cellId" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Cell width" target={image} propertyName="cellWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Cell height" target={image} propertyName="cellHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </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>
         );

+ 55 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/linePropertyGridComponent.tsx

@@ -0,0 +1,55 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Line } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface ILinePropertyGridComponentProps {
+    line: Line,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class LinePropertyGridComponent extends React.Component<ILinePropertyGridComponentProps> {
+    constructor(props: ILinePropertyGridComponentProps) {
+        super(props);
+    }
+
+    onDashChange(value: string) {
+        const line = this.props.line;
+        const split = value.split(",");
+        line.dash = [];
+
+        split.forEach(v => {
+            const int = parseInt(v);
+
+            if (isNaN(int)) {
+                return;
+            }
+
+            line.dash.push(int);
+        });
+    }
+
+    render() {
+        const line = this.props.line;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={line} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="LINE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Line width" target={line} propertyName="lineWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="X1" target={line} propertyName="x1" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Y1" target={line} propertyName="y1" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="X2" target={line} propertyName="x2" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Y2" target={line} propertyName="y2" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Dash pattern" target={line} value={line.dash.join(",")} onChange={newValue => this.onDashChange(newValue)} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 38 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/radioButtonPropertyGridComponent.tsx

@@ -0,0 +1,38 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { RadioButton } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+
+interface IRadioButtonPropertyGridComponentProps {
+    radioButton: RadioButton,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class RadioButtonPropertyGridComponent extends React.Component<IRadioButtonPropertyGridComponentProps> {
+    constructor(props: IRadioButtonPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const radioButton = this.props.radioButton;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={radioButton} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="RADIO BUTTON">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={radioButton} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Check size ratio" target={radioButton} propertyName="checkSizeRatio" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Group" target={radioButton} propertyName="group" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Checked" target={radioButton} propertyName="isChecked" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 34 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/rectanglePropertyGridComponent.tsx

@@ -0,0 +1,34 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Rectangle } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+
+interface IRectanglePropertyGridComponentProps {
+    rectangle: Rectangle,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class RectanglePropertyGridComponent extends React.Component<IRectanglePropertyGridComponentProps> {
+    constructor(props: IRectanglePropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const rectangle = this.props.rectangle;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={rectangle} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="RECTANGLE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={rectangle} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Corner radius" target={rectangle} propertyName="cornerRadius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 44 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/sliderPropertyGridComponent.tsx

@@ -0,0 +1,44 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { LockObject } from "../lockObject";
+import { Slider } from "babylonjs-gui";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface ISliderPropertyGridComponentProps {
+    slider: Slider,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class SliderPropertyGridComponent extends React.Component<ISliderPropertyGridComponentProps> {
+    constructor(props: ISliderPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const slider = this.props.slider;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={slider} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="SLIDER">
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Border color" target={slider} propertyName="borderColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Display thumb" target={slider} propertyName="displayThumb" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Thumb circle" target={slider} propertyName="isThumbCircle" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Vertical" target={slider} propertyName="isVertical" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Thumb clamped" target={slider} propertyName="isThumbClamped" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar offset" target={slider} propertyName="barOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Thumb width" target={slider} propertyName="thumbWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Minimum" target={slider} propertyName="minimum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Maximum" target={slider} propertyName="maximum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Value" target={slider} propertyName="value" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

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

@@ -5,9 +5,12 @@ import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridC
 import { TextBlock } from "babylonjs-gui";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { LockObject } from "../lockObject";
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
 
 interface ITextBlockPropertyGridComponentProps {
     textBlock: TextBlock,
+    lockObject: LockObject,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -19,11 +22,25 @@ export class TextBlockPropertyGridComponent extends React.Component<ITextBlockPr
     render() {
         const textBlock = this.props.textBlock;
 
+        var horizontalOptions = [
+            { label: "Left", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT },
+            { label: "Right", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT },
+            { label: "Center", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER },
+        ];
+
+        var verticalOptions = [
+            { label: "Top", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP },
+            { label: "Bottom", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM },
+            { label: "Center", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER },
+        ];
+
         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} />
+                    <OptionsLineComponent label="Horizontal text alignment" options={horizontalOptions} target={textBlock} propertyName="textHorizontalAlignment" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Vertical text alignment" options={verticalOptions} target={textBlock} propertyName="textVerticalAlignment" 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>
         );

+ 66 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/axesViewerComponent.tsx

@@ -0,0 +1,66 @@
+import * as React from "react";
+import { TransformNode } from "babylonjs";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+
+interface IAxisViewerComponentProps {
+    node: TransformNode
+}
+
+export class AxesViewerComponent extends React.Component<IAxisViewerComponentProps, { displayAxis: boolean }> {
+    constructor(props: IAxisViewerComponentProps) {
+        super(props);
+        const node = this.props.node;
+
+        if (!node.reservedDataStore) {
+            node.reservedDataStore = {};
+        }
+
+        this.state = { displayAxis: (node.reservedDataStore && node.reservedDataStore.axisViewer) ? true : false }
+    }
+
+    displayAxes() {
+        const node = this.props.node;
+        const scene = node.getScene();
+
+        if (node.reservedDataStore.axisViewer) {
+            node.reservedDataStore.axisViewer.dispose();
+            node.reservedDataStore.axisViewer = null;
+
+            scene.onBeforeRenderObservable.remove(node.reservedDataStore.onBeforeRenderObserver);
+            node.reservedDataStore.onBeforeRenderObserver = null;
+
+            this.setState({ displayAxis: false });
+
+            return;
+        }
+
+        const viewer = new BABYLON.Debug.AxesViewer(scene);
+        node.reservedDataStore.axisViewer = viewer;
+        const x = new BABYLON.Vector3(1, 0, 0);
+        const y = new BABYLON.Vector3(0, 1, 0);
+        const z = new BABYLON.Vector3(0, 0, 1);
+
+        viewer.xAxisMesh!.reservedDataStore = { hidden: true };
+        viewer.yAxisMesh!.reservedDataStore = { hidden: true };
+        viewer.zAxisMesh!.reservedDataStore = { hidden: true };
+
+        node.reservedDataStore.onBeforeRenderObserver = scene.onBeforeRenderObservable.add(() => {
+            let matrix = node.getWorldMatrix();
+            let extend = BABYLON.Tmp.Vector3[0];
+            const worldExtend = scene.getWorldExtends();
+            worldExtend.max.subtractToRef(worldExtend.min, extend);
+            extend.scaleInPlace(0.5 * 0.5);
+
+            viewer.scaleLines = Math.max(extend.x, extend.y, extend.z) * 2;
+            viewer.update(node.getAbsolutePosition(), BABYLON.Vector3.TransformNormal(x, matrix), BABYLON.Vector3.TransformNormal(y, matrix), BABYLON.Vector3.TransformNormal(z, matrix));
+        });
+
+        this.setState({ displayAxis: true });
+    }
+
+    render() {
+        return (
+            <CheckBoxLineComponent label="Display axes" isSelected={() => this.state.displayAxis} onSelect={() => this.displayAxes()} />
+        )
+    }
+}

+ 53 - 14
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx

@@ -7,9 +7,13 @@ import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 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>
 }
@@ -19,16 +23,16 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         super(props);
         const mesh = this.props.mesh;
 
-        this.state = { displayNormals: false, renderNormalVectors: mesh.metadata && mesh.metadata.normalLines }
+        this.state = { displayNormals: false, renderNormalVectors: mesh.reservedDataStore && mesh.reservedDataStore.normalLines }
     }
 
     renderNormalVectors() {
         const mesh = this.props.mesh;
         const scene = mesh.getScene();
 
-        if (mesh.metadata && mesh.metadata.normalLines) {
-            mesh.metadata.normalLines.dispose();
-            mesh.metadata.normalLines = null;
+        if (mesh.reservedDataStore && mesh.reservedDataStore.normalLines) {
+            mesh.reservedDataStore.normalLines.dispose();
+            mesh.reservedDataStore.normalLines = null;
 
             this.setState({ renderNormalVectors: false });
             return;
@@ -51,11 +55,11 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         normalLines.color = color;
         normalLines.parent = mesh;
 
-        if (!mesh.metadata) {
-            mesh.metadata = {};
+        if (!mesh.reservedDataStore) {
+            mesh.reservedDataStore = {};
         }
 
-        mesh.metadata.normalLines = normalLines;
+        mesh.reservedDataStore.normalLines = normalLines;
 
         this.setState({ renderNormalVectors: true });
     }
@@ -70,8 +74,8 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         if (mesh.material.getClassName() === "NormalMaterial") {
             mesh.material.dispose();
 
-            mesh.material = mesh.metadata.originalMaterial;
-            mesh.metadata.originalMaterial = null;
+            mesh.material = mesh.reservedDataStore.originalMaterial;
+            mesh.reservedDataStore.originalMaterial = null;
             this.setState({ displayNormals: false });
         } else {
 
@@ -83,15 +87,15 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
                 return;
             }
 
-            if (!mesh.metadata) {
-                mesh.metadata = {};
+            if (!mesh.reservedDataStore) {
+                mesh.reservedDataStore = {};
             }
 
-            mesh.metadata.originalMaterial = mesh.material;
+            mesh.reservedDataStore.originalMaterial = mesh.material;
             const normalMaterial = new (BABYLON as any).NormalMaterial("normalMaterial", scene);
             normalMaterial.disableLighting = true;
             normalMaterial.sideOrientation = mesh.material.sideOrientation;
-            normalMaterial.metadata = { hidden: true };
+            normalMaterial.reservedDataStore = { hidden: true };
             mesh.material = normalMaterial;
             this.setState({ displayNormals: true });
         }
@@ -106,12 +110,36 @@ 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();
 
         const displayNormals = mesh.material != null && mesh.material.getClassName() === "NormalMaterial";
-        const renderNormalVectors = (mesh.metadata && mesh.metadata.normalLines) ? true : false;
+        const renderNormalVectors = (mesh.reservedDataStore && mesh.reservedDataStore.normalLines) ? true : false;
 
         return (
             <div className="pane">
@@ -144,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) &&
@@ -174,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} />
                     {
@@ -184,6 +222,7 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
                         mesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind) &&
                         <CheckBoxLineComponent label="Render vertex normals" isSelected={() => renderNormalVectors} onSelect={() => this.renderNormalVectors()} />
                     }
+                    <AxesViewerComponent node={mesh} />
                 </LineContainerComponent>
             </div>
         );

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

@@ -6,9 +6,12 @@ import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 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>
 }
 
@@ -40,6 +43,9 @@ export class TransformNodePropertyGridComponent extends React.Component<ITransfo
                     }
                     <Vector3LineComponent label="Scaling" target={transformNode} propertyName="scaling" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
+                <LineContainerComponent title="DEBUG" closed={true}>
+                    <AxesViewerComponent node={transformNode} />
+                </LineContainerComponent>
             </div>
         );
     }

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

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

@@ -6,7 +6,7 @@ import { GlobalState } from "../../../globalState";
 import { FloatLineComponent } from "../../lines/floatLineComponent";
 import { OptionsLineComponent } from "../../lines/optionsLineComponent";
 import { MessageLineComponent } from "../../lines/messageLineComponent";
-import { BooleanLineComponent } from "../../lines/booleanLineComponent";
+import { faCheck, faTimesCircle } from "@fortawesome/free-solid-svg-icons";
 import { TextLineComponent } from "../../lines/textLineComponent";
 
 interface IGLTFComponentProps {
@@ -65,7 +65,7 @@ export class GLTFComponent extends React.Component<IGLTFComponentProps> {
             return `${count} ${singularForm}s`;
         }
 
-        return `No ${singularForm}`;
+        return `${singularForm}`;
     }
 
     renderValidation() {
@@ -74,10 +74,18 @@ export class GLTFComponent extends React.Component<IGLTFComponentProps> {
 
         return (
             <LineContainerComponent title="GLTF VALIDATION" closed={!issues.numErrors && !issues.numWarnings}>
-                <BooleanLineComponent label={this.prepareText("error", issues.numErrors)} value={issues.numErrors === 0} />
-                <BooleanLineComponent label={this.prepareText("warning", issues.numWarnings)} value={issues.numWarnings === 0} />
-                <BooleanLineComponent label={this.prepareText("info", issues.numInfos)} value={issues.numInfos === 0} />
-                <BooleanLineComponent label={this.prepareText("hint", issues.numHints)} value={issues.numHints === 0} />
+                {
+                    issues.numErrors !== 0 &&
+                    <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" />
+                }
+                <TextLineComponent label="Errors" value={issues.numErrors.toString()} />
+                <TextLineComponent label="Warnings" value={issues.numWarnings.toString()} />
+                <TextLineComponent label="Infos" value={issues.numInfos.toString()} />
+                <TextLineComponent label="Hints" value={issues.numHints.toString()} />
                 <TextLineComponent label="More details" value="Click here" onLink={() => this.openValidationDetails()} />
             </LineContainerComponent>
         )
@@ -114,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>
         )

+ 24 - 24
inspector/src/components/sceneExplorer/entities/meshTreeItemComponent.tsx

@@ -19,7 +19,7 @@ export class MeshTreeItemComponent extends React.Component<IMeshTreeItemComponen
 
         const mesh = this.props.mesh;
 
-        this.state = { isGizmoEnabled: mesh.metadata && mesh.metadata.gizmo, isVisible: this.props.mesh.isVisible }
+        this.state = { isGizmoEnabled: mesh.reservedDataStore && mesh.reservedDataStore.gizmo, isVisible: this.props.mesh.isVisible }
     }
 
     showGizmos(): void {
@@ -27,60 +27,60 @@ export class MeshTreeItemComponent extends React.Component<IMeshTreeItemComponen
 
         if (!this.state.isGizmoEnabled) {
 
-            if (!mesh.metadata) {
-                mesh.metadata = {};
+            if (!mesh.reservedDataStore) {
+                mesh.reservedDataStore = {};
             }
-            mesh.metadata.previousParent = mesh.parent;
+            mesh.reservedDataStore.previousParent = mesh.parent;
 
-            if (mesh.metadata.previousParent) {
-                if (!mesh.metadata.previousParent.metadata) {
-                    mesh.metadata.previousParent.metadata = {};
+            if (mesh.reservedDataStore.previousParent) {
+                if (!mesh.reservedDataStore.previousParent.reservedDataStore) {
+                    mesh.reservedDataStore.previousParent.reservedDataStore = {};
                 }
 
-                if (!mesh.metadata.previousParent.metadata.detachedChildren) {
-                    mesh.metadata.previousParent.metadata.detachedChildren = [];
+                if (!mesh.reservedDataStore.previousParent.reservedDataStore.detachedChildren) {
+                    mesh.reservedDataStore.previousParent.reservedDataStore.detachedChildren = [];
                 }
 
-                mesh.metadata.previousParent.metadata.detachedChildren.push(mesh);
+                mesh.reservedDataStore.previousParent.reservedDataStore.detachedChildren.push(mesh);
             }
 
             // Connect to gizmo
             const dummy = BABYLON.BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox(mesh as Mesh);
-            dummy.metadata = { hidden: true };
+            dummy.reservedDataStore = { hidden: true };
             const gizmo = new BABYLON.BoundingBoxGizmo(BABYLON.Color3.FromHexString("#0984e3"));
             gizmo.attachedMesh = dummy;
 
             gizmo.updateBoundingBox();
 
             gizmo.fixedDragMeshScreenSize = true;
-            mesh.metadata.gizmo = gizmo;
+            mesh.reservedDataStore.gizmo = gizmo;
 
             var pointerDragBehavior = new BABYLON.PointerDragBehavior();
             pointerDragBehavior.useObjectOrienationForDragging = false;
 
             dummy.addBehavior(pointerDragBehavior);
 
-            mesh.metadata.pointerDragBehavior = pointerDragBehavior;
-            mesh.metadata.dummy = dummy;
+            mesh.reservedDataStore.pointerDragBehavior = pointerDragBehavior;
+            mesh.reservedDataStore.dummy = dummy;
 
             this.setState({ isGizmoEnabled: true });
             return;
         }
 
-        const previousParent = mesh.metadata.previousParent;
-        mesh.removeBehavior(mesh.metadata.pointerDragBehavior);
-        mesh.metadata.gizmo.dispose();
-        mesh.metadata.gizmo = null;
+        const previousParent = mesh.reservedDataStore.previousParent;
+        mesh.removeBehavior(mesh.reservedDataStore.pointerDragBehavior);
+        mesh.reservedDataStore.gizmo.dispose();
+        mesh.reservedDataStore.gizmo = null;
         mesh.setParent(previousParent);
-        mesh.metadata.dummy.dispose();
-        mesh.metadata.dummy = null;
+        mesh.reservedDataStore.dummy.dispose();
+        mesh.reservedDataStore.dummy = null;
 
-        if (previousParent && previousParent.metadata) {
-            previousParent.metadata.detachedChildren = null;
+        if (previousParent && previousParent.reservedDataStore) {
+            previousParent.reservedDataStore.detachedChildren = null;
         }
 
-        mesh.metadata.previousParent = null;
-        mesh.metadata.pointerDragBehavior = null;
+        mesh.reservedDataStore.previousParent = null;
+        mesh.reservedDataStore.pointerDragBehavior = null;
 
         this.setState({ isGizmoEnabled: false });
     }

+ 10 - 10
inspector/src/components/sceneExplorer/entities/sceneTreeItemComponent.tsx

@@ -22,8 +22,8 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
 
         const scene = this.props.scene;
         let gizmoMode = 0;
-        if (scene.metadata && scene.metadata.gizmoManager) {
-            const manager: GizmoManager = scene.metadata.gizmoManager;
+        if (scene.reservedDataStore && scene.reservedDataStore.gizmoManager) {
+            const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
             if (manager.positionGizmoEnabled) {
                 gizmoMode = 1;
             } else if (manager.rotationGizmoEnabled) {
@@ -57,8 +57,8 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
         const scene = this.props.scene;
         this._onSelectionChangeObserver = this.props.onSelectionChangedObservable.add((entity) => {
             this._selectedEntity = entity;
-            if (scene.metadata && scene.metadata.gizmoManager) {
-                const manager: GizmoManager = scene.metadata.gizmoManager;
+            if (scene.reservedDataStore && scene.reservedDataStore.gizmoManager) {
+                const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
 
                 const className = entity.getClassName();
 
@@ -115,15 +115,15 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
     setGizmoMode(mode: number) {
         const scene = this.props.scene;
 
-        if (!scene.metadata) {
-            scene.metadata = {};
+        if (!scene.reservedDataStore) {
+            scene.reservedDataStore = {};
         }
 
-        if (!scene.metadata.gizmoManager) {
-            scene.metadata.gizmoManager = new GizmoManager(scene);
+        if (!scene.reservedDataStore.gizmoManager) {
+            scene.reservedDataStore.gizmoManager = new GizmoManager(scene);
         }
 
-        const manager: GizmoManager = scene.metadata.gizmoManager;
+        const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
 
         manager.positionGizmoEnabled = false;
         manager.rotationGizmoEnabled = false;
@@ -132,7 +132,7 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
         if (this.state.gizmoMode === mode) {
             mode = 0;
             manager.dispose();
-            scene.metadata.gizmoManager = null;
+            scene.reservedDataStore.gizmoManager = null;
         } else {
             switch (mode) {
                 case 1:

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

+ 51 - 11
inspector/src/components/sceneExplorer/sceneExplorer.scss

@@ -121,22 +121,48 @@
         -ms-user-select: none;    
         user-select: none;    
         
-        align-self: center;                
-        display: flex;
-        align-items: center;
+        align-self: center;     
+        display: grid;
+        align-items: center;           
 
         &:hover {
             background: #444444;
         }
-        
-        .arrow {
-            margin-left: 0px;
-            color: white;
-            cursor: pointer;
-            display: inline-block;
-            margin-right: 6px;
-            opacity: 0.5;
+
+        .expandableHeader {
+            display: grid;
+            grid-template-columns: 1fr 20px;
+
+            .text {
+                grid-column: 1;
+                display: grid;
+                grid-template-columns: 20px 1fr;
+
+                .arrow {
+                    grid-column: 1;
+                    margin-left: 0px;
+                    color: white;
+                    cursor: pointer;
+                    display: inline-block;
+                    margin-right: 6px;
+                    opacity: 0.5;
+                }
+
+                .text-value {
+                    grid-column: 2;
+                    display: flex;
+                    align-items: center;
+                }
+            }
+
+            .expandAll {                
+                opacity: 0.5;
+                grid-column: 2;
+                margin-right: 10px;  
+            }
         }
+        
+
     }
 
     .icon {
@@ -283,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%;

+ 52 - 7
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) {
@@ -101,7 +126,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 data.previousOne = item;
             }
 
-            if (item.getChildren && item.metadata && item.metadata.isExpanded) {
+            if (item.getChildren && item.reservedDataStore && item.reservedDataStore.isExpanded) {
                 if (this.findSiblings(item, item.getChildren(), target, goNext, data)) {
                     return true;
                 }
@@ -127,16 +152,16 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             goNext = true;
             search = true;
         } else if (keyEvent.keyCode === 13 || keyEvent.keyCode === 39) { // enter or right
-            var metadata = this.state.selectedEntity.metadata;
-            if (metadata && metadata.setExpandedState) {
-                metadata.setExpandedState(true);
+            var reservedDataStore = this.state.selectedEntity.reservedDataStore;
+            if (reservedDataStore && reservedDataStore.setExpandedState) {
+                reservedDataStore.setExpandedState(true);
             }
             keyEvent.preventDefault();
             return;
         } else if (keyEvent.keyCode === 37) { // left
-            var metadata = this.state.selectedEntity.metadata;
-            if (metadata && metadata.setExpandedState) {
-                metadata.setExpandedState(false);
+            var reservedDataStore = this.state.selectedEntity.reservedDataStore;
+            if (reservedDataStore && reservedDataStore.setExpandedState) {
+                reservedDataStore.setExpandedState(false);
             }
             keyEvent.preventDefault();
             return;
@@ -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");

+ 39 - 16
inspector/src/components/sceneExplorer/treeItemComponent.tsx

@@ -1,14 +1,15 @@
 import * as React from "react";
 import { Nullable, Observable, IExplorerExtensibilityGroup } from "babylonjs";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faPlus, faMinus, faBan } from '@fortawesome/free-solid-svg-icons';
+import { faPlus, faMinus, faBan, faExpandArrowsAlt, faCompress } from '@fortawesome/free-solid-svg-icons';
 import { TreeItemSelectableComponent } from "./treeItemSelectableComponent";
 import { Tools } from "../../tools";
 
 interface ITreeItemExpandableHeaderComponentProps {
     isExpanded: boolean,
     label: string,
-    onClick: () => void
+    onClick: () => void,
+    onExpandAll: (expand: boolean) => void
 }
 
 class TreeItemExpandableHeaderComponent extends React.Component<ITreeItemExpandableHeaderComponentProps> {
@@ -16,14 +17,27 @@ class TreeItemExpandableHeaderComponent extends React.Component<ITreeItemExpanda
         super(props);
     }
 
+    expandAll() {
+        this.props.onExpandAll(!this.props.isExpanded);
+    }
+
     render() {
         const chevron = this.props.isExpanded ? <FontAwesomeIcon icon={faMinus} /> : <FontAwesomeIcon icon={faPlus} />
+        const expandAll = this.props.isExpanded ? <FontAwesomeIcon icon={faCompress} /> : <FontAwesomeIcon icon={faExpandArrowsAlt} />
 
         return (
-            <div>
-                <span className="arrow icon" onClick={() => this.props.onClick()}>
-                    {chevron}
-                </span> {this.props.label}
+            <div className="expandableHeader">
+                <div className="text">
+                    <div className="arrow icon" onClick={() => this.props.onClick()}>
+                        {chevron}
+                    </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}
+                </div>
             </div>
         )
     }
@@ -40,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>
         )
     }
@@ -61,15 +80,15 @@ export interface ITreeItemComponentProps {
 }
 
 
-export class TreeItemComponent extends React.Component<ITreeItemComponentProps, { isExpanded: boolean }> {
+export class TreeItemComponent extends React.Component<ITreeItemComponentProps, { isExpanded: boolean, mustExpand: boolean }> {
     constructor(props: ITreeItemComponentProps) {
         super(props);
 
-        this.state = { isExpanded: false };
+        this.state = { isExpanded: false, mustExpand: false };
     }
 
     switchExpandedState(): void {
-        this.setState({ isExpanded: !this.state.isExpanded });
+        this.setState({ isExpanded: !this.state.isExpanded, mustExpand: false });
     }
 
     shouldComponentUpdate(nextProps: ITreeItemComponentProps, nextState: { isExpanded: boolean }) {
@@ -93,6 +112,10 @@ export class TreeItemComponent extends React.Component<ITreeItemComponentProps,
         return true;
     }
 
+    expandAll(expand: boolean) {
+        this.setState({ isExpanded: expand, mustExpand: expand });
+    }
+
     render() {
         const items = this.props.items;
 
@@ -121,7 +144,7 @@ export class TreeItemComponent extends React.Component<ITreeItemComponentProps,
         if (!this.state.isExpanded) {
             return (
                 <div className="groupContainer" style={marginStyle}>
-                    <TreeItemExpandableHeaderComponent isExpanded={false} label={this.props.label} onClick={() => this.switchExpandedState()} />
+                    <TreeItemExpandableHeaderComponent isExpanded={false} label={this.props.label} onClick={() => this.switchExpandedState()} onExpandAll={expand => this.expandAll(expand)} />
                 </div >
             )
         }
@@ -131,12 +154,12 @@ export class TreeItemComponent extends React.Component<ITreeItemComponentProps,
         return (
             <div>
                 <div className="groupContainer" style={marginStyle}>
-                    <TreeItemExpandableHeaderComponent isExpanded={this.state.isExpanded} label={this.props.label} onClick={() => this.switchExpandedState()} />
+                    <TreeItemExpandableHeaderComponent isExpanded={this.state.isExpanded} label={this.props.label} onClick={() => this.switchExpandedState()} onExpandAll={expand => this.expandAll(expand)} />
                 </div>
                 {
                     sortedItems.map(item => {
                         return (
-                            <TreeItemSelectableComponent 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} />
                         );
                     })
                 }

+ 7 - 6
inspector/src/components/sceneExplorer/treeItemSelectableComponent.tsx

@@ -9,6 +9,7 @@ import * as React from "react";
 export interface ITreeItemSelectableComponentProps {
     entity: any,
     selectedEntity?: any,
+    mustExpand?: boolean,
     offset: number,
     extensibilityGroups?: IExplorerExtensibilityGroup[],
     onSelectionChangedObservable?: Observable<any>,
@@ -21,7 +22,7 @@ export class TreeItemSelectableComponent extends React.Component<ITreeItemSelect
     constructor(props: ITreeItemSelectableComponentProps) {
         super(props);
 
-        this.state = { isSelected: this.props.entity === this.props.selectedEntity, isExpanded: Tools.LookForItem(this.props.entity, this.props.selectedEntity) };
+        this.state = { isSelected: this.props.entity === this.props.selectedEntity, isExpanded: this.props.mustExpand || Tools.LookForItem(this.props.entity, this.props.selectedEntity) };
     }
 
     switchExpandedState(): void {
@@ -91,7 +92,7 @@ export class TreeItemSelectableComponent extends React.Component<ITreeItemSelect
             children.map(item => {
 
                 return (
-                    <TreeItemSelectableComponent extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.props.selectedEntity} key={item.uniqueId} offset={this.props.offset + 2} entity={item} onSelectionChangedObservable={this.props.onSelectionChangedObservable} filter={this.props.filter} />
+                    <TreeItemSelectableComponent mustExpand={this.props.mustExpand} extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.props.selectedEntity} key={item.uniqueId} offset={this.props.offset + 2} entity={item} onSelectionChangedObservable={this.props.onSelectionChangedObservable} filter={this.props.filter} />
                 );
             })
         )
@@ -107,14 +108,14 @@ export class TreeItemSelectableComponent extends React.Component<ITreeItemSelect
         const children = Tools.SortAndFilter(entity, entity.getChildren ? entity.getChildren() : entity.children);
         const hasChildren = children.length > 0;
 
-        if (!entity.metadata) {
-            entity.metadata = {};
+        if (!entity.reservedDataStore) {
+            entity.reservedDataStore = {};
         }
 
-        entity.metadata.setExpandedState = (value: boolean) => {
+        entity.reservedDataStore.setExpandedState = (value: boolean) => {
             this.setState({ isExpanded: value });
         }
-        entity.metadata.isExpanded = this.state.isExpanded;
+        entity.reservedDataStore.isExpanded = this.state.isExpanded;
 
         if (this.props.filter) {
             const lowerCaseFilter = this.props.filter.toLowerCase();

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

+ 3 - 3
inspector/src/tools.ts

@@ -21,10 +21,10 @@ export class Tools {
             return [];
         }
 
-        const finalArray = items.filter((i) => !i.metadata || !i.metadata.hidden);
+        const finalArray = items.filter((i) => !i.reservedDataStore || !i.reservedDataStore.hidden);
 
-        if (parent && parent.metadata && parent.metadata.detachedChildren) {
-            finalArray.push(...parent.metadata.detachedChildren);
+        if (parent && parent.reservedDataStore && parent.reservedDataStore.detachedChildren) {
+            finalArray.push(...parent.reservedDataStore.detachedChildren);
         }
 
         return finalArray.sort((a: any, b: any) => {

+ 41 - 46
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -432,6 +432,18 @@ module BABYLON.GLTF2 {
                 }
             }
 
+            // 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 @@ module BABYLON.GLTF2 {
                     callback(babylonMesh);
                 }
             }
-            else if (node._babylonTransformNode instanceof AbstractMesh) {
-                callback(node._babylonTransformNode);
-            }
         }
 
         private _getMeshes(): AbstractMesh[] {
@@ -561,12 +570,6 @@ module BABYLON.GLTF2 {
                     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 @@ module BABYLON.GLTF2 {
                 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 @@ module BABYLON.GLTF2 {
             };
 
             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 @@ module BABYLON.GLTF2 {
                 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 @@ module BABYLON.GLTF2 {
                     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) {
-                            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!);
                 }
             });
         }
@@ -1713,8 +1701,15 @@ module BABYLON.GLTF2 {
             const sampler = (texture.sampler == undefined ? GLTFLoader._DefaultSampler : ArrayItem.Get(`${context}/sampler`, this.gltf.samplers, texture.sampler));
             const samplerData = this._loadSampler(`/samplers/${sampler.index}`, sampler);
 
+            const image = ArrayItem.Get(`${context}/source`, this.gltf.images, texture.source);
+            let textureURL: Nullable<string> = null;
+            if (image.uri && !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;
+            }
+
             const deferred = new Deferred<void>();
-            const babylonTexture = new Texture(null, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, () => {
+            const babylonTexture = new Texture(textureURL, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, () => {
                 if (!this._disposed) {
                     deferred.resolve();
                 }
@@ -1725,16 +1720,16 @@ module BABYLON.GLTF2 {
             });
             promises.push(deferred.promise);
 
+            if (!textureURL) {
+                promises.push(this.loadImageAsync(`/images/${image.index}`, image).then((data) => {
+                    const name = image.uri || `${this._fileName}#image${image.index}`;
+                    const dataUrl = `data:${this._uniqueRootUrl}${name}`;
+                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
+                }));
+            }
+
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapV = samplerData.wrapV;
-
-            const image = ArrayItem.Get(`${context}/source`, this.gltf.images, texture.source);
-            promises.push(this.loadImageAsync(`/images/${image.index}`, image).then((data) => {
-                const name = image.uri || `${this._fileName}#image${image.index}`;
-                const dataUrl = `data:${this._uniqueRootUrl}${name}`;
-                babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
-            }));
-
             assign(babylonTexture);
 
             this.logClose();

+ 0 - 0
package.json


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä