浏览代码

Merge branch 'master' into drigax/revert6550

David Catuhe 6 年之前
父节点
当前提交
2cda7d0121
共有 100 个文件被更改,包括 46715 次插入29104 次删除
  1. 1319 1000
      Playground/babylon.d.txt
  2. 1 0
      Tools/Config/config.json
  3. 15 15
      Tools/Gulp/helpers/gulp-validateImports.js
  4. 1 0
      Tools/Publisher/tasks/processEs6Packages.js
  5. 1250 1050
      dist/preview release/babylon.d.ts
  6. 2 2
      dist/preview release/babylon.js
  7. 1859 974
      dist/preview release/babylon.max.js
  8. 1 1
      dist/preview release/babylon.max.js.map
  9. 3693 3251
      dist/preview release/babylon.module.d.ts
  10. 1342 1022
      dist/preview release/documentation.d.ts
  11. 40 40
      dist/preview release/gui/babylon.gui.js
  12. 1 1
      dist/preview release/gui/babylon.gui.js.map
  13. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js
  14. 11 7
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  15. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  16. 25 24
      dist/preview release/inspector/babylon.inspector.d.ts
  17. 55 52
      dist/preview release/inspector/babylon.inspector.module.d.ts
  18. 73 50
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  19. 7 7
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  20. 30295 16128
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  21. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  22. 159 110
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  23. 1 1
      dist/preview release/packagesSizeBaseLine.json
  24. 2 1
      dist/preview release/serializers/babylon.glTF2Serializer.js
  25. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  26. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  27. 2 1
      dist/preview release/serializers/babylonjs.serializers.js
  28. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  29. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  30. 3693 3251
      dist/preview release/viewer/babylon.module.d.ts
  31. 94 62
      dist/preview release/viewer/babylon.viewer.js
  32. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  33. 8 2
      dist/preview release/what's new.md
  34. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/commonCameraPropertyGridComponent.tsx
  35. 8 0
      inspector/src/components/actionTabs/tabs/propertyGrids/customPropertyGridComponent.tsx
  36. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonLightPropertyGridComponent.tsx
  37. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/commonMaterialPropertyGridComponent.tsx
  38. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  39. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx
  40. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/skeletonPropertyGridComponent.tsx
  41. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/transformNodePropertyGridComponent.tsx
  42. 6 6
      inspector/src/inspector.ts
  43. 2 4
      localDev/index.html
  44. 746 1186
      localDev/src/webgl-debug.js
  45. 1 33
      nodeEditor/src/components/diagram/defaultNodeModel.ts
  46. 67 19
      nodeEditor/src/components/diagram/diagram.scss
  47. 8 37
      nodeEditor/src/components/diagram/generic/genericNodeWidget.tsx
  48. 5 6
      nodeEditor/src/components/diagram/input/inputNodeModel.tsx
  49. 26 41
      nodeEditor/src/components/diagram/input/inputNodePropertyComponent.tsx
  50. 28 49
      nodeEditor/src/components/diagram/input/inputNodeWidget.tsx
  51. 2 2
      nodeEditor/src/components/diagram/light/lightNodeModel.tsx
  52. 4 35
      nodeEditor/src/components/diagram/light/lightNodeWidget.tsx
  53. 61 0
      nodeEditor/src/components/diagram/portHelper.tsx
  54. 6 6
      nodeEditor/src/components/diagram/texture/textureNodeModel.tsx
  55. 4 33
      nodeEditor/src/components/diagram/texture/textureNodeWidget.tsx
  56. 3 2
      nodeEditor/src/components/diagram/texture/texturePropertyTabComponent.tsx
  57. 18 0
      nodeEditor/src/components/nodeList/nodeList.scss
  58. 7 34
      nodeEditor/src/components/nodeList/nodeListComponent.tsx
  59. 3 3
      nodeEditor/src/components/propertyTab/properties/color3PropertyTabComponent.tsx
  60. 3 3
      nodeEditor/src/components/propertyTab/properties/floatPropertyTabComponent.tsx
  61. 3 3
      nodeEditor/src/components/propertyTab/properties/vector2PropertyTabComponent.tsx
  62. 3 3
      nodeEditor/src/components/propertyTab/properties/vector3PropertyTabComponent.tsx
  63. 3 0
      nodeEditor/src/components/propertyTab/propertyTabComponent.tsx
  64. 1 0
      nodeEditor/src/globalState.ts
  65. 245 102
      nodeEditor/src/graphEditor.tsx
  66. 7 0
      nodeEditor/src/main.scss
  67. 23 0
      nodeEditor/src/sharedComponents/draggableLineComponent.tsx
  68. 3 2
      package.json
  69. 2 1
      serializers/src/glTF/2.0/glTFMaterialExporter.ts
  70. 4 0
      src/Cameras/XR/index.ts
  71. 116 0
      src/Cameras/XR/webXRController.ts
  72. 59 0
      src/Cameras/XR/webXRControllerModelLoader.ts
  73. 119 0
      src/Cameras/XR/webXRControllerPointerSelection.ts
  74. 181 0
      src/Cameras/XR/webXRControllerTeleportation.ts
  75. 104 0
      src/Cameras/XR/webXRDefaultExperience.ts
  76. 11 4
      src/Cameras/XR/webXRExperienceHelper.ts
  77. 32 105
      src/Cameras/XR/webXRInput.ts
  78. 4 1
      src/Cameras/XR/webXRSessionManager.ts
  79. 1 1
      src/Cameras/camera.ts
  80. 4 3
      src/Collisions/collider.ts
  81. 33 3
      src/Culling/ray.ts
  82. 23 8
      src/Gamepads/Controllers/poseEnabledController.ts
  83. 1 1
      src/Gizmos/boundingBoxGizmo.ts
  84. 5 3
      src/Gizmos/gizmo.ts
  85. 8 15
      src/Helpers/sceneHelpers.ts
  86. 24 4
      src/Layers/effectLayer.ts
  87. 21 26
      src/Materials/Node/Blocks/Dual/fogBlock.ts
  88. 2 1
      src/Materials/Node/Blocks/Dual/index.ts
  89. 28 23
      src/Materials/Node/Blocks/Dual/lightBlock.ts
  90. 185 0
      src/Materials/Node/Blocks/Dual/textureBlock.ts
  91. 4 2
      src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts
  92. 0 1
      src/Materials/Node/Blocks/Fragment/index.ts
  93. 0 209
      src/Materials/Node/Blocks/Fragment/textureBlock.ts
  94. 1 0
      src/Materials/Node/Blocks/Input/index.ts
  95. 426 0
      src/Materials/Node/Blocks/Input/inputBlock.ts
  96. 21 10
      src/Materials/Node/Blocks/Vertex/bonesBlock.ts
  97. 16 5
      src/Materials/Node/Blocks/Vertex/instancesBlock.ts
  98. 18 8
      src/Materials/Node/Blocks/Vertex/morphTargetsBlock.ts
  99. 2 1
      src/Materials/Node/Blocks/addBlock.ts
  100. 0 0
      src/Materials/Node/Blocks/clampBlock.ts

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


+ 1 - 0
Tools/Config/config.json

@@ -596,6 +596,7 @@
             "uncheckedLintImports": [
                 "react",
                 "react-dom",
+                "dagre",
                 "re-resizable",
                 "glTF"
             ],

+ 15 - 15
Tools/Gulp/helpers/gulp-validateImports.js

@@ -10,7 +10,7 @@ var config = require("../../Config/config");
 const indexExlclusion = ["States", "EmitterTypes"];
 const forbiddenImports = ["meshBuilder"];
 
-const mapping = { };
+const mapping = {};
 config.modules.forEach(moduleName => {
     mapping[config[moduleName].build.umd.packageName] = moduleName;
 });
@@ -196,7 +196,7 @@ var validateImports = function(data, fileLocation, options) {
 function gulpValidateImports(options) {
     var globalErrors = [];
 
-    return through.obj(function (file, enc, cb) {
+    return through.obj(function(file, enc, cb) {
         if (file.isNull()) {
             cb(null, file);
             return;
@@ -204,7 +204,7 @@ function gulpValidateImports(options) {
         if (file.isStream()) {
             cb(new PluginError("Validate imports", "Streaming not supported."));
         }
-        
+
         let data = file.contents.toString();
         let result = validateImports(data, file.path, options);
 
@@ -218,20 +218,20 @@ function gulpValidateImports(options) {
         }
 
         return cb();
-    }, 
-    function endStream(cb) {
-        if (globalErrors.length > 0) {
-            for (let error of globalErrors) {
-                colorConsole.error(error.message + " " + error.path);
-            }
-            colorConsole.error(`Import validation failed with ${globalErrors.length} errors.`);
+    },
+        function endStream(cb) {
+            if (globalErrors.length > 0) {
+                for (let error of globalErrors) {
+                    colorConsole.error(error.message + " " + error.path);
+                }
+                colorConsole.error(`Import validation failed with ${globalErrors.length} errors.`);
 
-            var finalMessage = new PluginError('gulp-validateImports', `gulp-validateImports: ${globalErrors.length} errors found.`);
-            this.emit('error', finalMessage);
-        }
+                var finalMessage = new PluginError('gulp-validateImports', `gulp-validateImports: ${globalErrors.length} errors found.`);
+                this.emit('error', finalMessage);
+            }
 
-        cb();
-    });
+            cb();
+        });
 }
 
 module.exports = gulpValidateImports;

+ 1 - 0
Tools/Publisher/tasks/processEs6Packages.js

@@ -98,6 +98,7 @@ function processEs6Packages(version) {
         var mainPackageJSON = fs.readJSONSync(mainPackageJSONPath);
         var tslibSemver = mainPackageJSON["devDependencies"]["tslib"];
         colorConsole.log("    Adding tslib version: ", tslibSemver.yellow);
+        umdPackageJson["dependencies"] = umdPackageJson["dependencies"] || {};
         umdPackageJson["dependencies"]["tslib"] = tslibSemver;
 
         let packageJSONPath = path.join(packagePath, "package.json");

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


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


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


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


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


文件差异内容过多而无法显示
+ 1342 - 1022
dist/preview release/documentation.d.ts


+ 40 - 40
dist/preview release/gui/babylon.gui.js

@@ -7,7 +7,7 @@
 		exports["babylonjs-gui"] = factory(require("babylonjs"));
 	else
 		root["BABYLON"] = root["BABYLON"] || {}, root["BABYLON"]["GUI"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -355,7 +355,7 @@ module.exports = g;
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTextureInstrumentation", function() { return AdvancedDynamicTextureInstrumentation; });
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -498,7 +498,7 @@ var AdvancedDynamicTextureInstrumentation = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTexture", function() { return AdvancedDynamicTexture; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _controls_container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./controls/container */ "./2D/controls/container.ts");
 /* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./style */ "./2D/style.ts");
@@ -1619,7 +1619,7 @@ var Button = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Checkbox", function() { return Checkbox; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -1800,7 +1800,7 @@ var Checkbox = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ColorPicker", function() { return ColorPicker; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
@@ -3187,7 +3187,7 @@ var ColorPicker = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container", function() { return Container; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -3592,7 +3592,7 @@ var Container = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control", function() { return Control; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -5783,7 +5783,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__);
 
 
@@ -6239,7 +6239,7 @@ var Grid = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Image", function() { return Image; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 
@@ -7014,7 +7014,7 @@ var InputPassword = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputText", function() { return InputText; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -8023,7 +8023,7 @@ var InputText = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Line", function() { return Line; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -8291,7 +8291,7 @@ var Line = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLine", function() { return MultiLine; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _multiLinePoint__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../multiLinePoint */ "./2D/multiLinePoint.ts");
@@ -8558,7 +8558,7 @@ var MultiLine = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RadioButton", function() { return RadioButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -8903,7 +8903,7 @@ var Rectangle = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScrollViewer", function() { return ScrollViewer; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Events/pointerEvents */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Events/pointerEvents */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _rectangle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../rectangle */ "./2D/controls/rectangle.ts");
 /* harmony import */ var _grid__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../grid */ "./2D/controls/grid.ts");
@@ -9994,7 +9994,7 @@ var SelectionPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSlider", function() { return BaseSlider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -10895,7 +10895,7 @@ var Slider = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel", function() { return StackPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -11153,7 +11153,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextWrapping", function() { return TextWrapping; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextBlock", function() { return TextBlock; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -11593,7 +11593,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KeyPropertySet", function() { return KeyPropertySet; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VirtualKeyboard", function() { return VirtualKeyboard; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
 /* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./button */ "./2D/controls/button.ts");
@@ -11974,7 +11974,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector2WithInfo", function() { return Vector2WithInfo; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Matrix2D", function() { return Matrix2D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -12198,7 +12198,7 @@ var Matrix2D = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Measure", function() { return Measure; });
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -12331,7 +12331,7 @@ var Measure = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLinePoint", function() { return MultiLinePoint; });
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -12474,7 +12474,7 @@ var MultiLinePoint = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Style", function() { return Style; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -12781,7 +12781,7 @@ var ValueAndUnit = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AbstractButton3D", function() { return AbstractButton3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -12824,7 +12824,7 @@ var AbstractButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Button3D", function() { return Button3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _abstractButton3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./abstractButton3D */ "./3D/controls/abstractButton3D.ts");
 /* harmony import */ var _2D_advancedDynamicTexture__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../2D/advancedDynamicTexture */ "./2D/advancedDynamicTexture.ts");
@@ -13001,7 +13001,7 @@ var Button3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container3D", function() { return Container3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -13158,7 +13158,7 @@ var Container3D = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control3D", function() { return Control3D; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _vector3WithInfo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../vector3WithInfo */ "./3D/vector3WithInfo.ts");
 
@@ -13564,7 +13564,7 @@ var Control3D = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CylinderPanel", function() { return CylinderPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -13649,7 +13649,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HolographicButton", function() { return HolographicButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _button3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./button3D */ "./3D/controls/button3D.ts");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _materials_fluentMaterial__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../materials/fluentMaterial */ "./3D/materials/fluentMaterial.ts");
 /* harmony import */ var _2D_controls_stackPanel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../2D/controls/stackPanel */ "./2D/controls/stackPanel.ts");
@@ -14125,7 +14125,7 @@ var MeshButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlanePanel", function() { return PlanePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
@@ -14180,7 +14180,7 @@ var PlanePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScatterPanel", function() { return ScatterPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -14307,7 +14307,7 @@ var ScatterPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SpherePanel", function() { return SpherePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -14392,7 +14392,7 @@ var SpherePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel3D", function() { return StackPanel3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -14517,7 +14517,7 @@ var StackPanel3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VolumeBasedPanel", function() { return VolumeBasedPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -14708,7 +14708,7 @@ var VolumeBasedPanel = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GUI3DManager", function() { return GUI3DManager; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _controls_container3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./controls/container3D */ "./3D/controls/container3D.ts");
 
@@ -14975,7 +14975,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterialDefines", function() { return FluentMaterialDefines; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterial", function() { return FluentMaterial; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _shaders_fluent_vertex__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./shaders/fluent.vertex */ "./3D/materials/shaders/fluent.vertex.ts");
 /* harmony import */ var _shaders_fluent_fragment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./shaders/fluent.fragment */ "./3D/materials/shaders/fluent.fragment.ts");
@@ -15297,7 +15297,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentPixelShader", function() { return fluentPixelShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentPixelShader';
@@ -15319,7 +15319,7 @@ var fluentPixelShader = { name: name, shader: shader };
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentVertexShader", function() { return fluentVertexShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentVertexShader';
@@ -15342,7 +15342,7 @@ var fluentVertexShader = { name: name, shader: shader };
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector3WithInfo", function() { return Vector3WithInfo; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Maths/math");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -15636,14 +15636,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Misc/observable":
+/***/ "babylonjs/Maths/math":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math__;
 
 /***/ })
 

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


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


+ 11 - 7
dist/preview release/inspector/babylon.inspector.bundle.max.js

@@ -35849,7 +35849,7 @@ var CommonCameraPropertyGridComponent = /** @class */ (function (_super) {
             { label: "Orthographic", value: babylonjs_Cameras_camera__WEBPACK_IMPORTED_MODULE_2__["Camera"].ORTHOGRAPHIC_CAMERA }
         ];
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", null,
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_8__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: camera, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_8__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: camera, lockObject: this.props.lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_4__["LineContainerComponent"], { globalState: this.props.globalState, title: "GENERAL" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_6__["TextLineComponent"], { label: "ID", value: camera.id }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_6__["TextLineComponent"], { label: "Unique ID", value: camera.uniqueId.toString() }),
@@ -35949,6 +35949,8 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _lines_vector3LineComponent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../lines/vector3LineComponent */ "./components/actionTabs/lines/vector3LineComponent.tsx");
 /* harmony import */ var _lines_quaternionLineComponent__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../lines/quaternionLineComponent */ "./components/actionTabs/lines/quaternionLineComponent.tsx");
 /* harmony import */ var _lineContainerComponent__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../lineContainerComponent */ "./components/actionTabs/lineContainerComponent.tsx");
+/* harmony import */ var _lines_textInputLineComponent__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../lines/textInputLineComponent */ "./components/actionTabs/lines/textInputLineComponent.tsx");
+
 
 
 
@@ -35977,6 +35979,8 @@ var CustomPropertyGridComponent = /** @class */ (function (_super) {
                 return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_quaternionLineComponent__WEBPACK_IMPORTED_MODULE_7__["QuaternionLineComponent"], { useEuler: this.props.globalState.onlyUseEulers, key: inspectable.label, label: inspectable.label, target: this.props.target, propertyName: inspectable.propertyName, onPropertyChangedObservable: this.props.onPropertyChangedObservable }));
             case babylonjs_Misc_iInspectable__WEBPACK_IMPORTED_MODULE_3__["InspectableType"].Color3:
                 return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_color3LineComponent__WEBPACK_IMPORTED_MODULE_2__["Color3LineComponent"], { key: inspectable.label, label: inspectable.label, target: this.props.target, propertyName: inspectable.propertyName, onPropertyChangedObservable: this.props.onPropertyChangedObservable }));
+            case babylonjs_Misc_iInspectable__WEBPACK_IMPORTED_MODULE_3__["InspectableType"].String:
+                return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textInputLineComponent__WEBPACK_IMPORTED_MODULE_9__["TextInputLineComponent"], { key: inspectable.label, label: inspectable.label, lockObject: this.props.lockObject, target: this.props.target, propertyName: inspectable.propertyName, onPropertyChangedObservable: this.props.onPropertyChangedObservable }));
         }
         return null;
     };
@@ -36962,7 +36966,7 @@ var CommonLightPropertyGridComponent = /** @class */ (function (_super) {
     CommonLightPropertyGridComponent.prototype.render = function () {
         var light = this.props.light;
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", null,
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_5__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: light, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_5__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: light, lockObject: this.props.lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_2__["LineContainerComponent"], { globalState: this.props.globalState, title: "GENERAL" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "ID", value: light.id }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "Unique ID", value: light.uniqueId.toString() }),
@@ -37431,7 +37435,7 @@ var CommonMaterialPropertyGridComponent = /** @class */ (function (_super) {
             { label: "Pre-multiplied", value: babylonjs_Materials_material__WEBPACK_IMPORTED_MODULE_2__["Engine"].ALPHA_PREMULTIPLIED },
         ];
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", null,
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_8__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: material, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_8__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: material, lockObject: this.props.lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_5__["LineContainerComponent"], { globalState: this.props.globalState, title: "GENERAL" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_6__["TextLineComponent"], { label: "ID", value: material.id }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_6__["TextLineComponent"], { label: "Unique ID", value: material.uniqueId.toString() }),
@@ -38103,7 +38107,7 @@ var TexturePropertyGridComponent = /** @class */ (function (_super) {
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "PREVIEW" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textureLineComponent__WEBPACK_IMPORTED_MODULE_7__["TextureLineComponent"], { texture: texture, width: 256, height: 256, globalState: this.props.globalState }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_fileButtonLineComponent__WEBPACK_IMPORTED_MODULE_10__["FileButtonLineComponent"], { label: "Replace texture", onClick: function (file) { return _this.updateTexture(file); }, accept: ".jpg, .png, .tga, .dds, .env" })),
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_13__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: texture, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_13__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: texture, lockObject: this.props.lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "GENERAL" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_5__["TextLineComponent"], { label: "Width", value: texture.getSize().width.toString() }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_5__["TextLineComponent"], { label: "Height", value: texture.getSize().height.toString() }),
@@ -38417,7 +38421,7 @@ var MeshPropertyGridComponent = /** @class */ (function (_super) {
             { label: "Strict", value: babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2__["AbstractMesh"].OCCLUSION_TYPE_STRICT },
         ];
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "pane" },
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_10__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: mesh, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_10__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: mesh, lockObject: this.props.lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "GENERAL" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "ID", value: mesh.id }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "Unique ID", value: mesh.uniqueId.toString() }),
@@ -38605,7 +38609,7 @@ var SkeletonPropertyGridComponent = /** @class */ (function (_super) {
         var _this = this;
         var skeleton = this.props.skeleton;
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "pane" },
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_7__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: skeleton, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_7__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: skeleton, lockObject: this.props.lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_2__["LineContainerComponent"], { globalState: this.props.globalState, title: "GENERAL" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "ID", value: skeleton.id }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "Bone count", value: skeleton.bones.length.toString() }),
@@ -38655,7 +38659,7 @@ var TransformNodePropertyGridComponent = /** @class */ (function (_super) {
     TransformNodePropertyGridComponent.prototype.render = function () {
         var transformNode = this.props.transformNode;
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "pane" },
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_7__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: transformNode, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_customPropertyGridComponent__WEBPACK_IMPORTED_MODULE_7__["CustomPropertyGridComponent"], { globalState: this.props.globalState, target: transformNode, lockObject: this.props.lockObject, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_2__["LineContainerComponent"], { globalState: this.props.globalState, title: "GENERAL" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_5__["TextLineComponent"], { label: "ID", value: transformNode.id }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_5__["TextLineComponent"], { label: "Unique ID", value: transformNode.uniqueId.toString() }),

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


+ 25 - 24
dist/preview release/inspector/babylon.inspector.d.ts

@@ -385,9 +385,34 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ITextInputLineComponentProps {
+        label: string;
+        lockObject: LockObject;
+        target?: any;
+        propertyName?: string;
+        value?: string;
+        onChange?: (value: string) => void;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {
+        value: string;
+    }> {
+        private _localChange;
+        constructor(props: ITextInputLineComponentProps);
+        componentWillUnmount(): void;
+        shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: {
+            value: string;
+        }): boolean;
+        raiseOnPropertyChanged(newValue: string, previousValue: string): void;
+        updateValue(value: string): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface ICustomPropertyGridComponentProps {
         globalState: GlobalState;
         target: any;
+        lockObject: LockObject;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     export class CustomPropertyGridComponent extends React.Component<ICustomPropertyGridComponentProps, {
@@ -796,30 +821,6 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface ITextInputLineComponentProps {
-        label: string;
-        lockObject: LockObject;
-        target?: any;
-        propertyName?: string;
-        value?: string;
-        onChange?: (value: string) => void;
-        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
-    }
-    export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {
-        value: string;
-    }> {
-        private _localChange;
-        constructor(props: ITextInputLineComponentProps);
-        componentWillUnmount(): void;
-        shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: {
-            value: string;
-        }): boolean;
-        raiseOnPropertyChanged(newValue: string, previousValue: string): void;
-        updateValue(value: string): void;
-        render(): JSX.Element;
-    }
-}
-declare module INSPECTOR {
     interface ICommonControlPropertyGridComponentProps {
         globalState: GlobalState;
         control: BABYLON.GUI.Control;

+ 55 - 52
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -434,15 +434,45 @@ declare module "babylonjs-inspector/components/actionTabs/lines/quaternionLineCo
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/actionTabs/lines/textInputLineComponent" {
+    import * as React from "react";
+    import { Observable } from "babylonjs/Misc/observable";
+    import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
+    import { LockObject } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/lockObject";
+    interface ITextInputLineComponentProps {
+        label: string;
+        lockObject: LockObject;
+        target?: any;
+        propertyName?: string;
+        value?: string;
+        onChange?: (value: string) => void;
+        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    }
+    export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {
+        value: string;
+    }> {
+        private _localChange;
+        constructor(props: ITextInputLineComponentProps);
+        componentWillUnmount(): void;
+        shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: {
+            value: string;
+        }): boolean;
+        raiseOnPropertyChanged(newValue: string, previousValue: string): void;
+        updateValue(value: string): void;
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/customPropertyGridComponent" {
     import * as React from "react";
     import { Observable } from "babylonjs/Misc/observable";
     import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
     import { GlobalState } from "babylonjs-inspector/components/globalState";
     import { IInspectable } from 'babylonjs/Misc/iInspectable';
+    import { LockObject } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/lockObject";
     interface ICustomPropertyGridComponentProps {
         globalState: GlobalState;
         target: any;
+        lockObject: LockObject;
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
     }
     export class CustomPropertyGridComponent extends React.Component<ICustomPropertyGridComponentProps, {
@@ -974,34 +1004,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         render(): JSX.Element;
     }
 }
-declare module "babylonjs-inspector/components/actionTabs/lines/textInputLineComponent" {
-    import * as React from "react";
-    import { Observable } from "babylonjs/Misc/observable";
-    import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
-    import { LockObject } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/lockObject";
-    interface ITextInputLineComponentProps {
-        label: string;
-        lockObject: LockObject;
-        target?: any;
-        propertyName?: string;
-        value?: string;
-        onChange?: (value: string) => void;
-        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
-    }
-    export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {
-        value: string;
-    }> {
-        private _localChange;
-        constructor(props: ITextInputLineComponentProps);
-        componentWillUnmount(): void;
-        shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: {
-            value: string;
-        }): boolean;
-        raiseOnPropertyChanged(newValue: string, previousValue: string): void;
-        updateValue(value: string): void;
-        render(): JSX.Element;
-    }
-}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent" {
     import * as React from "react";
     import { Observable } from "babylonjs/Misc/observable";
@@ -2637,9 +2639,34 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ITextInputLineComponentProps {
+        label: string;
+        lockObject: LockObject;
+        target?: any;
+        propertyName?: string;
+        value?: string;
+        onChange?: (value: string) => void;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {
+        value: string;
+    }> {
+        private _localChange;
+        constructor(props: ITextInputLineComponentProps);
+        componentWillUnmount(): void;
+        shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: {
+            value: string;
+        }): boolean;
+        raiseOnPropertyChanged(newValue: string, previousValue: string): void;
+        updateValue(value: string): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface ICustomPropertyGridComponentProps {
         globalState: GlobalState;
         target: any;
+        lockObject: LockObject;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     export class CustomPropertyGridComponent extends React.Component<ICustomPropertyGridComponentProps, {
@@ -3048,30 +3075,6 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface ITextInputLineComponentProps {
-        label: string;
-        lockObject: LockObject;
-        target?: any;
-        propertyName?: string;
-        value?: string;
-        onChange?: (value: string) => void;
-        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
-    }
-    export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {
-        value: string;
-    }> {
-        private _localChange;
-        constructor(props: ITextInputLineComponentProps);
-        componentWillUnmount(): void;
-        shouldComponentUpdate(nextProps: ITextInputLineComponentProps, nextState: {
-            value: string;
-        }): boolean;
-        raiseOnPropertyChanged(newValue: string, previousValue: string): void;
-        updateValue(value: string): void;
-        render(): JSX.Element;
-    }
-}
-declare module INSPECTOR {
     interface ICommonControlPropertyGridComponentProps {
         globalState: GlobalState;
         control: BABYLON.GUI.Control;

+ 73 - 50
dist/preview release/nodeEditor/babylon.nodeEditor.d.ts

@@ -9,33 +9,6 @@ declare module NODEEDITOR {
     }
 }
 declare module NODEEDITOR {
-    /**
-     * Port model
-     */
-    export class DefaultPortModel extends PortModel {
-        /**
-         * If the port is input or output
-         */
-        position: string | "input" | "output";
-        /**
-         * What the port is connected to
-         */
-        connection: BABYLON.Nullable<BABYLON.NodeMaterialConnectionPoint>;
-        defaultValue: any;
-        static idCounter: number;
-        constructor(name: string, type?: string);
-        canLinkToPort(port: DefaultPortModel): boolean;
-        syncWithNodeMaterialConnectionPoint(connection: BABYLON.NodeMaterialConnectionPoint): void;
-        getNodeModel(): DefaultNodeModel;
-        link(outPort: DefaultPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
-        createLinkModel(): LinkModel;
-        static SortInputOutput(a: BABYLON.Nullable<DefaultPortModel>, b: BABYLON.Nullable<DefaultPortModel>): {
-            input: DefaultPortModel;
-            output: DefaultPortModel;
-        } | null;
-    }
-}
-declare module NODEEDITOR {
     interface ITextLineComponentProps {
         label: string;
         value: string;
@@ -126,6 +99,39 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     /**
+     * Port model
+     */
+    export class DefaultPortModel extends PortModel {
+        /**
+         * If the port is input or output
+         */
+        position: string | "input" | "output";
+        /**
+         * What the port is connected to
+         */
+        connection: BABYLON.Nullable<BABYLON.NodeMaterialConnectionPoint>;
+        defaultValue: any;
+        static idCounter: number;
+        constructor(name: string, type?: string);
+        canLinkToPort(port: DefaultPortModel): boolean;
+        syncWithNodeMaterialConnectionPoint(connection: BABYLON.NodeMaterialConnectionPoint): void;
+        getNodeModel(): DefaultNodeModel;
+        link(outPort: DefaultPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
+        createLinkModel(): LinkModel;
+        static SortInputOutput(a: BABYLON.Nullable<DefaultPortModel>, b: BABYLON.Nullable<DefaultPortModel>): {
+            input: DefaultPortModel;
+            output: DefaultPortModel;
+        } | null;
+    }
+}
+declare module NODEEDITOR {
+    export class PortHelper {
+        static GenerateOutputPorts(node: BABYLON.Nullable<DefaultNodeModel>, ignoreLabel: boolean): JSX.Element[];
+        static GenerateInputPorts(node: BABYLON.Nullable<DefaultNodeModel>, includeOnly?: string[]): JSX.Element[];
+    }
+}
+declare module NODEEDITOR {
+    /**
      * GenericNodeWidgetProps
      */
     export interface GenericNodeWidgetProps {
@@ -175,10 +181,9 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     export interface IButtonLineComponentProps {
-        label: string;
-        onClick: () => void;
+        data: string;
     }
-    export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+    export class DraggableLineComponent extends React.Component<IButtonLineComponentProps> {
         constructor(props: IButtonLineComponentProps);
         render(): JSX.Element;
     }
@@ -186,14 +191,22 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface INodeListComponentProps {
         globalState: GlobalState;
-        onAddValueNode: (b: string) => void;
-        onAddNodeFromClass: (ObjectClass: typeof BABYLON.NodeMaterialBlock) => void;
     }
     export class NodeListComponent extends React.Component<INodeListComponentProps> {
         render(): JSX.Element;
     }
 }
 declare module NODEEDITOR {
+    export interface IButtonLineComponentProps {
+        label: string;
+        onClick: () => void;
+    }
+    export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+        constructor(props: IButtonLineComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
     interface IPropertyTabComponentProps {
         globalState: GlobalState;
     }
@@ -265,14 +278,14 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     /**
-     * BABYLON.Texture node model which stores information about a node editor block
+     * Texture node model which stores information about a node editor block
      */
     export class TextureNodeModel extends DefaultNodeModel {
         private _block;
         /**
-         * BABYLON.Texture for the node if it exists
+         * Texture for the node if it exists
          */
-        texture: BABYLON.Nullable<BABYLON.Texture>;
+        texture: BABYLON.Nullable<BABYLON.BaseTexture>;
         /**
          * Constructs the node model
          */
@@ -408,7 +421,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IVector2PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class Vector2PropertyTabComponent extends React.Component<IVector2PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -448,7 +461,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IVector3PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class Vector3PropertyTabComponent extends React.Component<IVector3PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -514,7 +527,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IColor3PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class Color3PropertyTabComponent extends React.Component<IColor3PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -549,7 +562,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IFloatPropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class FloatPropertyTabComponent extends React.Component<IFloatPropertyTabComponentProps> {
         render(): JSX.Element;
@@ -581,12 +594,12 @@ declare module NODEEDITOR {
      * Generic node model which stores information about a node editor block
      */
     export class InputNodeModel extends DefaultNodeModel {
-        connection?: BABYLON.NodeMaterialConnectionPoint;
+        readonly inputBlock: BABYLON.InputBlock;
         /**
          * Constructs the node model
          */
         constructor();
-        renderProperties(globalState: GlobalState): JSX.Element | null;
+        renderProperties(globalState: GlobalState): JSX.Element;
     }
 }
 declare module NODEEDITOR {
@@ -607,7 +620,7 @@ declare module NODEEDITOR {
          */
         constructor(props: InputNodeWidgetProps);
         renderValue(value: string): JSX.Element | null;
-        render(): JSX.Element;
+        render(): JSX.Element | null;
     }
 }
 declare module NODEEDITOR {
@@ -728,8 +741,7 @@ declare module NODEEDITOR {
         globalState: GlobalState;
     }
     export class NodeCreationOptions {
-        column: number;
-        nodeMaterialBlock?: BABYLON.NodeMaterialBlock;
+        nodeMaterialBlock: BABYLON.NodeMaterialBlock;
         type?: string;
         connection?: BABYLON.NodeMaterialConnectionPoint;
     }
@@ -740,10 +752,6 @@ declare module NODEEDITOR {
         /** @hidden */
         _toAdd: LinkModel[] | null;
         /**
-         * Current row/column position used when adding new nodes
-         */
-        private _rowPos;
-        /**
          * Creates a node and recursivly creates its parent nodes from it's input
          * @param nodeMaterialBlock
          */
@@ -751,10 +759,23 @@ declare module NODEEDITOR {
         componentDidMount(): void;
         componentWillUnmount(): void;
         constructor(props: IGraphEditorProps);
+        distributeGraph(): dagre.Node[];
+        mapElements(): {
+            id: string;
+            metadata: {
+                id: string;
+                width: number;
+                height: number;
+            };
+        }[];
+        mapEdges(): {
+            from: import("storm-react-diagrams").NodeModel;
+            to: import("storm-react-diagrams").NodeModel;
+        }[];
         buildMaterial(): void;
         build(): void;
-        addNodeFromClass(ObjectClass: typeof BABYLON.NodeMaterialBlock): DefaultNodeModel;
-        addValueNode(type: string, column?: number, connection?: BABYLON.NodeMaterialConnectionPoint): DefaultNodeModel;
+        reOrganize(): void;
+        addValueNode(type: string): DefaultNodeModel;
         private _startX;
         private _moveInProgress;
         private _leftWidth;
@@ -763,6 +784,7 @@ declare module NODEEDITOR {
         onPointerUp(evt: React.PointerEvent<HTMLDivElement>): void;
         resizeColumns(evt: React.PointerEvent<HTMLDivElement>, forLeft?: boolean): void;
         buildColumnLayout(): string;
+        emitNewBlock(event: React.DragEvent<HTMLDivElement>): void;
         render(): JSX.Element;
     }
 }
@@ -796,6 +818,7 @@ declare module NODEEDITOR {
         onResetRequiredObservable: BABYLON.Observable<void>;
         onUpdateRequiredObservable: BABYLON.Observable<void>;
         onZoomToFitRequiredObservable: BABYLON.Observable<void>;
+        onReOrganizedRequiredObservable: BABYLON.Observable<void>;
         onLogRequiredObservable: BABYLON.Observable<LogEntry>;
     }
 }

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


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


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


+ 159 - 110
dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts

@@ -8,37 +8,6 @@ declare module "babylonjs-node-editor/dataStorage" {
         static StoreNumber(key: string, value: number): void;
     }
 }
-declare module "babylonjs-node-editor/components/diagram/defaultPortModel" {
-    import { LinkModel, PortModel } from "storm-react-diagrams";
-    import { Nullable } from 'babylonjs/types';
-    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
-    import { DefaultNodeModel } from "babylonjs-node-editor/components/diagram/defaultNodeModel";
-    /**
-     * Port model
-     */
-    export class DefaultPortModel extends PortModel {
-        /**
-         * If the port is input or output
-         */
-        position: string | "input" | "output";
-        /**
-         * What the port is connected to
-         */
-        connection: Nullable<NodeMaterialConnectionPoint>;
-        defaultValue: any;
-        static idCounter: number;
-        constructor(name: string, type?: string);
-        canLinkToPort(port: DefaultPortModel): boolean;
-        syncWithNodeMaterialConnectionPoint(connection: NodeMaterialConnectionPoint): void;
-        getNodeModel(): DefaultNodeModel;
-        link(outPort: DefaultPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
-        createLinkModel(): LinkModel;
-        static SortInputOutput(a: Nullable<DefaultPortModel>, b: Nullable<DefaultPortModel>): {
-            input: DefaultPortModel;
-            output: DefaultPortModel;
-        } | null;
-    }
-}
 declare module "babylonjs-node-editor/sharedComponents/textLineComponent" {
     import * as React from "react";
     interface ITextLineComponentProps {
@@ -139,6 +108,45 @@ declare module "babylonjs-node-editor/components/diagram/generic/genericNodeMode
         renderProperties(globalState: GlobalState): JSX.Element;
     }
 }
+declare module "babylonjs-node-editor/components/diagram/defaultPortModel" {
+    import { LinkModel, PortModel } from "storm-react-diagrams";
+    import { Nullable } from 'babylonjs/types';
+    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+    import { DefaultNodeModel } from "babylonjs-node-editor/components/diagram/defaultNodeModel";
+    /**
+     * Port model
+     */
+    export class DefaultPortModel extends PortModel {
+        /**
+         * If the port is input or output
+         */
+        position: string | "input" | "output";
+        /**
+         * What the port is connected to
+         */
+        connection: Nullable<NodeMaterialConnectionPoint>;
+        defaultValue: any;
+        static idCounter: number;
+        constructor(name: string, type?: string);
+        canLinkToPort(port: DefaultPortModel): boolean;
+        syncWithNodeMaterialConnectionPoint(connection: NodeMaterialConnectionPoint): void;
+        getNodeModel(): DefaultNodeModel;
+        link(outPort: DefaultPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
+        createLinkModel(): LinkModel;
+        static SortInputOutput(a: Nullable<DefaultPortModel>, b: Nullable<DefaultPortModel>): {
+            input: DefaultPortModel;
+            output: DefaultPortModel;
+        } | null;
+    }
+}
+declare module "babylonjs-node-editor/components/diagram/portHelper" {
+    import { DefaultNodeModel } from "babylonjs-node-editor/components/diagram/defaultNodeModel";
+    import { Nullable } from 'babylonjs/types';
+    export class PortHelper {
+        static GenerateOutputPorts(node: Nullable<DefaultNodeModel>, ignoreLabel: boolean): JSX.Element[];
+        static GenerateInputPorts(node: Nullable<DefaultNodeModel>, includeOnly?: string[]): JSX.Element[];
+    }
+}
 declare module "babylonjs-node-editor/components/diagram/generic/genericNodeWidget" {
     import * as React from "react";
     import { Nullable } from 'babylonjs/types';
@@ -195,13 +203,12 @@ declare module "babylonjs-node-editor/components/diagram/generic/genericNodeFact
         getNewInstance(): GenericNodeModel;
     }
 }
-declare module "babylonjs-node-editor/sharedComponents/buttonLineComponent" {
+declare module "babylonjs-node-editor/sharedComponents/draggableLineComponent" {
     import * as React from "react";
     export interface IButtonLineComponentProps {
-        label: string;
-        onClick: () => void;
+        data: string;
     }
-    export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+    export class DraggableLineComponent extends React.Component<IButtonLineComponentProps> {
         constructor(props: IButtonLineComponentProps);
         render(): JSX.Element;
     }
@@ -209,16 +216,24 @@ declare module "babylonjs-node-editor/sharedComponents/buttonLineComponent" {
 declare module "babylonjs-node-editor/components/nodeList/nodeListComponent" {
     import * as React from "react";
     import { GlobalState } from "babylonjs-node-editor/globalState";
-    import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
     interface INodeListComponentProps {
         globalState: GlobalState;
-        onAddValueNode: (b: string) => void;
-        onAddNodeFromClass: (ObjectClass: typeof NodeMaterialBlock) => void;
     }
     export class NodeListComponent extends React.Component<INodeListComponentProps> {
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-node-editor/sharedComponents/buttonLineComponent" {
+    import * as React from "react";
+    export interface IButtonLineComponentProps {
+        label: string;
+        onClick: () => void;
+    }
+    export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+        constructor(props: IButtonLineComponentProps);
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-node-editor/components/propertyTab/propertyTabComponent" {
     import * as React from "react";
     import { GlobalState } from "babylonjs-node-editor/globalState";
@@ -304,7 +319,7 @@ declare module "babylonjs-node-editor/components/diagram/texture/textureProperty
 }
 declare module "babylonjs-node-editor/components/diagram/texture/textureNodeModel" {
     import { Nullable } from 'babylonjs/types';
-    import { Texture } from 'babylonjs/Materials/Textures/texture';
+    import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
     import { DefaultNodeModel } from "babylonjs-node-editor/components/diagram/defaultNodeModel";
     import { GlobalState } from "babylonjs-node-editor/globalState";
     import { NodeCreationOptions, GraphEditor } from "babylonjs-node-editor/graphEditor";
@@ -317,7 +332,7 @@ declare module "babylonjs-node-editor/components/diagram/texture/textureNodeMode
         /**
          * Texture for the node if it exists
          */
-        texture: Nullable<Texture>;
+        texture: Nullable<BaseTexture>;
         /**
          * Constructs the node model
          */
@@ -467,10 +482,10 @@ declare module "babylonjs-node-editor/sharedComponents/vector2LineComponent" {
 declare module "babylonjs-node-editor/components/propertyTab/properties/vector2PropertyTabComponent" {
     import * as React from "react";
     import { GlobalState } from "babylonjs-node-editor/globalState";
-    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+    import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
     interface IVector2PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: NodeMaterialConnectionPoint;
+        inputBlock: InputBlock;
     }
     export class Vector2PropertyTabComponent extends React.Component<IVector2PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -514,10 +529,10 @@ declare module "babylonjs-node-editor/sharedComponents/vector3LineComponent" {
 declare module "babylonjs-node-editor/components/propertyTab/properties/vector3PropertyTabComponent" {
     import * as React from "react";
     import { GlobalState } from "babylonjs-node-editor/globalState";
-    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+    import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
     interface IVector3PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: NodeMaterialConnectionPoint;
+        inputBlock: InputBlock;
     }
     export class Vector3PropertyTabComponent extends React.Component<IVector3PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -590,10 +605,10 @@ declare module "babylonjs-node-editor/sharedComponents/color3LineComponent" {
 declare module "babylonjs-node-editor/components/propertyTab/properties/color3PropertyTabComponent" {
     import * as React from "react";
     import { GlobalState } from "babylonjs-node-editor/globalState";
-    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+    import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
     interface IColor3PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: NodeMaterialConnectionPoint;
+        inputBlock: InputBlock;
     }
     export class Color3PropertyTabComponent extends React.Component<IColor3PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -631,10 +646,10 @@ declare module "babylonjs-node-editor/sharedComponents/floatLineComponent" {
 declare module "babylonjs-node-editor/components/propertyTab/properties/floatPropertyTabComponent" {
     import * as React from "react";
     import { GlobalState } from "babylonjs-node-editor/globalState";
-    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+    import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
     interface IFloatPropertyTabComponentProps {
         globalState: GlobalState;
-        connection: NodeMaterialConnectionPoint;
+        inputBlock: InputBlock;
     }
     export class FloatPropertyTabComponent extends React.Component<IFloatPropertyTabComponentProps> {
         render(): JSX.Element;
@@ -667,18 +682,18 @@ declare module "babylonjs-node-editor/components/diagram/input/inputNodeProperty
 }
 declare module "babylonjs-node-editor/components/diagram/input/inputNodeModel" {
     import { DefaultNodeModel } from "babylonjs-node-editor/components/diagram/defaultNodeModel";
-    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
     import { GlobalState } from "babylonjs-node-editor/globalState";
+    import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
     /**
      * Generic node model which stores information about a node editor block
      */
     export class InputNodeModel extends DefaultNodeModel {
-        connection?: NodeMaterialConnectionPoint;
+        readonly inputBlock: InputBlock;
         /**
          * Constructs the node model
          */
         constructor();
-        renderProperties(globalState: GlobalState): JSX.Element | null;
+        renderProperties(globalState: GlobalState): JSX.Element;
     }
 }
 declare module "babylonjs-node-editor/components/diagram/input/inputNodeWidget" {
@@ -703,7 +718,7 @@ declare module "babylonjs-node-editor/components/diagram/input/inputNodeWidget"
          */
         constructor(props: InputNodeWidgetProps);
         renderValue(value: string): JSX.Element | null;
-        render(): JSX.Element;
+        render(): JSX.Element | null;
     }
 }
 declare module "babylonjs-node-editor/components/diagram/input/inputNodeFactory" {
@@ -843,6 +858,7 @@ declare module "babylonjs-node-editor/components/diagram/light/lightNodeFactory"
 declare module "babylonjs-node-editor/graphEditor" {
     import { LinkModel } from "storm-react-diagrams";
     import * as React from "react";
+    import * as dagre from "babylonjs-node-editor/dagre";
     import { GlobalState } from "babylonjs-node-editor/globalState";
     import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
     import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
@@ -851,8 +867,7 @@ declare module "babylonjs-node-editor/graphEditor" {
         globalState: GlobalState;
     }
     export class NodeCreationOptions {
-        column: number;
-        nodeMaterialBlock?: NodeMaterialBlock;
+        nodeMaterialBlock: NodeMaterialBlock;
         type?: string;
         connection?: NodeMaterialConnectionPoint;
     }
@@ -863,10 +878,6 @@ declare module "babylonjs-node-editor/graphEditor" {
         /** @hidden */
         _toAdd: LinkModel[] | null;
         /**
-         * Current row/column position used when adding new nodes
-         */
-        private _rowPos;
-        /**
          * Creates a node and recursivly creates its parent nodes from it's input
          * @param nodeMaterialBlock
          */
@@ -874,10 +885,23 @@ declare module "babylonjs-node-editor/graphEditor" {
         componentDidMount(): void;
         componentWillUnmount(): void;
         constructor(props: IGraphEditorProps);
+        distributeGraph(): dagre.Node[];
+        mapElements(): {
+            id: string;
+            metadata: {
+                id: string;
+                width: number;
+                height: number;
+            };
+        }[];
+        mapEdges(): {
+            from: import("storm-react-diagrams").NodeModel;
+            to: import("storm-react-diagrams").NodeModel;
+        }[];
         buildMaterial(): void;
         build(): void;
-        addNodeFromClass(ObjectClass: typeof NodeMaterialBlock): DefaultNodeModel;
-        addValueNode(type: string, column?: number, connection?: NodeMaterialConnectionPoint): DefaultNodeModel;
+        reOrganize(): void;
+        addValueNode(type: string): DefaultNodeModel;
         private _startX;
         private _moveInProgress;
         private _leftWidth;
@@ -886,6 +910,7 @@ declare module "babylonjs-node-editor/graphEditor" {
         onPointerUp(evt: React.PointerEvent<HTMLDivElement>): void;
         resizeColumns(evt: React.PointerEvent<HTMLDivElement>, forLeft?: boolean): void;
         buildColumnLayout(): string;
+        emitNewBlock(event: React.DragEvent<HTMLDivElement>): void;
         render(): JSX.Element;
     }
 }
@@ -930,6 +955,7 @@ declare module "babylonjs-node-editor/globalState" {
         onResetRequiredObservable: Observable<void>;
         onUpdateRequiredObservable: Observable<void>;
         onZoomToFitRequiredObservable: Observable<void>;
+        onReOrganizedRequiredObservable: Observable<void>;
         onLogRequiredObservable: Observable<LogEntry>;
     }
 }
@@ -978,33 +1004,6 @@ declare module NODEEDITOR {
     }
 }
 declare module NODEEDITOR {
-    /**
-     * Port model
-     */
-    export class DefaultPortModel extends PortModel {
-        /**
-         * If the port is input or output
-         */
-        position: string | "input" | "output";
-        /**
-         * What the port is connected to
-         */
-        connection: BABYLON.Nullable<BABYLON.NodeMaterialConnectionPoint>;
-        defaultValue: any;
-        static idCounter: number;
-        constructor(name: string, type?: string);
-        canLinkToPort(port: DefaultPortModel): boolean;
-        syncWithNodeMaterialConnectionPoint(connection: BABYLON.NodeMaterialConnectionPoint): void;
-        getNodeModel(): DefaultNodeModel;
-        link(outPort: DefaultPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
-        createLinkModel(): LinkModel;
-        static SortInputOutput(a: BABYLON.Nullable<DefaultPortModel>, b: BABYLON.Nullable<DefaultPortModel>): {
-            input: DefaultPortModel;
-            output: DefaultPortModel;
-        } | null;
-    }
-}
-declare module NODEEDITOR {
     interface ITextLineComponentProps {
         label: string;
         value: string;
@@ -1095,6 +1094,39 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     /**
+     * Port model
+     */
+    export class DefaultPortModel extends PortModel {
+        /**
+         * If the port is input or output
+         */
+        position: string | "input" | "output";
+        /**
+         * What the port is connected to
+         */
+        connection: BABYLON.Nullable<BABYLON.NodeMaterialConnectionPoint>;
+        defaultValue: any;
+        static idCounter: number;
+        constructor(name: string, type?: string);
+        canLinkToPort(port: DefaultPortModel): boolean;
+        syncWithNodeMaterialConnectionPoint(connection: BABYLON.NodeMaterialConnectionPoint): void;
+        getNodeModel(): DefaultNodeModel;
+        link(outPort: DefaultPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
+        createLinkModel(): LinkModel;
+        static SortInputOutput(a: BABYLON.Nullable<DefaultPortModel>, b: BABYLON.Nullable<DefaultPortModel>): {
+            input: DefaultPortModel;
+            output: DefaultPortModel;
+        } | null;
+    }
+}
+declare module NODEEDITOR {
+    export class PortHelper {
+        static GenerateOutputPorts(node: BABYLON.Nullable<DefaultNodeModel>, ignoreLabel: boolean): JSX.Element[];
+        static GenerateInputPorts(node: BABYLON.Nullable<DefaultNodeModel>, includeOnly?: string[]): JSX.Element[];
+    }
+}
+declare module NODEEDITOR {
+    /**
      * GenericNodeWidgetProps
      */
     export interface GenericNodeWidgetProps {
@@ -1144,10 +1176,9 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     export interface IButtonLineComponentProps {
-        label: string;
-        onClick: () => void;
+        data: string;
     }
-    export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+    export class DraggableLineComponent extends React.Component<IButtonLineComponentProps> {
         constructor(props: IButtonLineComponentProps);
         render(): JSX.Element;
     }
@@ -1155,14 +1186,22 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface INodeListComponentProps {
         globalState: GlobalState;
-        onAddValueNode: (b: string) => void;
-        onAddNodeFromClass: (ObjectClass: typeof BABYLON.NodeMaterialBlock) => void;
     }
     export class NodeListComponent extends React.Component<INodeListComponentProps> {
         render(): JSX.Element;
     }
 }
 declare module NODEEDITOR {
+    export interface IButtonLineComponentProps {
+        label: string;
+        onClick: () => void;
+    }
+    export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+        constructor(props: IButtonLineComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
     interface IPropertyTabComponentProps {
         globalState: GlobalState;
     }
@@ -1234,14 +1273,14 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     /**
-     * BABYLON.Texture node model which stores information about a node editor block
+     * Texture node model which stores information about a node editor block
      */
     export class TextureNodeModel extends DefaultNodeModel {
         private _block;
         /**
-         * BABYLON.Texture for the node if it exists
+         * Texture for the node if it exists
          */
-        texture: BABYLON.Nullable<BABYLON.Texture>;
+        texture: BABYLON.Nullable<BABYLON.BaseTexture>;
         /**
          * Constructs the node model
          */
@@ -1377,7 +1416,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IVector2PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class Vector2PropertyTabComponent extends React.Component<IVector2PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -1417,7 +1456,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IVector3PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class Vector3PropertyTabComponent extends React.Component<IVector3PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -1483,7 +1522,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IColor3PropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class Color3PropertyTabComponent extends React.Component<IColor3PropertyTabComponentProps> {
         render(): JSX.Element;
@@ -1518,7 +1557,7 @@ declare module NODEEDITOR {
 declare module NODEEDITOR {
     interface IFloatPropertyTabComponentProps {
         globalState: GlobalState;
-        connection: BABYLON.NodeMaterialConnectionPoint;
+        inputBlock: BABYLON.InputBlock;
     }
     export class FloatPropertyTabComponent extends React.Component<IFloatPropertyTabComponentProps> {
         render(): JSX.Element;
@@ -1550,12 +1589,12 @@ declare module NODEEDITOR {
      * Generic node model which stores information about a node editor block
      */
     export class InputNodeModel extends DefaultNodeModel {
-        connection?: BABYLON.NodeMaterialConnectionPoint;
+        readonly inputBlock: BABYLON.InputBlock;
         /**
          * Constructs the node model
          */
         constructor();
-        renderProperties(globalState: GlobalState): JSX.Element | null;
+        renderProperties(globalState: GlobalState): JSX.Element;
     }
 }
 declare module NODEEDITOR {
@@ -1576,7 +1615,7 @@ declare module NODEEDITOR {
          */
         constructor(props: InputNodeWidgetProps);
         renderValue(value: string): JSX.Element | null;
-        render(): JSX.Element;
+        render(): JSX.Element | null;
     }
 }
 declare module NODEEDITOR {
@@ -1697,8 +1736,7 @@ declare module NODEEDITOR {
         globalState: GlobalState;
     }
     export class NodeCreationOptions {
-        column: number;
-        nodeMaterialBlock?: BABYLON.NodeMaterialBlock;
+        nodeMaterialBlock: BABYLON.NodeMaterialBlock;
         type?: string;
         connection?: BABYLON.NodeMaterialConnectionPoint;
     }
@@ -1709,10 +1747,6 @@ declare module NODEEDITOR {
         /** @hidden */
         _toAdd: LinkModel[] | null;
         /**
-         * Current row/column position used when adding new nodes
-         */
-        private _rowPos;
-        /**
          * Creates a node and recursivly creates its parent nodes from it's input
          * @param nodeMaterialBlock
          */
@@ -1720,10 +1754,23 @@ declare module NODEEDITOR {
         componentDidMount(): void;
         componentWillUnmount(): void;
         constructor(props: IGraphEditorProps);
+        distributeGraph(): dagre.Node[];
+        mapElements(): {
+            id: string;
+            metadata: {
+                id: string;
+                width: number;
+                height: number;
+            };
+        }[];
+        mapEdges(): {
+            from: import("storm-react-diagrams").NodeModel;
+            to: import("storm-react-diagrams").NodeModel;
+        }[];
         buildMaterial(): void;
         build(): void;
-        addNodeFromClass(ObjectClass: typeof BABYLON.NodeMaterialBlock): DefaultNodeModel;
-        addValueNode(type: string, column?: number, connection?: BABYLON.NodeMaterialConnectionPoint): DefaultNodeModel;
+        reOrganize(): void;
+        addValueNode(type: string): DefaultNodeModel;
         private _startX;
         private _moveInProgress;
         private _leftWidth;
@@ -1732,6 +1779,7 @@ declare module NODEEDITOR {
         onPointerUp(evt: React.PointerEvent<HTMLDivElement>): void;
         resizeColumns(evt: React.PointerEvent<HTMLDivElement>, forLeft?: boolean): void;
         buildColumnLayout(): string;
+        emitNewBlock(event: React.DragEvent<HTMLDivElement>): void;
         render(): JSX.Element;
     }
 }
@@ -1765,6 +1813,7 @@ declare module NODEEDITOR {
         onResetRequiredObservable: BABYLON.Observable<void>;
         onUpdateRequiredObservable: BABYLON.Observable<void>;
         onZoomToFitRequiredObservable: BABYLON.Observable<void>;
+        onReOrganizedRequiredObservable: BABYLON.Observable<void>;
         onLogRequiredObservable: BABYLON.Observable<LogEntry>;
     }
 }

+ 1 - 1
dist/preview release/packagesSizeBaseLine.json

@@ -1 +1 @@
-{"engineOnly":252171,"sceneOnly":510346,"minGridMaterial":639256,"minStandardMaterial":765323}
+{"engineOnly":252171,"sceneOnly":510337,"minGridMaterial":639247,"minStandardMaterial":765316}

+ 2 - 1
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -3599,6 +3599,7 @@ var _GLTFMaterialExporter = /** @class */ (function () {
         var imageValues = { data: arr, mimeType: mimeType };
         var extension = mimeType === "image/jpeg" /* JPEG */ ? '.jpeg' : '.png';
         var textureName = baseTextureName + extension;
+        var originalTextureName = textureName;
         if (textureName in imageData) {
             textureName = baseTextureName + "_" + babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Tools"].RandomId() + extension;
         }
@@ -3610,7 +3611,7 @@ var _GLTFMaterialExporter = /** @class */ (function () {
             };
             var foundIndex = null;
             for (var i = 0; i < images.length; ++i) {
-                if (images[i].uri === textureName) {
+                if (images[i].uri === originalTextureName) {
                     foundIndex = i;
                     break;
                 }

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


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


+ 2 - 1
dist/preview release/serializers/babylonjs.serializers.js

@@ -3777,6 +3777,7 @@ var _GLTFMaterialExporter = /** @class */ (function () {
         var imageValues = { data: arr, mimeType: mimeType };
         var extension = mimeType === "image/jpeg" /* JPEG */ ? '.jpeg' : '.png';
         var textureName = baseTextureName + extension;
+        var originalTextureName = textureName;
         if (textureName in imageData) {
             textureName = baseTextureName + "_" + babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Tools"].RandomId() + extension;
         }
@@ -3788,7 +3789,7 @@ var _GLTFMaterialExporter = /** @class */ (function () {
             };
             var foundIndex = null;
             for (var i = 0; i < images.length; ++i) {
-                if (images[i].uri === textureName) {
+                if (images[i].uri === originalTextureName) {
                     foundIndex = i;
                     break;
                 }

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


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


文件差异内容过多而无法显示
+ 3693 - 3251
dist/preview release/viewer/babylon.module.d.ts


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


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


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

@@ -24,7 +24,8 @@
 - Added startAndReleaseDragOnPointerEvents property to pointerDragBehavior which can be set to false for custom drag triggering ([TrevorDev](https://github.com/TrevorDev))
 - Effect renderer to render one or multiple shader effects to a texture ([TrevorDev](https://github.com/TrevorDev))
 - Added url parameters to web request modifiers ([PierreLeBlond](https://github.com/PierreLeBlond))
-- WebXR updated to spec as of June 27th ([TrevorDev](https://github.com/TrevorDev))
+- WebXR updated to spec as of July 9th ([TrevorDev](https://github.com/TrevorDev))
+- WebXR webVR parity helpers (Vive, WMR, Oculus Rift) ([TrevorDev](https://github.com/TrevorDev))
 
 ### Engine
 - Morph targets now can morph UV channel as well ([Deltakosh](https://github.com/deltakosh/))
@@ -43,6 +44,7 @@
 - Added support for occlusion properties ([Deltakosh](https://github.com/deltakosh/))
 - Texture channels are now displayed in grayscale ([Deltakosh](https://github.com/deltakosh/))
 - Ambiant and metallic maps are displayed correctly on PBR material even when using ORM packed texture ([Deltakosh](https://github.com/deltakosh/))
+- Added support for inspectable strings ([Deltakosh](https://github.com/deltakosh/))
 
 ### Tools
 - Added `Color3.toHSV()`, `Color3.toHSVToRef()` and `Color3.HSVtoRGBToRef()` ([Deltakosh](https://github.com/deltakosh/))
@@ -61,7 +63,7 @@
 
 ### Loaders
 - Added support for non-float accessors in animation data for glTF loader. ([bghgary](https://github.com/bghgary))
-- Support loading cube data in .basis loader ([TrevorDev](https://github.com/TrevorDev))
+- Support loading cube data in the .basis loader ([TrevorDev](https://github.com/TrevorDev))
 - Load glTF extras into BJS metadata ([pjoe](https://github.com/pjoe))
 
 ### Materials
@@ -71,6 +73,9 @@
 ### Sounds
 - Added `ISoundOptions.skipCodecCheck` to make `Sound` more flexible with URLs ([nbduke](https://github.com/nbduke))
 
+### Ray
+- Added `Ray.intersectsAxis` to translate screen to axis coordinates without checking collisions ([horusscope](https://github.com/horusscope))
+
 ### Documentation
 - Added a note on shallow bounding of getBoundingInfo ([tibotiber](https://github.com/tibotiber))
 
@@ -88,6 +93,7 @@
 - Fix bug when adding and removing observers in quick succession ([sable](https://github.com/thscott))
 - Cannon and Ammo forceUpdate will no longer cause an unexpected exception ([TrevorDev](https://github.com/TrevorDev))
 - Loading the same multi-material twice and disposing one should not impact the other ([TrevorDev](https://github.com/TrevorDev))
+- GLTF exporter should no longer duplicate exported texture data ([Drigax](https://github.com/Drigax))
 - Avoid exception when disposing of Ammo cloth physics ([TrevorDev](https://github.com/TrevorDev))
 
 ## Breaking changes

+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/commonCameraPropertyGridComponent.tsx

@@ -36,6 +36,7 @@ export class CommonCameraPropertyGridComponent extends React.Component<ICommonCa
         return (
             <div>
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={camera}
+                    lockObject={this.props.lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="ID" value={camera.id} />

+ 8 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/customPropertyGridComponent.tsx

@@ -9,10 +9,13 @@ import { SliderLineComponent } from '../../lines/sliderLineComponent';
 import { Vector3LineComponent } from '../../lines/vector3LineComponent';
 import { QuaternionLineComponent } from '../../lines/quaternionLineComponent';
 import { LineContainerComponent } from '../../lineContainerComponent';
+import { TextInputLineComponent } from '../../lines/textInputLineComponent';
+import { LockObject } from './lockObject';
 
 interface ICustomPropertyGridComponentProps {
     globalState: GlobalState;
     target: any,
+    lockObject: LockObject;
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
 }
 
@@ -51,6 +54,11 @@ export class CustomPropertyGridComponent extends React.Component<ICustomProperty
                     <Color3LineComponent key={inspectable.label} label={inspectable.label} target={this.props.target} propertyName={inspectable.propertyName}
                         onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 )
+            case InspectableType.String:
+                return (
+                    <TextInputLineComponent key={inspectable.label} label={inspectable.label} lockObject={this.props.lockObject} target={this.props.target} propertyName={inspectable.propertyName}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                )
         }
 
         return null;

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

@@ -27,6 +27,7 @@ export class CommonLightPropertyGridComponent extends React.Component<ICommonLig
         return (
             <div>
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={light}
+                    lockObject={this.props.lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="ID" value={light.id} />

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

@@ -55,6 +55,7 @@ export class CommonMaterialPropertyGridComponent extends React.Component<ICommon
         return (
             <div>
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={material}
+                    lockObject={this.props.lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="ID" value={material.id} />

+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -123,6 +123,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                     <FileButtonLineComponent label="Replace texture" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
                 </LineContainerComponent>
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={texture}
+                    lockObject={this.props.lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="Width" value={texture.getSize().width.toString()} />

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

@@ -241,6 +241,7 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         return (
             <div className="pane">
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={mesh}
+                    lockObject={this.props.lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="ID" value={mesh.id} />

+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/skeletonPropertyGridComponent.tsx

@@ -99,6 +99,7 @@ export class SkeletonPropertyGridComponent extends React.Component<ISkeletonProp
         return (
             <div className="pane">
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={skeleton}
+                    lockObject={this.props.lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="ID" value={skeleton.id} />

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

@@ -31,6 +31,7 @@ export class TransformNodePropertyGridComponent extends React.Component<ITransfo
         return (
             <div className="pane">
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={transformNode}
+                    lockObject={this.props.lockObject}
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="ID" value={transformNode.id} />

+ 6 - 6
inspector/src/inspector.ts

@@ -48,27 +48,27 @@ export class Inspector {
     private static _CopyStyles(sourceDoc: HTMLDocument, targetDoc: HTMLDocument) {
         for (var index = 0; index < sourceDoc.styleSheets.length; index++) {
             var styleSheet: any = sourceDoc.styleSheets[index];
-            try{
+            try {
                 if (styleSheet.cssRules) { // for <style> elements
                     const newStyleEl = sourceDoc.createElement('style');
-    
+
                     for (var cssRule of styleSheet.cssRules) {
                         // write the text of each rule into the body of the style element
                         newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText));
                     }
-    
+
                     targetDoc.head!.appendChild(newStyleEl);
                 } else if (styleSheet.href) { // for <link> elements loading CSS from a URL
                     const newLinkEl = sourceDoc.createElement('link');
-    
+
                     newLinkEl.rel = 'stylesheet';
                     newLinkEl.href = styleSheet.href;
                     targetDoc.head!.appendChild(newLinkEl);
                 }
-            }catch(e){
+            } catch (e) {
                 console.log(e)
             }
-            
+
         }
     }
 

+ 2 - 4
localDev/index.html

@@ -11,7 +11,6 @@
     <script src="../dist/preview%20release/ammo.js"></script>
     <script src="../dist/preview%20release/gltf_validator.js"></script>
     <script src="../Tools/DevLoader/BabylonLoader.js"></script>
-    <script src="src/webgl-debug.js"></script>
 
     <style>
         html,
@@ -45,10 +44,9 @@
 
         @font-face {
             font-family: BabylonJSglyphs;
-           /* src: url("http://www.killer-squid.com/fonts/BabylonJSglyphs.otf"); */
+            /* src: url("http://www.killer-squid.com/fonts/BabylonJSglyphs.otf"); */
             src: local("BabylonJSglyphs");
         }
-        
     </style>
 </head>
 
@@ -90,7 +88,7 @@
                     if (typeof createEngine !== "undefined") {
                         engine = createEngine();
                     } else {
-                        engine = new BABYLON.Engine(canvas, true, {  premultipliedAlpha: false, stencil: true, disableWebGL2Support: false, preserveDrawingBuffer: true });
+                        engine = new BABYLON.Engine(canvas, true, { premultipliedAlpha: false, stencil: true, disableWebGL2Support: false, preserveDrawingBuffer: true });
                     }
 
                     BABYLONDEVTOOLS.Loader.debugShortcut(engine);

文件差异内容过多而无法显示
+ 746 - 1186
localDev/src/webgl-debug.js


+ 1 - 33
nodeEditor/src/components/diagram/defaultNodeModel.ts

@@ -1,7 +1,6 @@
 import { NodeModel, DiagramModel } from "storm-react-diagrams";
 import { Nullable } from 'babylonjs/types';
 import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
-import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPointTypes';
 import { GraphEditor, NodeCreationOptions } from '../../graphEditor';
 import { GlobalState } from '../../globalState';
 import { DefaultPortModel } from './defaultPortModel';
@@ -52,7 +51,7 @@ export class DefaultNodeModel extends NodeModel {
                 var connectedNode;
                 var existingNodes = nodes.filter((n) => { return n.block === (connection as any)._connectedPoint._ownerBlock });
                 if (existingNodes.length == 0) {
-                    connectedNode = graphEditor.createNodeFromObject({ column: options.column + 1, nodeMaterialBlock: connection.connectedPoint._ownerBlock });
+                    connectedNode = graphEditor.createNodeFromObject({ nodeMaterialBlock: connection.connectedPoint._ownerBlock });
                 } else {
                     connectedNode = existingNodes[0];
                 }
@@ -63,37 +62,6 @@ export class DefaultNodeModel extends NodeModel {
                 } else {
                     model.addAll(link);
                 }
-            } else if (!connection.isUndefined) {
-                // Create value node for the connection
-                var type = ""
-                if (connection.type == NodeMaterialBlockConnectionPointTypes.Texture) {
-                    type = "Texture"
-                } else if (connection.type == NodeMaterialBlockConnectionPointTypes.Matrix) {
-                    type = "Matrix"
-                } else if (connection.type & NodeMaterialBlockConnectionPointTypes.Vector3OrColor3) {
-                    type = "Vector3"
-                } else if (connection.type & NodeMaterialBlockConnectionPointTypes.Vector2) {
-                    type = "Vector2"
-                } else if (connection.type & NodeMaterialBlockConnectionPointTypes.Vector3OrColor3OrVector4OrColor4) {
-                    type = "Vector4"
-                }
-                else if (connection.type & NodeMaterialBlockConnectionPointTypes.Float) {
-                    type = "Float"
-                }
-
-                // Create links
-                var localNode = graphEditor.addValueNode(type, options.column + 1, connection);
-                if (localNode) {
-                    var ports = localNode.getPorts()
-                    for (var key in ports) {
-                        let link = (ports[key] as DefaultPortModel).link(inputPort);
-                        if (graphEditor._toAdd) {
-                            graphEditor._toAdd.push(link);
-                        } else {
-                            model.addAll(link);
-                        }
-                    }
-                }
             }
         });
     }

+ 67 - 19
nodeEditor/src/components/diagram/diagram.scss

@@ -2,18 +2,45 @@
     width: 200px;
 }
 
+.srd-node--selected > * {
+    border: 4px solid  rgb(0, 192, 255) !important;
+    border-radius: 20px;
+}
+          
+.srd-port {
+    background: rgb(0, 192, 255);
+    border-radius: 10px;
+    border: black 4px solid;
+
+    &.connected {
+        background: #CAB422;
+    }
+}
+
 .diagramBlock {
     background: white;
     width: 100%;
     border: 4px solid black;
+    border-radius: 20px;
+    display: grid;
+    grid-template-rows: 30px auto;
+    grid-template-columns: 50% 50%;
 
     &.input {
-        background: green;
+        background: #40866E;
         color:white;
+
+        .value {
+            grid-row: 2;
+        }
+
+        .outputs {
+            transform: translateY(5px);
+        }
     }
 
     &.attribute {
-        background: orange;
+        background: #40866E;
     }
 
     &.output {
@@ -25,31 +52,51 @@
         }
     }
 
-    .header {
-        margin: 10px;
+    .header {            
+        grid-row: 1;
+        grid-column: 1 / span 2;
+        border: 4px solid black;
+        border-top-right-radius: 16px;
+        border-top-left-radius: 16px;
         font-size: 16px;
         text-align: center;
+        margin: -1px;
         
         white-space: nowrap;
         text-overflow: ellipsis;
         overflow: hidden;
+        background: black;
+        color: white;
     }
 
     .value {
+        grid-row: 3;
+        grid-column: 1 / span 2;
         height: 34px;
         text-align: center;
-        font-size: 20px;
+        font-size: 18px;
         font-weight: bold;
+        margin: 0 10px;
 
-        .fullColor {
-            height: 100%;
+        .value-text {
+            white-space: nowrap;
+            text-overflow: ellipsis;
+            overflow: hidden;
         }
     }
 
+    .preview {
+        border-bottom-left-radius: 16px;
+        border-bottom-right-radius: 16px;
+        padding-top: 2px;
+    }
+
     .inputs {
+        grid-row: 2;
+        grid-column: 1;
         .input-port {
             display: grid;
-            grid-template-columns: 4px calc(100% - 4px);
+            grid-template-columns: 10px calc(100% - 10px);
             grid-template-rows: 100%;
 
             .input-port-plug {
@@ -58,25 +105,27 @@
                 display: grid;
                 align-content: center;
                 margin-left: -11px;
-                
-                .srd-port {
-                    background: grey;
-                }
+
             }
 
             .input-port-label {
                 margin-left: 10px;
                 grid-column: 2;
                 grid-row: 1;         
-                margin-bottom: 2px;       
+                margin-bottom: 2px;    
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;   
             }
         }
     }
 
     .outputs {
+        grid-row: 2;
+        grid-column: 2;
         .output-port {
             display: grid;
-            grid-template-columns: calc(100% - 4px) 4px;
+            grid-template-columns: calc(100% - 10px) 10px;
             grid-template-rows: 100%;
 
             .output-port-plug {
@@ -84,10 +133,6 @@
                 grid-row: 1;
                 display: grid;
                 align-content: center;
-
-                .srd-port {
-                    background: grey;
-                }
             }
 
             .output-port-label {
@@ -95,7 +140,10 @@
                 margin-right: 10px;
                 grid-column: 1;
                 grid-row: 1;                        
-                margin-bottom: 2px;        
+                margin-bottom: 2px;   
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;   
             }
         }
     }

+ 8 - 37
nodeEditor/src/components/diagram/generic/genericNodeWidget.tsx

@@ -1,9 +1,8 @@
 import * as React from "react";
-import { PortWidget } from "storm-react-diagrams";
-import { DefaultPortModel } from '../defaultPortModel';
 import { Nullable } from 'babylonjs/types';
 import { GlobalState } from '../../../globalState';
 import { GenericNodeModel } from './genericNodeModel';
+import { PortHelper } from '../portHelper';
 
 /**
  * GenericNodeWidgetProps
@@ -43,44 +42,16 @@ export class GenericNodeWidget extends React.Component<GenericNodeWidgetProps, G
     }
 
     render() {
+        // Header label
         var header = "";
-        var inputPorts = new Array<JSX.Element>()
-        var outputPorts = new Array<JSX.Element>()
-        if (this.props.node) {
-            // Header label
-            if (this.props.node.block) {
-                header = this.props.node.block.name;
-            }
-
-            // Input/Output ports
-            for (var key in this.props.node.ports) {
-                var port = this.props.node.ports[key] as DefaultPortModel;
-                if (port.position === "input") {
-                    inputPorts.push(
-                        <div key={key} className="input-port">
-                            <div className="input-port-plug">
-                                <PortWidget key={key} name={port.name} node={this.props.node} />
-                            </div>
-                            <div className="input-port-label">
-                                {port.name}
-                            </div>
-                        </div>
-                    )
-                } else {
-                    outputPorts.push(
-                        <div key={key} className="output-port">
-                            <div className="output-port-label">
-                                {port.name}
-                            </div>
-                            <div className="output-port-plug">
-                                <PortWidget key={key} name={port.name} node={this.props.node} />
-                            </div>
-                        </div>
-                    )
-                }
-            }
+        if (this.props.node && this.props.node.block) {
+            header = this.props.node.block.name;
         }
 
+        // Input/Output ports
+        var outputPorts = PortHelper.GenerateOutputPorts(this.props.node, false);
+        var inputPorts = PortHelper.GenerateInputPorts(this.props.node);
+
         return (
             <div className={"diagramBlock" + (outputPorts.length === 0 ? " output" : "")}>
                 <div className="header">

+ 5 - 6
nodeEditor/src/components/diagram/input/inputNodeModel.tsx

@@ -1,14 +1,17 @@
 import * as React from "react";
 import { DefaultNodeModel } from '../defaultNodeModel';
-import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
 import { GlobalState } from '../../../globalState';
 import { InputPropertyTabComponentProps } from './inputNodePropertyComponent';
+import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 
 /**
  * Generic node model which stores information about a node editor block
  */
 export class InputNodeModel extends DefaultNodeModel {
-    public connection?: NodeMaterialConnectionPoint;
+
+    public get inputBlock(): InputBlock {
+        return this.block as InputBlock;
+    }
 
 	/**
 	 * Constructs the node model
@@ -18,10 +21,6 @@ export class InputNodeModel extends DefaultNodeModel {
     }
 
     renderProperties(globalState: GlobalState) {
-        if (!this.connection) {
-            return null;
-        }
-
         return (
             <InputPropertyTabComponentProps globalState={globalState} inputNode={this} />
         );

+ 26 - 41
nodeEditor/src/components/diagram/input/inputNodePropertyComponent.tsx

@@ -8,7 +8,6 @@ import { InputNodeModel } from './inputNodeModel';
 import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPointTypes';
 import { OptionsLineComponent } from '../../../sharedComponents/optionsLineComponent';
 import { NodeMaterialWellKnownValues } from 'babylonjs/Materials/Node/nodeMaterialWellKnownValues';
-import { Vector2, Vector3, Matrix } from 'babylonjs/Maths/math';
 import { TextLineComponent } from '../../../sharedComponents/textLineComponent';
 import { Color3PropertyTabComponent } from '../../propertyTab/properties/color3PropertyTabComponent';
 import { FloatPropertyTabComponent } from '../../propertyTab/properties/floatPropertyTabComponent';
@@ -27,53 +26,39 @@ export class InputPropertyTabComponentProps extends React.Component<IInputProper
     }
 
     renderValue(globalState: GlobalState) {
-        let connection = this.props.inputNode.connection!;
-        switch (connection.type) {
+        let inputBlock = this.props.inputNode.inputBlock;
+        switch (inputBlock.type) {
             case NodeMaterialBlockConnectionPointTypes.Float:
                 return (
-                    <FloatPropertyTabComponent globalState={globalState} connection={connection} />
+                    <FloatPropertyTabComponent globalState={globalState} inputBlock={inputBlock} />
                 );
             case NodeMaterialBlockConnectionPointTypes.Vector2:
                 return (
-                    <Vector2PropertyTabComponent globalState={globalState} connection={connection} />
+                    <Vector2PropertyTabComponent globalState={globalState} inputBlock={inputBlock} />
                 );
             case NodeMaterialBlockConnectionPointTypes.Color3:
             case NodeMaterialBlockConnectionPointTypes.Color3OrColor4:
             case NodeMaterialBlockConnectionPointTypes.Color4:
                 return (
-                    <Color3PropertyTabComponent globalState={globalState} connection={connection} />
+                    <Color3PropertyTabComponent globalState={globalState} inputBlock={inputBlock} />
                 );
             case NodeMaterialBlockConnectionPointTypes.Vector3:
             case NodeMaterialBlockConnectionPointTypes.Vector3OrColor3:
                 return (
-                    <Vector3PropertyTabComponent globalState={globalState} connection={connection} />
+                    <Vector3PropertyTabComponent globalState={globalState} inputBlock={inputBlock} />
                 );
         }
+
         return null;
     }
 
     setDefaultValue() {
-        let connection = this.props.inputNode.connection!;
-        switch (connection.type) {
-            case NodeMaterialBlockConnectionPointTypes.Float:
-                connection.value = 0;
-                break;
-            case NodeMaterialBlockConnectionPointTypes.Vector2:
-                connection.value = Vector2.Zero();
-                break;
-            case NodeMaterialBlockConnectionPointTypes.Vector3:
-            case NodeMaterialBlockConnectionPointTypes.Color3:
-            case NodeMaterialBlockConnectionPointTypes.Vector3OrColor3:
-                connection.value = Vector3.Zero();
-                break;
-            case NodeMaterialBlockConnectionPointTypes.Matrix:
-                connection.value = Matrix.Identity();
-                break;
-        }
+        let inputBlock = this.props.inputNode.inputBlock;
+        inputBlock.setDefaultValue();
     }
 
     render() {
-        let connection = this.props.inputNode.connection!;
+        let inputBlock = this.props.inputNode.inputBlock;
 
         var wellKnownOptions = [
             { label: "World", value: NodeMaterialWellKnownValues.World },
@@ -83,7 +68,7 @@ export class InputPropertyTabComponentProps extends React.Component<IInputProper
             { label: "ViewxProjection", value: NodeMaterialWellKnownValues.ViewProjection },
             { label: "Projection", value: NodeMaterialWellKnownValues.Projection },
             { label: "Camera position", value: NodeMaterialWellKnownValues.CameraPosition },
-            { label: "Automatic", value: NodeMaterialWellKnownValues.Automatic },
+            { label: "Fog color", value: NodeMaterialWellKnownValues.FogColor },
         ];
 
         var attributeOptions = [
@@ -98,48 +83,48 @@ export class InputPropertyTabComponentProps extends React.Component<IInputProper
         return (
             <div>
                 <LineContainerComponent title="GENERAL">
-                    <TextLineComponent label="Type" value={StringTools.GetBaseType(connection.type)} />
+                    <TextLineComponent label="Type" value={StringTools.GetBaseType(inputBlock.type)} />
                 </LineContainerComponent>
                 <LineContainerComponent title="PROPERTIES">
                     <CheckBoxLineComponent label="Is mesh attribute" onSelect={value => {
                         if (!value) {
-                            connection.isUniform = true;
+                            inputBlock.isUniform = true;
                             this.setDefaultValue();
                         } else {
-                            connection.isAttribute = true;
+                            inputBlock.isAttribute = true;
                         }
                         this.props.globalState.onRebuildRequiredObservable.notifyObservers();
                         this.forceUpdate();
-                    }} isSelected={() => connection!.isAttribute} />
+                    }} isSelected={() => inputBlock.isAttribute} />
                     {
-                        connection.isAttribute &&
-                        <OptionsLineComponent label="Attribute" valuesAreStrings={true} options={attributeOptions} target={connection} propertyName="name" onSelect={(value: any) => {
-                            connection.setAsAttribute(value);
+                        inputBlock.isAttribute &&
+                        <OptionsLineComponent label="Attribute" valuesAreStrings={true} options={attributeOptions} target={inputBlock} propertyName="name" onSelect={(value: any) => {
+                            inputBlock.setAsAttribute(value);
                             this.forceUpdate();
                             this.props.globalState.onRebuildRequiredObservable.notifyObservers();
                         }} />
                     }
                     {
-                        connection.isUniform &&
+                        inputBlock.isUniform &&
                         <CheckBoxLineComponent label="Is well known value" onSelect={value => {
                             if (value) {
-                                connection!.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+                                inputBlock.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
                             } else {
-                                connection!.setAsWellKnownValue(null);
+                                inputBlock.setAsWellKnownValue(null);
                                 this.setDefaultValue();
                             }
                             this.props.globalState.onRebuildRequiredObservable.notifyObservers();
                             this.forceUpdate();
-                        }} isSelected={() => connection!.isWellKnownValue} />
+                        }} isSelected={() => inputBlock.isWellKnownValue} />
                     }
                     {
-                        connection.isUniform && !connection.isWellKnownValue &&
+                        inputBlock.isUniform && !inputBlock.isWellKnownValue &&
                         this.renderValue(this.props.globalState)
                     }
                     {
-                        connection.isUniform && connection.isWellKnownValue &&
-                        <OptionsLineComponent label="Well known value" options={wellKnownOptions} target={connection} propertyName="wellKnownValue" onSelect={(value: any) => {
-                            connection.setAsWellKnownValue(value);
+                        inputBlock.isUniform && inputBlock.isWellKnownValue &&
+                        <OptionsLineComponent label="Well known value" options={wellKnownOptions} target={inputBlock} propertyName="wellKnownValue" onSelect={(value: any) => {
+                            inputBlock.setAsWellKnownValue(value);
                             this.forceUpdate();
                             this.props.globalState.onRebuildRequiredObservable.notifyObservers();
                         }} />

+ 28 - 49
nodeEditor/src/components/diagram/input/inputNodeWidget.tsx

@@ -1,13 +1,12 @@
 import * as React from "react";
-import { PortWidget } from "storm-react-diagrams";
 import { InputNodeModel } from './inputNodeModel';
 import { Nullable } from 'babylonjs/types';
 import { GlobalState } from '../../../globalState';
-import { DefaultPortModel } from '../defaultPortModel';
 import { NodeMaterialWellKnownValues } from 'babylonjs/Materials/Node/nodeMaterialWellKnownValues';
 import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPointTypes';
 import { Color3 } from 'babylonjs/Maths/math';
 import { StringTools } from '../../../stringTools';
+import { PortHelper } from '../portHelper';
 
 /**
  * GenericNodeWidgetProps
@@ -42,62 +41,28 @@ export class InputNodeWidget extends React.Component<InputNodeWidgetProps> {
     renderValue(value: string) {
         if (value) {
             return (
-                <div>
+                <div className="value-text">
                     {value}
                 </div>
             )
         }
 
-        let connection = this.props.node!.connection;
-        if (!connection || !connection.isUniform) {
-            return null;
-        }
-
-        switch (connection.type) {
-            case NodeMaterialBlockConnectionPointTypes.Color3:
-            case NodeMaterialBlockConnectionPointTypes.Color3OrColor4:
-            case NodeMaterialBlockConnectionPointTypes.Color4: {
-                let color = connection.value as Color3;
-                return (
-                    <div className="fullColor" style={{ background: color.toHexString() }}></div>
-                )
-            }
-        }
-
         return null;
     }
 
     render() {
-        var outputPorts = new Array<JSX.Element>()
-        let port: DefaultPortModel;
-        if (this.props.node) {
-            for (var key in this.props.node.ports) {
-                port = this.props.node.ports[key] as DefaultPortModel;
-
-                outputPorts.push(
-                    <div key={key} className="output-port">
-                        <div className="output-port-label">
-                        </div>
-                        <div className="output-port-plug">
-                            <PortWidget key={key} name={port.name} node={this.props.node} />
-                        </div>
-                    </div>
-                );
-                break;
-            }
-        }
+        var outputPorts = PortHelper.GenerateOutputPorts(this.props.node, true);
 
-        let connection = this.props.node!.connection;
+        let inputBlock = this.props.node!.inputBlock;
         let value = "";
-        let name = "";
+        let name = StringTools.GetBaseType(inputBlock.output.type);
+        let color = "";
 
-        if (connection) {
-            name = StringTools.GetBaseType(connection.type)
-
-            if (connection.isAttribute) {
-                value = "mesh." + connection.name;
-            } else if (connection.isWellKnownValue) {
-                switch (connection.wellKnownValue) {
+        if (inputBlock) {
+            if (inputBlock.isAttribute) {
+                value = "mesh." + inputBlock.name;
+            } else if (inputBlock.isWellKnownValue) {
+                switch (inputBlock.wellKnownValue) {
                     case NodeMaterialWellKnownValues.World:
                         value = "World";
                         break;
@@ -119,17 +84,31 @@ export class InputNodeWidget extends React.Component<InputNodeWidgetProps> {
                     case NodeMaterialWellKnownValues.CameraPosition:
                         value = "Camera position";
                         break;
-                    case NodeMaterialWellKnownValues.Automatic:
-                        value = "Automatic";
+                    case NodeMaterialWellKnownValues.FogColor:
+                        value = "Fog color";
                         break;
                 }
+            } else {
+                if (!inputBlock || !inputBlock.isUniform) {
+                    return null;
+                }
+
+                switch (inputBlock.type) {
+                    case NodeMaterialBlockConnectionPointTypes.Color3:
+                    case NodeMaterialBlockConnectionPointTypes.Color3OrColor4:
+                    case NodeMaterialBlockConnectionPointTypes.Color4: {
+                        color = (inputBlock.value as Color3).toHexString();
+                    }
+                }
             }
         } else {
             name = "Not connected input";
         }
 
         return (
-            <div className={"diagramBlock input" + (connection && connection.isAttribute ? " attribute" : "")}>
+            <div className={"diagramBlock input" + (inputBlock && inputBlock.isAttribute ? " attribute" : "")} style={{
+                background: color
+            }}>
                 <div className="header">
                     {name}
                 </div>

+ 2 - 2
nodeEditor/src/components/diagram/light/lightNodeModel.tsx

@@ -18,11 +18,11 @@ export class LightNodeModel extends DefaultNodeModel {
 	 * Light for the node if it exists
 	 */
     public get light(): Nullable<Light> {
-        return this._block.light.value;
+        return this._block.light;
     }
 
     public set light(value: Nullable<Light>) {
-        this._block.light.value = value;
+        this._block.light = value;
     }
 
 	/**

+ 4 - 35
nodeEditor/src/components/diagram/light/lightNodeWidget.tsx

@@ -1,9 +1,8 @@
 import * as React from "react";
-import { PortWidget } from "storm-react-diagrams";
 import { LightNodeModel } from './lightNodeModel';
 import { Nullable } from 'babylonjs/types';
 import { GlobalState } from '../../../globalState';
-import { DefaultPortModel } from '../defaultPortModel';
+import { PortHelper } from '../portHelper';
 
 /**
  * GenericNodeWidgetProps
@@ -36,39 +35,9 @@ export class LightNodeWidget extends React.Component<ILightNodeWidgetProps> {
     }
 
     render() {
-        var inputPorts = new Array<JSX.Element>();
-        var outputPorts = new Array<JSX.Element>();
-        if (this.props.node) {
-            // Input/Output ports
-            for (var key in this.props.node.ports) {
-                var port = this.props.node.ports[key] as DefaultPortModel;
-                if (port.position === "input") {
-                    if (port.name !== "light") {
-                        inputPorts.push(
-                            <div key={key} className="input-port">
-                                <div className="input-port-plug">
-                                    <PortWidget key={key} name={port.name} node={this.props.node} />
-                                </div>
-                                <div className="input-port-label">
-                                    {port.name}
-                                </div>
-                            </div>
-                        )
-                    }
-                } else {
-                    outputPorts.push(
-                        <div key={key} className="output-port">
-                            <div className="output-port-label">
-                                {port.name}
-                            </div>
-                            <div className="output-port-plug">
-                                <PortWidget key={key} name={port.name} node={this.props.node} />
-                            </div>
-                        </div>
-                    )
-                }
-            }
-        }
+        // Input/Output ports
+        var outputPorts = PortHelper.GenerateOutputPorts(this.props.node, false);
+        var inputPorts = PortHelper.GenerateInputPorts(this.props.node);
 
         return (
             <div className={"diagramBlock"}>

+ 61 - 0
nodeEditor/src/components/diagram/portHelper.tsx

@@ -0,0 +1,61 @@
+import * as React from "react";
+import { PortWidget } from "storm-react-diagrams";
+import { DefaultNodeModel } from './defaultNodeModel';
+import { DefaultPortModel } from './defaultPortModel';
+import { Nullable } from 'babylonjs/types';
+
+
+export class PortHelper {
+    public static GenerateOutputPorts(node: Nullable<DefaultNodeModel>, ignoreLabel: boolean) {
+        if (!node) {
+            return new Array<JSX.Element>();
+        }
+        let outputPorts = [];
+        for (var key in node.ports) {
+            let port = node.ports[key] as DefaultPortModel;
+            if (port.position === "output") {
+                outputPorts.push(
+                    <div key={key} className="output-port">
+                        {
+                            !ignoreLabel &&
+                            <div className="output-port-label">
+                                {port.name}
+                            </div>
+                        }
+                        <div className="output-port-plug">
+                            <PortWidget key={key} name={port.name} node={node} className={port.connection && port.connection.endpoints.length > 0 ? "connected" : ""} />
+                        </div>
+                    </div>
+                );
+            }
+        }
+
+        return outputPorts;
+    }
+
+    public static GenerateInputPorts(node: Nullable<DefaultNodeModel>, includeOnly?: string[]) {
+        if (!node) {
+            return new Array<JSX.Element>();
+        }
+        let inputPorts = [];
+        for (var key in node.ports) {
+            let port = node.ports[key] as DefaultPortModel;
+            if (port.position === "input") {
+                if (!includeOnly || includeOnly.indexOf(port.name) !== -1) {
+                    inputPorts.push(
+                        <div key={key} className="input-port">
+                            <div className="input-port-plug">
+                                <PortWidget key={key} name={port.name} node={node} className={port.connection && port.connection.connectedPoint ? "connected" : ""} />
+                            </div>
+                            <div className="input-port-label">
+                                {port.name}
+                            </div>
+                        </div>
+                    );
+                }
+            }
+        }
+
+        return inputPorts;
+    }
+}

+ 6 - 6
nodeEditor/src/components/diagram/texture/textureNodeModel.tsx

@@ -1,12 +1,12 @@
 import * as React from 'react';
 import { Nullable } from 'babylonjs/types';
-import { Texture } from 'babylonjs/Materials/Textures/texture';
+import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 import { DefaultNodeModel } from '../defaultNodeModel';
 import { GlobalState } from '../../../globalState';
 import { TexturePropertyTabComponent } from './texturePropertyTabComponent';
 import { NodeCreationOptions, GraphEditor } from '../../../graphEditor';
 import { DiagramModel } from 'storm-react-diagrams/dist/@types/src/models/DiagramModel';
-import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/textureBlock';
+import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
 
 /**
  * Texture node model which stores information about a node editor block
@@ -17,12 +17,12 @@ export class TextureNodeModel extends DefaultNodeModel {
 	/**
 	 * Texture for the node if it exists
 	 */
-    public get texture(): Nullable<Texture> {
-        return this._block.texture.value;
+    public get texture(): Nullable<BaseTexture> {
+        return this._block.texture;
     }
 
-    public set texture(value: Nullable<Texture>) {
-        this._block.texture.value = value;
+    public set texture(value: Nullable<BaseTexture>) {
+        this._block.texture = value;
     }
 
 	/**

+ 4 - 33
nodeEditor/src/components/diagram/texture/textureNodeWidget.tsx

@@ -1,10 +1,9 @@
 import * as React from "react";
-import { PortWidget } from "storm-react-diagrams";
 import { TextureNodeModel } from './textureNodeModel';
 import { TextureLineComponent } from "../../../sharedComponents/textureLineComponent"
 import { Nullable } from 'babylonjs/types';
 import { GlobalState } from '../../../globalState';
-import { DefaultPortModel } from '../defaultPortModel';
+import { PortHelper } from '../portHelper';
 
 /**
  * GenericNodeWidgetProps
@@ -37,37 +36,9 @@ export class TextureNodeWidget extends React.Component<ITextureNodeWidgetProps>
     }
 
     render() {
-        var inputPorts = new Array<JSX.Element>();
-        var outputPorts = new Array<JSX.Element>();
-        if (this.props.node) {
-            // Input/Output ports
-            for (var key in this.props.node.ports) {
-                var port = this.props.node.ports[key] as DefaultPortModel;
-                if (port.position === "output") {
-                    outputPorts.push(
-                        <div key={key} className="output-port">
-                            <div className="output-port-label">
-                                {port.name}
-                            </div>
-                            <div className="output-port-plug">
-                                <PortWidget key={key} name={port.name} node={this.props.node} />
-                            </div>
-                        </div>
-                    );
-                } else if (port.name === "uv") {
-                    inputPorts.push(
-                        <div key={key} className="input-port">
-                            <div className="input-port-plug">
-                                <PortWidget key={key} name={port.name} node={this.props.node} />
-                            </div>
-                            <div className="input-port-label">
-                                {port.name}
-                            </div>
-                        </div>
-                    )
-                }
-            }
-        }
+        // Input/Output ports
+        var outputPorts = PortHelper.GenerateOutputPorts(this.props.node, false);
+        var inputPorts = PortHelper.GenerateInputPorts(this.props.node, ["uv"]);
 
         return (
             <div className={"diagramBlock"}>

+ 3 - 2
nodeEditor/src/components/diagram/texture/texturePropertyTabComponent.tsx

@@ -1,7 +1,7 @@
 
 import * as React from "react";
 import { GlobalState } from '../../../globalState';
-import { Texture } from 'babylonjs/Materials/Textures/texture';
+import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 import { FileButtonLineComponent } from '../../../sharedComponents/fileButtonLineComponent';
 import { Tools } from 'babylonjs/Misc/tools';
 import { Engine } from 'babylonjs/Engines/engine';
@@ -10,6 +10,7 @@ import { TextLineComponent } from '../../../sharedComponents/textLineComponent';
 import { LineContainerComponent } from '../../../sharedComponents/lineContainerComponent';
 import { TextInputLineComponent } from '../../../sharedComponents/textInputLineComponent';
 import { CheckBoxLineComponent } from '../../../sharedComponents/checkBoxLineComponent';
+import { Texture } from 'babylonjs/Materials/Textures/texture';
 
 interface ITexturePropertyTabComponentProps {
     globalState: GlobalState;
@@ -27,7 +28,7 @@ export class TexturePropertyTabComponent extends React.Component<ITexturePropert
             return;
         }
 
-        let texture = this.props.node.texture as Texture;
+        let texture = this.props.node.texture as BaseTexture;
         if (!texture) {
             this.props.node.texture = new Texture(null, Engine.LastCreatedScene)
             texture = this.props.node.texture;

+ 18 - 0
nodeEditor/src/components/nodeList/nodeList.scss

@@ -22,6 +22,24 @@
         .underline {
             border-bottom: 0.5px solid rgba(255, 255, 255, 0.5);
         }
+
+        .draggableLine {
+            height: 30px;
+            display: grid;
+            align-items: center;
+            justify-items: stretch;
+            background: #222222;
+            border: 1px solid rgb(51, 122, 183) !important;
+            text-align: center;
+            margin: 4px 20px;
+            box-sizing: border-box;
+            border-radius: 10px;
+
+            &:hover {
+                background: rgb(51, 122, 183);
+                color: white;
+            }
+        }
     
         .buttonLine {
             height: 30px;

+ 7 - 34
nodeEditor/src/components/nodeList/nodeListComponent.tsx

@@ -2,57 +2,30 @@
 import * as React from "react";
 import { GlobalState } from '../../globalState';
 import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
-import { ButtonLineComponent } from '../../sharedComponents/buttonLineComponent';
-import { AlphaTestBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/alphaTestBlock';
-import { FragmentOutputBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/fragmentOutputBlock';
-import { ImageProcessingBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/imageProcessingBlock';
-import { RGBAMergerBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/rgbaMergerBlock';
-import { RGBASplitterBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/rgbaSplitterBlock';
-import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/textureBlock';
-import { BonesBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/bonesBlock';
-import { InstancesBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/instancesBlock';
-import { MorphTargetsBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/morphTargetsBlock';
-import { VertexOutputBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/vertexOutputBlock';
-import { FogBlock } from 'babylonjs/Materials/Node/Blocks/Dual/fogBlock';
-import { AddBlock } from 'babylonjs/Materials/Node/Blocks/addBlock';
-import { ClampBlock } from 'babylonjs/Materials/Node/Blocks/clampBlock';
-import { MultiplyBlock } from 'babylonjs/Materials/Node/Blocks/multiplyBlock';
-import { Vector2TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector2TransformBlock';
-import { Vector3TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector3TransformBlock';
-import { Vector4TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector4TransformBlock';
-import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
-import { LightBlock } from 'babylonjs/Materials/Node/Blocks/Dual/lightBlock';
+import { DraggableLineComponent } from '../../sharedComponents/draggableLineComponent';
 
 require("./nodeList.scss");
 
 interface INodeListComponentProps {
     globalState: GlobalState;
-    onAddValueNode: (b: string) => void;
-    onAddNodeFromClass: (ObjectClass: typeof NodeMaterialBlock) => void;
 }
 
 export class NodeListComponent extends React.Component<INodeListComponentProps> {
     render() {
         // Block types used to create the menu from
         const allBlocks = {
-            Vertex: [BonesBlock, InstancesBlock, MorphTargetsBlock],
-            Fragment: [AlphaTestBlock, , ImageProcessingBlock, RGBAMergerBlock, RGBASplitterBlock, TextureBlock, LightBlock],
-            Outputs: [VertexOutputBlock, FragmentOutputBlock],
-            Dual: [FogBlock],
-            Math: [AddBlock, ClampBlock, MultiplyBlock, Vector2TransformBlock, Vector3TransformBlock, Vector4TransformBlock],
+            Vertex: ["BonesBlock", "InstancesBlock", "MorphTargetsBlock"],
+            Fragment: ["AlphaTestBlock", "ImageProcessingBlock", "RGBAMergerBlock", "RGBASplitterBlock", "TextureBlock", "LightBlock", "FogBlock"],
+            Outputs: ["VertexOutputBlock", "FragmentOutputBlock"],
+            Math: ["AddBlock", "ClampBlock", "MultiplyBlock", "Vector2TransformBlock", "Vector3TransformBlock", "Vector4TransformBlock"],
             Inputs: ["Float", "Vector2", "Vector3", "Vector4", "Color3", "Color4", "Matrix"],
         }
 
         // Create node menu
         var blockMenu = []
         for (var key in allBlocks) {
-            var blockList = (allBlocks as any)[key].map((b: any) => {
-                var label = typeof b === "string" ? b : b.prototype.getClassName().replace("Block", "")
-                var onClick = typeof b === "string" ? () => {
-                    this.props.onAddValueNode(b);
-                    this.props.globalState.onUpdateRequiredObservable.notifyObservers();
-                } : () => { this.props.onAddNodeFromClass(b) };
-                return <ButtonLineComponent key={label} label={label} onClick={onClick} />
+            var blockList = (allBlocks as any)[key].map((block: any, i: number) => {
+                return <DraggableLineComponent key={block} data={block} />
             })
             blockMenu.push(
                 <LineContainerComponent key={key + " blocks"} title={key + " blocks"} closed={false}>

+ 3 - 3
nodeEditor/src/components/propertyTab/properties/color3PropertyTabComponent.tsx

@@ -1,19 +1,19 @@
 
 import * as React from "react";
 import { GlobalState } from '../../../globalState';
-import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
 import { Color3LineComponent } from '../../../sharedComponents/color3LineComponent';
+import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 
 interface IColor3PropertyTabComponentProps {
     globalState: GlobalState;
-    connection: NodeMaterialConnectionPoint;
+    inputBlock: InputBlock;
 }
 
 export class Color3PropertyTabComponent extends React.Component<IColor3PropertyTabComponentProps> {
 
     render() {
         return (
-            <Color3LineComponent label="Value" target={this.props.connection} propertyName="value" onChange={() => {
+            <Color3LineComponent label="Value" target={this.props.inputBlock} propertyName="value" onChange={() => {
                 this.props.globalState.onUpdateRequiredObservable.notifyObservers();
             }}></Color3LineComponent>
         );

+ 3 - 3
nodeEditor/src/components/propertyTab/properties/floatPropertyTabComponent.tsx

@@ -1,19 +1,19 @@
 
 import * as React from "react";
 import { GlobalState } from '../../../globalState';
-import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
 import { FloatLineComponent } from '../../../sharedComponents/floatLineComponent';
+import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 
 interface IFloatPropertyTabComponentProps {
     globalState: GlobalState;
-    connection: NodeMaterialConnectionPoint;
+    inputBlock: InputBlock;
 }
 
 export class FloatPropertyTabComponent extends React.Component<IFloatPropertyTabComponentProps> {
 
     render() {
         return (
-            <FloatLineComponent label="Value" target={this.props.connection} propertyName="value"></FloatLineComponent>
+            <FloatLineComponent label="Value" target={this.props.inputBlock} propertyName="value"></FloatLineComponent>
         );
     }
 }

+ 3 - 3
nodeEditor/src/components/propertyTab/properties/vector2PropertyTabComponent.tsx

@@ -2,18 +2,18 @@
 import * as React from "react";
 import { GlobalState } from '../../../globalState';
 import { Vector2LineComponent } from '../../../sharedComponents/vector2LineComponent';
-import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 
 interface IVector2PropertyTabComponentProps {
     globalState: GlobalState;
-    connection: NodeMaterialConnectionPoint;
+    inputBlock: InputBlock;
 }
 
 export class Vector2PropertyTabComponent extends React.Component<IVector2PropertyTabComponentProps> {
 
     render() {
         return (
-            <Vector2LineComponent label="Value" target={this.props.connection} propertyName="value"></Vector2LineComponent>
+            <Vector2LineComponent label="Value" target={this.props.inputBlock} propertyName="value"></Vector2LineComponent>
         );
     }
 }

+ 3 - 3
nodeEditor/src/components/propertyTab/properties/vector3PropertyTabComponent.tsx

@@ -2,18 +2,18 @@
 import * as React from "react";
 import { GlobalState } from '../../../globalState';
 import { Vector3LineComponent } from '../../../sharedComponents/vector3LineComponent';
-import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 
 interface IVector3PropertyTabComponentProps {
     globalState: GlobalState;
-    connection: NodeMaterialConnectionPoint;
+    inputBlock: InputBlock;
 }
 
 export class Vector3PropertyTabComponent extends React.Component<IVector3PropertyTabComponentProps> {
 
     render() {
         return (
-            <Vector3LineComponent label="Value" target={this.props.connection} propertyName="value"></Vector3LineComponent>
+            <Vector3LineComponent label="Value" target={this.props.inputBlock} propertyName="value"></Vector3LineComponent>
         );
     }
 }

+ 3 - 0
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -61,6 +61,9 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                         <ButtonLineComponent label="Zoom to fit" onClick={() => {
                             this.props.globalState.onZoomToFitRequiredObservable.notifyObservers();
                         }} />
+                        <ButtonLineComponent label="Reorganize" onClick={() => {
+                            this.props.globalState.onReOrganizedRequiredObservable.notifyObservers();
+                        }} />
                     </LineContainerComponent>
                 </div>
             </div>

+ 1 - 0
nodeEditor/src/globalState.ts

@@ -13,5 +13,6 @@ export class GlobalState {
     onResetRequiredObservable = new Observable<void>();
     onUpdateRequiredObservable = new Observable<void>();
     onZoomToFitRequiredObservable = new Observable<void>();
+    onReOrganizedRequiredObservable = new Observable<void>();
     onLogRequiredObservable = new Observable<LogEntry>();
 }

+ 245 - 102
nodeEditor/src/graphEditor.tsx

@@ -2,11 +2,11 @@ import {
     DiagramEngine,
     DiagramModel,
     DiagramWidget,
-    MoveCanvasAction,
     LinkModel
 } from "storm-react-diagrams";
 
 import * as React from "react";
+import * as dagre from "dagre";
 import { GlobalState } from './globalState';
 
 import { GenericNodeFactory } from './components/diagram/generic/genericNodeFactory';
@@ -22,13 +22,31 @@ import { TextureNodeModel } from './components/diagram/texture/textureNodeModel'
 import { DefaultPortModel } from './components/diagram/defaultPortModel';
 import { InputNodeFactory } from './components/diagram/input/inputNodeFactory';
 import { InputNodeModel } from './components/diagram/input/inputNodeModel';
-import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/textureBlock';
-import { Vector2, Vector3, Vector4, Matrix, Color3, Color4 } from 'babylonjs/Maths/math';
+import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
 import { LogComponent, LogEntry } from './components/log/logComponent';
 import { LightBlock } from 'babylonjs/Materials/Node/Blocks/Dual/lightBlock';
 import { LightNodeModel } from './components/diagram/light/lightNodeModel';
 import { LightNodeFactory } from './components/diagram/light/lightNodeFactory';
 import { DataStorage } from './dataStorage';
+import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPointTypes';
+import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
+import { Nullable } from 'babylonjs/types';
+import { BonesBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/bonesBlock';
+import { InstancesBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/instancesBlock';
+import { MorphTargetsBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/morphTargetsBlock';
+import { AlphaTestBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/alphaTestBlock';
+import { ImageProcessingBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/imageProcessingBlock';
+import { RGBAMergerBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/rgbaMergerBlock';
+import { RGBASplitterBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/rgbaSplitterBlock';
+import { FogBlock } from 'babylonjs/Materials/Node/Blocks/Dual/fogBlock';
+import { VertexOutputBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/vertexOutputBlock';
+import { FragmentOutputBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/fragmentOutputBlock';
+import { AddBlock } from 'babylonjs/Materials/Node/Blocks/addBlock';
+import { ClampBlock } from 'babylonjs/Materials/Node/Blocks/clampBlock';
+import { MultiplyBlock } from 'babylonjs/Materials/Node/Blocks/multiplyBlock';
+import { Vector2TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector2TransformBlock';
+import { Vector3TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector3TransformBlock';
+import { Vector4TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector4TransformBlock';
 
 require("storm-react-diagrams/dist/style.min.css");
 require("./main.scss");
@@ -53,8 +71,7 @@ interface IGraphEditorProps {
 }
 
 export class NodeCreationOptions {
-    column: number;
-    nodeMaterialBlock?: NodeMaterialBlock;
+    nodeMaterialBlock: NodeMaterialBlock;
     type?: string;
     connection?: NodeMaterialConnectionPoint;
 }
@@ -69,49 +86,33 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
     public _toAdd: LinkModel[] | null = [];
 
     /**
-     * Current row/column position used when adding new nodes
-     */
-    private _rowPos = new Array<number>();
-
-    /**
      * Creates a node and recursivly creates its parent nodes from it's input
      * @param nodeMaterialBlock 
      */
     public createNodeFromObject(options: NodeCreationOptions) {
-        // Update rows/columns
-        if (this._rowPos[options.column] == undefined) {
-            this._rowPos[options.column] = 0;
-        } else {
-            this._rowPos[options.column]++;
-        }
-
         // Create new node in the graph
         var newNode: DefaultNodeModel;
         var filterInputs = [];
 
-        if (options.nodeMaterialBlock) {
-            if (options.nodeMaterialBlock instanceof TextureBlock) {
-                newNode = new TextureNodeModel();
-                filterInputs.push("uv");
-            } else if (options.nodeMaterialBlock instanceof LightBlock) {
-                newNode = new LightNodeModel();
-                filterInputs.push("worldPosition");
-                filterInputs.push("worldNormal");
-                filterInputs.push("cameraPosition");
-            } else {
-                newNode = new GenericNodeModel();
-            }
-
-            if (options.nodeMaterialBlock.isFinalMerger) {
-                this.props.globalState.nodeMaterial!.addOutputNode(options.nodeMaterialBlock);
-            }
-
-        } else {
+        if (options.nodeMaterialBlock instanceof TextureBlock) {
+            newNode = new TextureNodeModel();
+            filterInputs.push("uv");
+        } else if (options.nodeMaterialBlock instanceof LightBlock) {
+            newNode = new LightNodeModel();
+            filterInputs.push("worldPosition");
+            filterInputs.push("worldNormal");
+            filterInputs.push("cameraPosition");
+        } else if (options.nodeMaterialBlock instanceof InputBlock) {
             newNode = new InputNodeModel();
-            (newNode as InputNodeModel).connection = options.connection;
+        } else {
+            newNode = new GenericNodeModel();
+        }
+
+        if (options.nodeMaterialBlock.isFinalMerger) {
+            this.props.globalState.nodeMaterial!.addOutputNode(options.nodeMaterialBlock);
         }
+
         this._nodes.push(newNode)
-        newNode.setPosition(1600 - (300 * options.column), 210 * this._rowPos[options.column])
         this._model.addAll(newNode);
 
         if (options.nodeMaterialBlock) {
@@ -155,7 +156,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         });
 
         this.props.globalState.onResetRequiredObservable.add(() => {
-            this._rowPos = [];
             this.build();
             if (this.props.globalState.nodeMaterial) {
                 this.buildMaterial();
@@ -170,9 +170,67 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             this._engine.zoomToFit();
         });
 
+        this.props.globalState.onReOrganizedRequiredObservable.add(() => {
+            this.reOrganize();
+        })
+
         this.build();
     }
 
+    distributeGraph() {
+        let nodes = this.mapElements();
+        let edges = this.mapEdges();
+        let graph = new dagre.graphlib.Graph();
+        graph.setGraph({});
+        graph.setDefaultEdgeLabel(() => ({}));
+        graph.graph().rankdir = "LR";
+        //add elements to dagre graph
+        nodes.forEach(node => {
+            graph.setNode(node.id, node.metadata);
+        });
+        edges.forEach(edge => {
+            if (edge.from && edge.to) {
+                graph.setEdge(edge.from.id, edge.to.id);
+            }
+        });
+        //auto-distribute
+        dagre.layout(graph);
+        return graph.nodes().map(node => graph.node(node));
+    }
+
+    mapElements() {
+        let output = [];
+
+        // dagre compatible format
+        for (var nodeName in this._model.nodes) {
+            let node = this._model.nodes[nodeName];
+            let size = {
+                width: node.width,
+                height: node.height
+            };
+            output.push({ id: node.id, metadata: { ...size, id: node.id } });
+        }
+
+        return output;
+    }
+
+    mapEdges() {
+        // returns links which connects nodes
+        // we check are there both from and to nodes in the model. Sometimes links can be detached
+        let output = [];
+
+        for (var linkName in this._model.links) {
+            let link = this._model.links[linkName];
+
+            output.push({
+                from: link.sourcePort!.parent,
+                to: link.targetPort!.parent
+            });
+        }
+
+        return output;
+    }
+
     buildMaterial() {
         if (!this.props.globalState.nodeMaterial) {
             return;
@@ -201,6 +259,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                     if (targetBlock && targetBlock.isFinalMerger) {
                         this.props.globalState.nodeMaterial!.removeOutputNode(targetBlock);
                     }
+
+                    this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
                 }
             },
             linksUpdated: (e) => {
@@ -215,22 +275,15 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                                 link.output.connection.disconnectFrom(link.input.connection)
                                 link.input.syncWithNodeMaterialConnectionPoint(link.input.connection)
                                 link.output.syncWithNodeMaterialConnectionPoint(link.output.connection)
-                            } else {
-                                let inputNode = link.output.parent as InputNodeModel
-                                inputNode.connection = undefined;
-
-                                if (link.input.connection.value) {
-                                    inputNode.ports[link.output.name].defaultValue = link.input.connection.value;
-                                    link.input.connection.value = null;
-                                }
                             }
                         }
                     }
+                    this.forceUpdate();
+                    return;
                 }
 
                 e.link.addListener({
                     sourcePortChanged: () => {
-                        console.log("port change")
                     },
                     targetPortChanged: () => {
                         // Link is created with a target port
@@ -238,15 +291,18 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
 
                         if (link) {
                             if (link.output.connection && link.input.connection) {
-                                link.output.connection.connectTo(link.input.connection)
-                            } else if (link.input.connection) {
-                                if (!link.output.connection) { // Input Node
-                                    let name = link.output.name;
-                                    link.output.syncWithNodeMaterialConnectionPoint(link.input.connection);
-                                    link.output.name = name;
-                                    (link.output.getNode() as InputNodeModel).connection = link.output.connection!;
-                                    link.input.connection.value = link.output.defaultValue;
+                                // Disconnect previous connection
+                                for (var key in link.input.links) {
+                                    let other = link.input.links[key];
+
+                                    if (other.getSourcePort() !== link.output) {
+                                        other.remove();
+                                    }
                                 }
+
+                                link.output.connection.connectTo(link.input.connection);
+
+                                this.forceUpdate();
                             }
                             if (this.props.globalState.nodeMaterial) {
                                 this.buildMaterial();
@@ -261,10 +317,10 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         if (this.props.globalState.nodeMaterial) {
             var material: any = this.props.globalState.nodeMaterial;
             material._vertexOutputNodes.forEach((n: any) => {
-                this.createNodeFromObject({ column: 0, nodeMaterialBlock: n });
+                this.createNodeFromObject({ nodeMaterialBlock: n });
             })
             material._fragmentOutputNodes.forEach((n: any) => {
-                this.createNodeFromObject({ column: 0, nodeMaterialBlock: n });
+                this.createNodeFromObject({ nodeMaterialBlock: n });
             })
         }
 
@@ -275,58 +331,58 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             }
             this._toAdd = null;
             this._engine.setDiagramModel(this._model);
-            this.forceUpdate();
-        }, 550);
-    }
-
-    addNodeFromClass(ObjectClass: typeof NodeMaterialBlock) {
-        var block = new ObjectClass(ObjectClass.prototype.getClassName())
-        var localNode = this.createNodeFromObject({ column: 0, nodeMaterialBlock: block })
-        var widget = (this.refs["test"] as DiagramWidget);
 
-        this.forceUpdate();
+            this.forceUpdate();
 
-        // This is needed to fix link offsets when created, (eg. create a fog block)
-        // Todo figure out how to correct this without this
-        setTimeout(() => {
-            widget.startFiringAction(new MoveCanvasAction(1, 0, this._model));
+            this.reOrganize();
         }, 500);
-
-        return localNode;
     }
 
-    addValueNode(type: string, column = 0, connection?: NodeMaterialConnectionPoint) {
-        var localNode = this.createNodeFromObject({ column: column, type: type, connection: connection })
-        var outPort = new DefaultPortModel(type, "output");
-
-        localNode.addPort(outPort);
+    reOrganize() {
+        let nodes = this.distributeGraph();
+        nodes.forEach(node => {
+            for (var nodeName in this._model.nodes) {
+                let modelNode = this._model.nodes[nodeName];
 
-        if (!connection) {
-            switch (type) {
-                case "Float":
-                    outPort.defaultValue = 0;
-                    break;
-                case "Vector2":
-                    outPort.defaultValue = Vector2.Zero();
-                    break;
-                case "Vector3":
-                    outPort.defaultValue = Vector3.Zero();
-                    break;
-                case "Vector4":
-                    outPort.defaultValue = Vector4.Zero();
-                    break;
-                case "Matrix":
-                    outPort.defaultValue = Matrix.Identity();
-                    break;
-                case "Color3":
-                    outPort.defaultValue = Color3.White();
-                    break;
-                case "Color4":
-                    outPort.defaultValue = new Color4(1, 1, 1, 1);
-                    break;
+                if (modelNode.id === node.id) {
+                    modelNode.setPosition(node.x - node.width / 2, node.y - node.height / 2);
+                    return;
+                }
             }
+        });
+        this.forceUpdate();
+    }
+
+    addValueNode(type: string) {
+        let nodeType: NodeMaterialBlockConnectionPointTypes = NodeMaterialBlockConnectionPointTypes.Vector3;
+        switch (type) {
+            case "Float":
+                nodeType = NodeMaterialBlockConnectionPointTypes.Float;
+                break;
+            case "Vector2":
+                nodeType = NodeMaterialBlockConnectionPointTypes.Vector2;
+                break;
+            case "Vector3":
+                nodeType = NodeMaterialBlockConnectionPointTypes.Vector3;
+                break;
+            case "Vector4":
+                nodeType = NodeMaterialBlockConnectionPointTypes.Vector4;
+                break;
+            case "Matrix":
+                nodeType = NodeMaterialBlockConnectionPointTypes.Matrix;
+                break;
+            case "Color3":
+                nodeType = NodeMaterialBlockConnectionPointTypes.Color3;
+                break;
+            case "Color4":
+                nodeType = NodeMaterialBlockConnectionPointTypes.Color4;
+                break;
         }
 
+        let newInputBlock = new InputBlock(type, undefined, nodeType);
+        newInputBlock.setDefaultValue();
+        var localNode = this.createNodeFromObject({ type: type, nodeMaterialBlock: newInputBlock })
+
         return localNode;
     }
 
@@ -375,6 +431,84 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         return `${this._leftWidth}px 4px calc(100% - ${this._leftWidth + 8 + this._rightWidth}px) 4px ${this._rightWidth}px`;
     }
 
+    emitNewBlock(event: React.DragEvent<HTMLDivElement>) {
+        var data = event.dataTransfer.getData("babylonjs-material-node") as string;
+
+        if (data.indexOf("Block") === -1) {
+            this.addValueNode(data);
+        } else {
+            let block: Nullable<NodeMaterialBlock> = null;
+
+            switch (data) {
+                case "BonesBlock":
+                    block = new BonesBlock("Bones");
+                    break;
+                case "InstancesBlock":
+                    block = new InstancesBlock("Instances");
+                    break;
+                case "MorphTargetsBlock":
+                    block = new MorphTargetsBlock("MorphTargets");
+                    break;
+                case "AlphaTestBlock":
+                    block = new AlphaTestBlock("AlphaTest");
+                    break;
+                case "ImageProcessingBlock":
+                    block = new ImageProcessingBlock("ImageProcessing");
+                    break;
+                case "RGBAMergerBlock":
+                    block = new RGBAMergerBlock("RGBAMerger");
+                    break;
+                case "RGBASplitterBlock":
+                    block = new RGBASplitterBlock("RGBASplitter");
+                    break;
+                case "TextureBlock":
+                    block = new TextureBlock("Texture");
+                    break;
+                case "LightBlock":
+                    block = new LightBlock("Light");
+                    break;
+                case "FogBlock":
+                    block = new FogBlock("Fog");
+                    break;
+                case "VertexOutputBlock":
+                    block = new VertexOutputBlock("VertexOutput");
+                    break;
+                case "FragmentOutputBlock":
+                    block = new FragmentOutputBlock("FragmentOutput");
+                    break;
+                case "AddBlock":
+                    block = new AddBlock("Add");
+                    break;
+                case "ClampBlock":
+                    block = new ClampBlock("Clamp");
+                    break;
+                case "MultiplyBlock":
+                    block = new MultiplyBlock("Multiply");
+                    break;
+                case "Vector2TransformBlock":
+                    block = new Vector2TransformBlock("Vector2Transform");
+                    break;
+                case "Vector3TransformBlock":
+                    block = new Vector3TransformBlock("Vector3Transform");
+                    break;
+                case "Vector4TransformBlock":
+                    block = new Vector4TransformBlock("Vector4Transform");
+                    break;
+            }
+
+            if (block) {
+                let nodeModel = this.createNodeFromObject({ nodeMaterialBlock: block });
+                const zoomLevel = this._engine.diagramModel.getZoomLevel() / 100.0;
+
+                let x = (event.clientX - event.currentTarget.offsetLeft - this._engine.diagramModel.getOffsetX()) / zoomLevel;
+                let y = (event.clientY - event.currentTarget.offsetTop - this._engine.diagramModel.getOffsetY()) / zoomLevel;
+                nodeModel.setPosition(x, y);
+            }
+        };
+
+        this.forceUpdate();
+    }
+
     render() {
         return (
             <Portal globalState={this.props.globalState}>
@@ -384,7 +518,7 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                     }
                 }>
                     {/* Node creation menu */}
-                    <NodeListComponent globalState={this.props.globalState} onAddValueNode={b => this.addValueNode(b)} onAddNodeFromClass={b => this.addNodeFromClass(b)} />
+                    <NodeListComponent globalState={this.props.globalState} />
 
                     <div id="leftGrab"
                         onPointerDown={evt => this.onPointerDown(evt)}
@@ -393,7 +527,16 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                     ></div>
 
                     {/* The node graph diagram */}
-                    <DiagramWidget deleteKeys={[46]} ref={"test"} inverseZoom={true} className="diagram-container" diagramEngine={this._engine} maxNumberPointsPerLink={0} />
+                    <div className="diagram-container"
+                        onDrop={event => {
+                            this.emitNewBlock(event);
+                        }}
+                        onDragOver={event => {
+                            event.preventDefault();
+                        }}
+                    >
+                        <DiagramWidget className="diagram" deleteKeys={[46]} ref={"test"} inverseZoom={true} diagramEngine={this._engine} maxNumberPointsPerLink={0} />
+                    </div>
 
                     <div id="rightGrab"
                         onPointerDown={evt => this.onPointerDown(evt)}

+ 7 - 0
nodeEditor/src/main.scss

@@ -28,6 +28,13 @@
     grid-row: 1;
     grid-column: 3;
     background: #222222;
+    width: 100%;
+    height: 100%;
+
+    .diagram {
+        width: 100%;
+        height: 100%;
+    }
 }
 
 #propertyTab {

+ 23 - 0
nodeEditor/src/sharedComponents/draggableLineComponent.tsx

@@ -0,0 +1,23 @@
+import * as React from "react";
+
+export interface IButtonLineComponentProps {
+    data: string;
+}
+
+export class DraggableLineComponent extends React.Component<IButtonLineComponentProps> {
+    constructor(props: IButtonLineComponentProps) {
+        super(props);
+    }
+
+    render() {
+        return (
+            <div className="draggableLine"
+                draggable={true}
+                onDragStart={event => {
+                    event.dataTransfer.setData("babylonjs-material-node", this.props.data);
+                }}>
+                {this.props.data}
+            </div>
+        );
+    }
+}

+ 3 - 2
package.json

@@ -35,7 +35,6 @@
     },
     "readme": "Babylon.js is a 3D engine based on webgl and javascript",
     "readmeFilename": "README.md",
-    "dependencies": {},
     "devDependencies": {
         "@fortawesome/fontawesome-svg-core": "~1.2.8",
         "@fortawesome/free-regular-svg-icons": "~5.4.1",
@@ -47,6 +46,7 @@
         "@types/react": "~16.7.3",
         "@types/react-dom": "~16.0.9",
         "@types/sinon": "^4.1.3",
+        "@types/dagre": "^0.7.42",
         "ajv": "^6.9.1",
         "awesome-typescript-loader": "^5.2.1",
         "base64-font-loader": "0.0.4",
@@ -87,10 +87,11 @@
         "react-contextmenu": "~2.10.0",
         "react-dom": "~16.6.3",
         "sass-loader": "^7.1.0",
-        "shelljs": "^0.7.8",
+        "shelljs": "^0.8.3",
         "sinon": "^6.1.4",
         "split.js": "^1.5.9",
         "storm-react-diagrams": "^5.2.1",
+        "dagre": "0.8.4",
         "style-loader": "^0.21.0",
         "through2": "~2.0.3",
         "ts-loader": "^5.2.1",

+ 2 - 1
serializers/src/glTF/2.0/glTFMaterialExporter.ts

@@ -1209,6 +1209,7 @@ export class _GLTFMaterialExporter {
 
         let extension = mimeType === ImageMimeType.JPEG ? '.jpeg' : '.png';
         let textureName = baseTextureName + extension;
+        let originalTextureName = textureName;
         if (textureName in imageData) {
             textureName = `${baseTextureName}_${Tools.RandomId()}${extension}`;
         }
@@ -1221,7 +1222,7 @@ export class _GLTFMaterialExporter {
             };
             let foundIndex: Nullable<number> = null;
             for (let i = 0; i < images.length; ++i) {
-                if (images[i].uri === textureName) {
+                if (images[i].uri === originalTextureName) {
                     foundIndex = i;
                     break;
                 }

+ 4 - 0
src/Cameras/XR/index.ts

@@ -2,5 +2,9 @@ export * from "./webXRCamera";
 export * from "./webXREnterExitUI";
 export * from "./webXRExperienceHelper";
 export * from "./webXRInput";
+export * from "./webXRControllerTeleportation";
+export * from "./webXRControllerPointerSelection";
+export * from "./webXRControllerModelLoader";
+export * from "./webXRController";
 export * from "./webXRManagedOutputCanvas";
 export * from "./webXRSessionManager";

+ 116 - 0
src/Cameras/XR/webXRController.ts

@@ -0,0 +1,116 @@
+import { Nullable } from "../../types";
+import { Observable } from "../../Misc/observable";
+import { AbstractMesh } from "../../Meshes/abstractMesh";
+import { Matrix, Quaternion, Vector3 } from '../../Maths/math';
+import { Ray } from '../../Culling/ray';
+import { Scene } from '../../scene';
+/**
+ * Represents an XR input
+ */
+export class WebXRController {
+    /**
+     * Represents the part of the controller that is held. This may not exist if the controller is the head mounted display itself, if thats the case only the pointer from the head will be availible
+     */
+    public grip?: AbstractMesh;
+    /**
+     * Pointer which can be used to select objects or attach a visible laser to
+     */
+    public pointer: AbstractMesh;
+
+    /**
+     * Event that fires when the controller is removed/disposed
+     */
+    public onDisposeObservable = new Observable<{}>();
+
+    private _tmpMatrix = new Matrix();
+    private _tmpQuaternion = new Quaternion();
+    private _tmpVector = new Vector3();
+
+    /**
+     * Creates the controller
+     * @see https://doc.babylonjs.com/how_to/webxr
+     * @param scene the scene which the controller should be associated to
+     * @param inputSource the underlying input source for the controller
+     * @param parentContainer parent that the controller meshes should be children of
+     */
+    constructor(
+        private scene: Scene,
+        /** The underlying input source for the controller  */
+        public inputSource: XRInputSource,
+        private parentContainer: Nullable<AbstractMesh> = null)
+    {
+        this.pointer = new AbstractMesh("controllerPointer", scene);
+        if (parentContainer) {
+            parentContainer.addChild(this.pointer);
+        }
+
+        if (this.inputSource.gripSpace) {
+            this.grip = new AbstractMesh("controllerGrip", this.scene);
+            if (this.parentContainer) {
+                this.parentContainer.addChild(this.grip);
+            }
+        }
+    }
+
+    /**
+     * Updates the controller pose based on the given XRFrame
+     * @param xrFrame xr frame to update the pose with
+     * @param referenceSpace reference space to use
+     */
+    public updateFromXRFrame(xrFrame: XRFrame, referenceSpace: XRReferenceSpace) {
+        let pose = xrFrame.getPose(this.inputSource.targetRaySpace, referenceSpace);
+
+        // Update the pointer mesh
+        if (pose) {
+            Matrix.FromFloat32ArrayToRefScaled(pose.transform.matrix, 0, 1, this._tmpMatrix);
+            if (!this.pointer.getScene().useRightHandedSystem) {
+                this._tmpMatrix.toggleModelMatrixHandInPlace();
+            }
+            if (!this.pointer.rotationQuaternion) {
+                this.pointer.rotationQuaternion = new Quaternion();
+            }
+            this._tmpMatrix.decompose(this.pointer.scaling, this.pointer.rotationQuaternion!, this.pointer.position);
+        }
+
+        // Update the grip mesh if it exists
+        if (this.inputSource.gripSpace && this.grip) {
+            let pose = xrFrame.getPose(this.inputSource.gripSpace, referenceSpace);
+            if (pose) {
+                Matrix.FromFloat32ArrayToRefScaled(pose.transform.matrix, 0, 1, this._tmpMatrix);
+                if (!this.grip.getScene().useRightHandedSystem) {
+                    this._tmpMatrix.toggleModelMatrixHandInPlace();
+                }
+                if (!this.grip.rotationQuaternion) {
+                    this.grip.rotationQuaternion = new Quaternion();
+                }
+                this._tmpMatrix.decompose(this.grip.scaling, this.grip.rotationQuaternion!, this.grip.position);
+            }
+        }
+    }
+
+    /**
+     * Gets a world space ray coming from the controller
+     * @param result the resulting ray
+     */
+    public getWorldPointerRayToRef(result: Ray) {
+        // Force update to ensure picked point is synced with ray
+        let worldMatrix = this.pointer.computeWorldMatrix(true);
+        worldMatrix.decompose(undefined, this._tmpQuaternion, undefined);
+        this._tmpVector.set(0, 0, 1);
+        this._tmpVector.rotateByQuaternionToRef(this._tmpQuaternion, this._tmpVector);
+        result.origin = this.pointer.absolutePosition;
+        result.direction.copyFrom(this._tmpVector);
+        result.length = 1000;
+    }
+
+    /**
+     * Disposes of the object
+     */
+    dispose() {
+        if (this.grip) {
+            this.grip.dispose();
+        }
+        this.pointer.dispose();
+        this.onDisposeObservable.notifyObservers({});
+    }
+}

+ 59 - 0
src/Cameras/XR/webXRControllerModelLoader.ts

@@ -0,0 +1,59 @@
+import { Quaternion } from '../../Maths/math';
+import { WindowsMotionController } from '../../Gamepads/Controllers/windowsMotionController';
+import { OculusTouchController } from '../../Gamepads/Controllers/oculusTouchController';
+import { WebXRInput } from './webXRInput';
+import { ViveController } from '../../Gamepads/Controllers/viveController';
+
+/**
+ * Loads a controller model and adds it as a child of the WebXRControllers grip when the controller is created
+ */
+export class WebXRControllerModelLoader {
+    /**
+     * Creates the WebXRControllerModelLoader
+     * @param input xr input that creates the controllers
+     */
+    constructor(input: WebXRInput) {
+        input.onControllerAddedObservable.add((c) => {
+            if (c.inputSource.gamepad && c.inputSource.gamepad.id === "htc-vive") {
+                let controllerModel = new ViveController(c.inputSource.gamepad);
+                controllerModel.hand = c.inputSource.handedness;
+                controllerModel.isXR = true;
+                controllerModel.initControllerMesh(c.grip!.getScene(), (m) => {
+                    m.isPickable = false;
+                    m.getChildMeshes(false).forEach((m) => {
+                        m.isPickable = false;
+                    });
+                    controllerModel.mesh!.parent = c.grip!;
+                    controllerModel.mesh!.rotationQuaternion = Quaternion.FromEulerAngles(0, Math.PI, 0);
+                });
+            } else if (c.inputSource.gamepad && c.inputSource.gamepad.id === "oculus-touch") {
+                let controllerModel = new OculusTouchController(c.inputSource.gamepad);
+                controllerModel.hand = c.inputSource.handedness;
+                controllerModel.isXR = true;
+                controllerModel.initControllerMesh(c.grip!.getScene(), (m) => {
+                    controllerModel.mesh!.parent = c.grip!;
+                    controllerModel.mesh!.rotationQuaternion = Quaternion.FromEulerAngles(0, Math.PI, 0);
+                    controllerModel.mesh!.position.y = 0.034;
+                    controllerModel.mesh!.position.z = 0.052;
+                });
+            }else if (c.inputSource.gamepad && c.inputSource.gamepad.id === "oculus-quest") {
+                OculusTouchController._IsQuest = true;
+                let controllerModel = new OculusTouchController(c.inputSource.gamepad);
+                controllerModel.hand = c.inputSource.handedness;
+                controllerModel.isXR = true;
+                controllerModel.initControllerMesh(c.grip!.getScene(), (m) => {
+                    controllerModel.mesh!.parent = c.grip!;
+                    controllerModel.mesh!.rotationQuaternion = Quaternion.FromEulerAngles(Math.PI / -4, Math.PI, 0);
+                });
+            }else {
+                let controllerModel = new WindowsMotionController(c.inputSource.gamepad);
+                controllerModel.hand = c.inputSource.handedness;
+                controllerModel.isXR = true;
+                controllerModel.initControllerMesh(c.grip!.getScene(), (m) => {
+                    controllerModel.mesh!.parent = c.grip!;
+                    controllerModel.mesh!.rotationQuaternion = Quaternion.FromEulerAngles(0, Math.PI, 0);
+                });
+            }
+        });
+    }
+}

+ 119 - 0
src/Cameras/XR/webXRControllerPointerSelection.ts

@@ -0,0 +1,119 @@
+import { Nullable } from "../../types";
+import {  Vector3, Color3, Axis } from '../../Maths/math';
+import { Mesh } from '../../Meshes/mesh';
+import { Ray } from '../../Culling/ray';
+import { StandardMaterial } from '../../Materials/standardMaterial';
+import { WebXRInput } from './webXRInput';
+
+/**
+ * Handles pointer input automatically for the pointer of XR controllers
+ */
+export class WebXRControllerPointerSelection {
+    private static _idCounter = 0;
+    private _tmpRay = new Ray(new Vector3(), new Vector3());
+
+    /**
+     * Creates a WebXRControllerPointerSelection
+     * @param input input manager to setup pointer selection
+     */
+    constructor(input: WebXRInput) {
+        input.onControllerAddedObservable.add((controller) => {
+            let scene = controller.pointer.getScene();
+
+            let laserPointer: Mesh;
+            let cursorMesh: Mesh;
+            let triggerDown = false;
+            let id: number;
+            id = WebXRControllerPointerSelection._idCounter++;
+
+            // Create a laser pointer for the XR controller
+            laserPointer = Mesh.CreateCylinder("laserPointer", 1, 0.0002, 0.004, 20, 1, scene, false);
+            laserPointer.parent = controller.pointer;
+            let laserPointerMaterial = new StandardMaterial("laserPointerMat", scene);
+            laserPointerMaterial.emissiveColor = new Color3(0.7, 0.7, 0.7);
+            laserPointerMaterial.alpha = 0.6;
+            laserPointer.material = laserPointerMaterial;
+            laserPointer.rotation.x = Math.PI / 2;
+            this._updatePointerDistance(laserPointer, 1);
+            laserPointer.isPickable = false;
+
+            // Create a gaze tracker for the  XR controlelr
+            cursorMesh = Mesh.CreateTorus("gazeTracker", 0.0035 * 3, 0.0025 * 3, 20, scene, false);
+            cursorMesh.bakeCurrentTransformIntoVertices();
+            cursorMesh.isPickable = false;
+            cursorMesh.isVisible = false;
+            let targetMat = new StandardMaterial("targetMat", scene);
+            targetMat.specularColor = Color3.Black();
+            targetMat.emissiveColor = new Color3(0.7, 0.7, 0.7);
+            targetMat.backFaceCulling = false;
+            cursorMesh.material = targetMat;
+
+            let renderObserver = scene.onBeforeRenderObservable.add(() => {
+                // Every frame check collisions/input
+                controller.getWorldPointerRayToRef(this._tmpRay);
+                let pick = scene.pickWithRay(this._tmpRay);
+                if (pick) {
+                    if (controller.inputSource.gamepad && controller.inputSource.gamepad.buttons[0] && controller.inputSource.gamepad.buttons[0].value > 0.7) {
+                        if (!triggerDown) {
+                            scene.simulatePointerDown(pick, { pointerId: id });
+                        }
+                        triggerDown = true;
+                    }else {
+                        if (triggerDown) {
+                            scene.simulatePointerUp(pick, { pointerId: id });
+                        }
+                        triggerDown = false;
+                    }
+                    scene.simulatePointerMove(pick, { pointerId: id });
+                }
+
+                if (pick && pick.pickedPoint && pick.hit) {
+                    // Update laser state
+                    this._updatePointerDistance(laserPointer, pick.distance);
+
+                    // Update cursor state
+                    cursorMesh.position.copyFrom(pick.pickedPoint);
+                    cursorMesh.scaling.x = Math.sqrt(pick.distance);
+                    cursorMesh.scaling.y = Math.sqrt(pick.distance);
+                    cursorMesh.scaling.z = Math.sqrt(pick.distance);
+
+                    // To avoid z-fighting
+                    let pickNormal = this._convertNormalToDirectionOfRay(pick.getNormal(), this._tmpRay);
+                    let deltaFighting = 0.002;
+                    cursorMesh.position.copyFrom(pick.pickedPoint);
+                    if (pickNormal) {
+                        let axis1 = Vector3.Cross(Axis.Y, pickNormal);
+                        let axis2 = Vector3.Cross(pickNormal, axis1);
+                        Vector3.RotationFromAxisToRef(axis2, pickNormal, axis1, cursorMesh.rotation);
+                        cursorMesh.position.addInPlace(pickNormal.scale(deltaFighting));
+                    }
+                    cursorMesh.isVisible = true;
+                }else {
+                    cursorMesh.isVisible = false;
+                }
+            });
+
+            controller.onDisposeObservable.addOnce(() => {
+                laserPointer.dispose();
+                cursorMesh.dispose();
+
+                scene.onBeforeRenderObservable.remove(renderObserver);
+            });
+        });
+    }
+
+    private _convertNormalToDirectionOfRay(normal: Nullable<Vector3>, ray: Ray) {
+        if (normal) {
+            let angle = Math.acos(Vector3.Dot(normal, ray.direction));
+            if (angle < Math.PI / 2) {
+                normal.scaleInPlace(-1);
+            }
+        }
+        return normal;
+    }
+
+    private _updatePointerDistance(_laserPointer: Mesh, distance: number = 100) {
+        _laserPointer.scaling.y = distance;
+        _laserPointer.position.z = distance / 2;
+    }
+}

+ 181 - 0
src/Cameras/XR/webXRControllerTeleportation.ts

@@ -0,0 +1,181 @@
+import { AbstractMesh } from "../../Meshes/abstractMesh";
+import { Quaternion, Vector3 } from '../../Maths/math';
+import { Mesh } from '../../Meshes/mesh';
+import { Ray } from '../../Culling/ray';
+import { StandardMaterial } from '../../Materials/standardMaterial';
+import { DynamicTexture } from '../../Materials/Textures/dynamicTexture';
+import { EasingFunction, SineEase } from '../../Animations/easing';
+import { Animation } from '../../Animations/animation';
+import { WebXRInput } from './webXRInput';
+
+/**
+ * Enables teleportation
+ */
+export class WebXRControllerTeleportation {
+    private _teleportationFillColor: string = "#444444";
+    private _teleportationBorderColor: string = "#FFFFFF";
+
+    private _tmpRay = new Ray(new Vector3(), new Vector3());
+    private _tmpVector = new Vector3();
+
+    /**
+     * Creates a WebXRControllerTeleportation
+     * @param input input manager to add teleportation to
+     * @param floorMeshes floormeshes which can be teleported to
+     */
+    constructor(input: WebXRInput, floorMeshes: Array<AbstractMesh> = []) {
+        input.onControllerAddedObservable.add((c) => {
+            let scene = c.pointer.getScene();
+
+            let forwardReadyToTeleport = false;
+            let backwardReadyToTeleport = false;
+            let leftReadyToTeleport = false;
+            let rightReadyToTeleport = false;
+
+            // Teleport target abd it's animation
+            let teleportationTarget = Mesh.CreateGround("teleportationTarget", 2, 2, 2, scene);
+            teleportationTarget.isPickable = false;
+            let length = 512;
+            let dynamicTexture = new DynamicTexture("DynamicTexture", length, scene, true);
+            dynamicTexture.hasAlpha = true;
+            let context = dynamicTexture.getContext();
+            let centerX = length / 2;
+            let centerY = length / 2;
+            let radius = 200;
+            context.beginPath();
+            context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
+            context.fillStyle = this._teleportationFillColor;
+            context.fill();
+            context.lineWidth = 10;
+            context.strokeStyle = this._teleportationBorderColor;
+            context.stroke();
+            context.closePath();
+            dynamicTexture.update();
+            let teleportationCircleMaterial = new StandardMaterial("TextPlaneMaterial", scene);
+            teleportationCircleMaterial.diffuseTexture = dynamicTexture;
+            teleportationTarget.material = teleportationCircleMaterial;
+            let torus = Mesh.CreateTorus("torusTeleportation", 0.75, 0.1, 25, scene, false);
+            torus.isPickable = false;
+            torus.parent = teleportationTarget;
+            let animationInnerCircle = new Animation("animationInnerCircle", "position.y", 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
+            let keys = [];
+            keys.push({
+                frame: 0,
+                value: 0
+            });
+            keys.push({
+                frame: 30,
+                value: 0.4
+            });
+            keys.push({
+                frame: 60,
+                value: 0
+            });
+            animationInnerCircle.setKeys(keys);
+            let easingFunction = new SineEase();
+            easingFunction.setEasingMode(EasingFunction.EASINGMODE_EASEINOUT);
+            animationInnerCircle.setEasingFunction(easingFunction);
+            torus.animations = [];
+            torus.animations.push(animationInnerCircle);
+            scene.beginAnimation(torus, 0, 60, true);
+
+            // Handle user input on every frame
+            let renderObserver = scene.onBeforeRenderObservable.add(() => {
+                // Move the teleportationTarget to where the user is targetting to teleport to
+                if (forwardReadyToTeleport) {
+                    c.getWorldPointerRayToRef(this._tmpRay);
+                    let pick = scene.pickWithRay(this._tmpRay, (o) => {
+                        return floorMeshes.indexOf(o) !== -1;
+                    });
+                    if (pick && pick.pickedPoint) {
+                        // To avoid z-fighting
+                        teleportationTarget.position.copyFrom(pick.pickedPoint);
+                        teleportationTarget.position.y += 0.002;
+                    }
+                    teleportationTarget.isVisible = true;
+                    (<Mesh>teleportationTarget.getChildren()[0]).isVisible = true;
+                }else {
+                    teleportationTarget.isVisible = false;
+                    (<Mesh>teleportationTarget.getChildren()[0]).isVisible = false;
+                }
+
+                if (c.inputSource.gamepad) {
+                    if (c.inputSource.gamepad.axes[1] !== undefined) {
+                        // Forward teleportation
+                        if (c.inputSource.gamepad.axes[1] < -0.7) {
+                            forwardReadyToTeleport = true;
+                        }else {
+                            if (forwardReadyToTeleport) {
+                                // Teleport the users feet to where they targetted
+                                this._tmpVector.copyFrom(teleportationTarget.position);
+                                this._tmpVector.y += input.baseExperience.camera.position.y;
+                                input.baseExperience.setPositionOfCameraUsingContainer(this._tmpVector);
+                            }
+                            forwardReadyToTeleport = false;
+                        }
+
+                        // Backward teleportation
+                        if (c.inputSource.gamepad.axes[1] > 0.7) {
+                            backwardReadyToTeleport = true;
+                        }else {
+                            if (backwardReadyToTeleport) {
+                                // Cast a ray down from behind the user
+                                let camMat = input.baseExperience.camera.computeWorldMatrix();
+                                let q = new Quaternion();
+                                camMat.decompose(undefined, q, this._tmpRay.origin);
+                                this._tmpVector.set(0, 0, -1);
+                                this._tmpVector.rotateByQuaternionToRef(q, this._tmpVector);
+                                this._tmpVector.y = 0;
+                                this._tmpVector.normalize();
+                                this._tmpVector.y = -1.5;
+                                this._tmpVector.normalize();
+                                this._tmpRay.direction.copyFrom(this._tmpVector);
+                                let pick = scene.pickWithRay(this._tmpRay, (o) => {
+                                    return floorMeshes.indexOf(o) !== -1;
+                                });
+
+                                if (pick && pick.pickedPoint) {
+                                    // Teleport the users feet to where they targetted
+                                    this._tmpVector.copyFrom(pick.pickedPoint);
+                                    this._tmpVector.y += input.baseExperience.camera.position.y;
+                                    input.baseExperience.setPositionOfCameraUsingContainer(this._tmpVector);
+                                }
+                            }
+                            backwardReadyToTeleport = false;
+                        }
+                    }
+
+                    if (c.inputSource.gamepad.axes[0] !== undefined) {
+                        if (c.inputSource.gamepad.axes[0] < -0.7) {
+                            leftReadyToTeleport = true;
+                        }else {
+                            if (leftReadyToTeleport) {
+                                input.baseExperience.rotateCameraByQuaternionUsingContainer(Quaternion.FromEulerAngles(0, -Math.PI / 4, 0));
+                            }
+                            leftReadyToTeleport = false;
+                        }
+                        if (c.inputSource.gamepad.axes[0] > 0.7) {
+                            rightReadyToTeleport = true;
+                        }else {
+                            if (rightReadyToTeleport) {
+                                input.baseExperience.rotateCameraByQuaternionUsingContainer(Quaternion.FromEulerAngles(0, Math.PI / 4, 0));
+                            }
+                            rightReadyToTeleport = false;
+                        }
+                    }
+
+                }
+            });
+
+            c.onDisposeObservable.addOnce(() => {
+                teleportationTarget.dispose();
+                dynamicTexture.dispose();
+                teleportationCircleMaterial.dispose();
+                torus.dispose();
+
+                scene.onBeforeRenderObservable.remove(renderObserver);
+            });
+        });
+    }
+
+}

+ 104 - 0
src/Cameras/XR/webXRDefaultExperience.ts

@@ -0,0 +1,104 @@
+import { WebXRExperienceHelper } from "./webXRExperienceHelper";
+import { Scene } from '../../scene';
+import { WebXRInput } from './webXRInput';
+import { WebXRControllerModelLoader } from './webXRControllerModelLoader';
+import { WebXRControllerPointerSelection } from './webXRControllerPointerSelection';
+import { WebXRControllerTeleportation } from './webXRControllerTeleportation';
+import { WebXRManagedOutputCanvas } from './webXRManagedOutputCanvas';
+import { WebXREnterExitUI } from './webXREnterExitUI';
+import { AbstractMesh } from '../../Meshes/abstractMesh';
+/**
+ * Options for the default xr helper
+ */
+export class WebXRDefaultExperienceOptions {
+    /**
+     * Floor meshes that should be used for teleporting
+     */
+    public floorMeshes: Array<AbstractMesh>;
+}
+
+/**
+ * Default experience which provides a similar setup to the previous webVRExperience
+ */
+export class WebXRDefaultExperience {
+    /**
+     * Base experience
+     */
+    public baseExperience: WebXRExperienceHelper;
+    /**
+     * Input experience extension
+     */
+    public input: WebXRInput;
+    /**
+     * Loads the controller models
+     */
+    public controllerModelLoader: WebXRControllerModelLoader;
+    /**
+     * Enables laser pointer and selection
+     */
+    public pointerSelection: WebXRControllerPointerSelection;
+    /**
+     * Enables teleportation
+     */
+    public teleportation: WebXRControllerTeleportation;
+    /**
+     * Enables ui for enetering/exiting xr
+     */
+    public enterExitUI: WebXREnterExitUI;
+    /**
+     * Default output canvas xr should render to
+     */
+    public outputCanvas: WebXRManagedOutputCanvas;
+
+    /**
+     * Creates the default xr experience
+     * @param scene scene
+     * @param options options for basic configuration
+     * @returns resulting WebXRDefaultExperience
+     */
+    public static CreateAsync(scene: Scene, options: WebXRDefaultExperienceOptions) {
+        var result = new WebXRDefaultExperience();
+
+        // Create base experience
+        return WebXRExperienceHelper.CreateAsync(scene).then((xrHelper) => {
+            result.baseExperience = xrHelper;
+
+            // Add controller support
+            result.input = new WebXRInput(xrHelper);
+            result.controllerModelLoader = new WebXRControllerModelLoader(result.input);
+            result.pointerSelection = new WebXRControllerPointerSelection(result.input);
+            result.teleportation = new WebXRControllerTeleportation(result.input, options.floorMeshes);
+
+            // Create output canvas manager (this controls where the xr frames will be rendered)
+            result.outputCanvas = new WebXRManagedOutputCanvas(xrHelper, scene.getEngine().getRenderingCanvas() as HTMLCanvasElement);
+
+            // Create ui for entering/exiting xr
+            return WebXREnterExitUI.CreateAsync(scene, result.baseExperience, {webXRManagedOutputCanvas: result.outputCanvas});
+        }).then((ui) => {
+            result.enterExitUI = ui;
+            return result;
+        });
+    }
+
+    private constructor() {
+
+    }
+
+    /**
+     * DIsposes of the experience helper
+     */
+    public dispose() {
+        if (this.baseExperience) {
+            this.baseExperience.dispose();
+        }
+        if (this.input) {
+            this.input.dispose();
+        }
+        if (this.enterExitUI) {
+            this.enterExitUI.dispose();
+        }
+        if (this.outputCanvas) {
+            this.outputCanvas.dispose();
+        }
+    }
+}

+ 11 - 4
src/Cameras/XR/webXRExperienceHelper.ts

@@ -29,7 +29,7 @@ export enum WebXRState {
     NOT_IN_XR
 }
 /**
- * Helper class used to enable XR
+ * Base set of functionality needed to create an XR experince (WebXRSessionManager, Camera, StateManagement, etc.)
  * @see https://doc.babylonjs.com/how_to/webxr
  */
 export class WebXRExperienceHelper implements IDisposable {
@@ -89,8 +89,12 @@ export class WebXRExperienceHelper implements IDisposable {
     private constructor(private scene: Scene) {
         this.camera = new WebXRCamera("", scene);
         this.sessionManager = new WebXRSessionManager(scene);
-        this.container = new AbstractMesh("", scene);
+        this.container = new AbstractMesh("WebXR Container", scene);
         this.camera.parent = this.container;
+
+        scene.onDisposeObservable.add(() => {
+            this.exitXRAsync();
+        });
     }
 
     /**
@@ -144,11 +148,14 @@ export class WebXRExperienceHelper implements IDisposable {
                 // Restore scene settings
                 this.scene.autoClear = this._originalSceneAutoClear;
                 this.scene.activeCamera = this._nonVRCamera;
-                this.sessionManager.onXRFrameObservable.clear();
 
                 this._setState(WebXRState.NOT_IN_XR);
             });
-            this._setState(WebXRState.IN_XR);
+
+            // Wait until the first frame arrives before setting state to in xr
+            this.sessionManager.onXRFrameObservable.addOnce(() => {
+                this._setState(WebXRState.IN_XR);
+            });
         }).catch((e: any) => {
             console.log(e);
             console.log(e.message);

+ 32 - 105
src/Cameras/XR/webXRInput.ts

@@ -1,95 +1,8 @@
 import { Nullable } from "../../types";
 import { Observer, Observable } from "../../Misc/observable";
-import { IDisposable, Scene } from "../../scene";
-import { AbstractMesh } from "../../Meshes/abstractMesh";
-import { WebXRExperienceHelper } from "./webXRExperienceHelper";
-import { Matrix, Quaternion } from '../../Maths/math';
-/**
- * Represents an XR input
- */
-export class WebXRController {
-    /**
-     * Represents the part of the controller that is held. This may not exist if the controller is the head mounted display itself, if thats the case only the pointer from the head will be availible
-     */
-    public grip?: AbstractMesh;
-    /**
-     * Pointer which can be used to select objects or attach a visible laser to
-     */
-    public pointer: AbstractMesh;
-
-    private _tmpMatrix = new Matrix();
-
-    /**
-     * Creates the controller
-     * @see https://doc.babylonjs.com/how_to/webxr
-     * @param scene the scene which the controller should be associated to
-     * @param inputSource the underlying input source for the controller
-     * @param parentContainer parent that the controller meshes should be children of
-     */
-    constructor(
-        private scene: Scene,
-        /** The underlying input source for the controller  */
-        public inputSource: XRInputSource,
-        private parentContainer: Nullable<AbstractMesh> = null)
-    {
-        this.pointer = new AbstractMesh("controllerPointer", scene);
-        if (parentContainer) {
-            parentContainer.addChild(this.pointer);
-
-        }
-    }
-
-    /**
-     * Updates the controller pose based on the given XRFrame
-     * @param xrFrame xr frame to update the pose with
-     * @param referenceSpace reference space to use
-     */
-    public updateFromXRFrame(xrFrame: XRFrame, referenceSpace: XRReferenceSpace) {
-        var pose = xrFrame.getPose(this.inputSource.targetRaySpace, referenceSpace);
-        if (pose) {
-            Matrix.FromFloat32ArrayToRefScaled(pose.transform.matrix, 0, 1, this._tmpMatrix);
-            if (!this.pointer.getScene().useRightHandedSystem) {
-                this._tmpMatrix.toggleModelMatrixHandInPlace();
-            }
-            if (!this.pointer.rotationQuaternion) {
-                this.pointer.rotationQuaternion = new Quaternion();
-            }
-            this._tmpMatrix.decompose(this.pointer.scaling, this.pointer.rotationQuaternion!, this.pointer.position);
-        }
-
-        if (this.inputSource.gripSpace) {
-            if (!this.grip) {
-                this.grip = new AbstractMesh("controllerGrip", this.scene);
-                if (this.parentContainer) {
-                    this.parentContainer.addChild(this.grip);
-                }
-            }
-
-            var pose = xrFrame.getPose(this.inputSource.gripSpace, referenceSpace);
-            if (pose) {
-                Matrix.FromFloat32ArrayToRefScaled(pose.transform.matrix, 0, 1, this._tmpMatrix);
-                if (!this.grip.getScene().useRightHandedSystem) {
-                    this._tmpMatrix.toggleModelMatrixHandInPlace();
-                }
-                if (!this.grip.rotationQuaternion) {
-                    this.grip.rotationQuaternion = new Quaternion();
-                }
-                this._tmpMatrix.decompose(this.grip.scaling, this.grip.rotationQuaternion!, this.grip.position);
-            }
-        }
-
-    }
-
-    /**
-     * Disposes of the object
-     */
-    dispose() {
-        if (this.grip) {
-            this.grip.dispose();
-        }
-        this.pointer.dispose();
-    }
-}
+import { IDisposable } from "../../scene";
+import { WebXRExperienceHelper, WebXRState } from "./webXRExperienceHelper";
+import { WebXRController } from './webXRController';
 
 /**
  * XR input used to track XR inputs such as controllers/rays
@@ -100,6 +13,7 @@ export class WebXRInput implements IDisposable {
      */
     public controllers: Array<WebXRController> = [];
     private _frameObserver: Nullable<Observer<any>>;
+    private _stateObserver: Nullable<Observer<any>>;
     /**
      * Event when a controller has been connected/added
      */
@@ -111,23 +25,35 @@ export class WebXRInput implements IDisposable {
 
     /**
      * Initializes the WebXRInput
-     * @param helper experience helper which the input should be created for
+     * @param baseExperience experience helper which the input should be created for
      */
-    public constructor(private helper: WebXRExperienceHelper) {
-        this._frameObserver = helper.sessionManager.onXRFrameObservable.add(() => {
-            if (!helper.sessionManager.currentFrame) {
+    public constructor(
+        /**
+         * Base experience the input listens to
+         */
+        public baseExperience: WebXRExperienceHelper
+    ) {
+        // Remove controllers when exiting XR
+        this._stateObserver = baseExperience.onStateChangedObservable.add((s) => {
+            if (s === WebXRState.NOT_IN_XR) {
+                this._addAndRemoveControllers([], this.controllers.map((c) => {return c.inputSource; }));
+            }
+        });
+
+        this._frameObserver = baseExperience.sessionManager.onXRFrameObservable.add(() => {
+            if (!baseExperience.sessionManager.currentFrame) {
                 return;
             }
 
             // Start listing to input add/remove event
-            if (this.controllers.length == 0 && helper.sessionManager.session.inputSources) {
-                this._addAndRemoveControllers(helper.sessionManager.session.inputSources, []);
-                helper.sessionManager.session.addEventListener("inputsourceschange", this._onInputSourcesChange);
+            if (this.controllers.length == 0 && baseExperience.sessionManager.session.inputSources) {
+                this._addAndRemoveControllers(baseExperience.sessionManager.session.inputSources, []);
+                baseExperience.sessionManager.session.addEventListener("inputsourceschange", this._onInputSourcesChange);
             }
 
             // Update controller pose info
             this.controllers.forEach((controller) => {
-                controller.updateFromXRFrame(helper.sessionManager.currentFrame!, helper.sessionManager.referenceSpace);
+                controller.updateFromXRFrame(baseExperience.sessionManager.currentFrame!, baseExperience.sessionManager.referenceSpace);
             });
 
         });
@@ -139,18 +65,18 @@ export class WebXRInput implements IDisposable {
 
     private _addAndRemoveControllers(addInputs: Array<XRInputSource>, removeInputs: Array<XRInputSource>) {
         // Add controllers if they don't already exist
-        var sources = this.controllers.map((c) => {return c.inputSource; });
-        addInputs.forEach((input) => {
+        let sources = this.controllers.map((c) => {return c.inputSource; });
+        for (let input of addInputs) {
             if (sources.indexOf(input) === -1) {
-                var controller = new WebXRController(this.helper.camera._scene, input, this.helper.container);
+                let controller = new WebXRController(this.baseExperience.camera._scene, input, this.baseExperience.container);
                 this.controllers.push(controller);
                 this.onControllerAddedObservable.notifyObservers(controller);
             }
-        });
+        }
 
         // Remove and dispose of controllers to be disposed
-        var keepControllers: Array<WebXRController> = [];
-        var removedControllers: Array<WebXRController> = [];
+        let keepControllers: Array<WebXRController> = [];
+        let removedControllers: Array<WebXRController> = [];
         this.controllers.forEach((c) => {
             if (removeInputs.indexOf(c.inputSource) === -1) {
                 keepControllers.push(c);
@@ -173,6 +99,7 @@ export class WebXRInput implements IDisposable {
         this.controllers.forEach((c) => {
             c.dispose();
         });
-        this.helper.sessionManager.onXRFrameObservable.remove(this._frameObserver);
+        this.baseExperience.sessionManager.onXRFrameObservable.remove(this._frameObserver);
+        this.baseExperience.onStateChangedObservable.remove(this._stateObserver);
     }
 }

+ 4 - 1
src/Cameras/XR/webXRSessionManager.ts

@@ -138,7 +138,10 @@ export class WebXRSessionManager implements IDisposable {
      * @returns Promise which resolves after it exits XR
      */
     public exitXRAsync() {
-        return this.session.end();
+        if (this.session) {
+            this.session.end();
+        }
+        return new Promise(() => {});
     }
 
     /**

+ 1 - 1
src/Cameras/camera.ts

@@ -902,7 +902,7 @@ export class Camera extends Node {
     }
 
     /** @hidden */
-    public _isRightCamera = true;
+    public _isRightCamera = false;
     /**
      * Gets the right camera of a rig setup in case of Rigged Camera
      */

+ 4 - 3
src/Collisions/collider.ts

@@ -187,7 +187,7 @@ export class Collider {
     }
 
     /** @hidden */
-    public _testTriangle(faceIndex: number, trianglePlaneArray: Array<Plane>, p1: Vector3, p2: Vector3, p3: Vector3, hasMaterial: boolean): void {
+    public _testTriangle(faceIndex: number, trianglePlaneArray: Array<Plane>, p1: Vector3, p2: Vector3, p3: Vector3, hasMaterial: boolean, hostMesh: AbstractMesh): void {
         var t0;
         var embeddedInPlane = false;
 
@@ -371,18 +371,19 @@ export class Collider {
                 }
                 this._nearestDistance = distToCollision;
                 this.collisionFound = true;
+                this.collidedMesh = hostMesh;
             }
         }
     }
 
     /** @hidden */
-    public _collide(trianglePlaneArray: Array<Plane>, pts: Vector3[], indices: IndicesArray, indexStart: number, indexEnd: number, decal: number, hasMaterial: boolean): void {
+    public _collide(trianglePlaneArray: Array<Plane>, pts: Vector3[], indices: IndicesArray, indexStart: number, indexEnd: number, decal: number, hasMaterial: boolean, hostMesh: AbstractMesh): void {
         for (var i = indexStart; i < indexEnd; i += 3) {
             var p1 = pts[indices[i] - decal];
             var p2 = pts[indices[i + 1] - decal];
             var p3 = pts[indices[i + 2] - decal];
 
-            this._testTriangle(i, trianglePlaneArray, p3, p2, p1, hasMaterial);
+            this._testTriangle(i, trianglePlaneArray, p3, p2, p1, hasMaterial, hostMesh);
         }
     }
 

+ 33 - 3
src/Culling/ray.ts

@@ -245,6 +245,36 @@ export class Ray {
             return distance;
         }
     }
+    /**
+     * Calculate the intercept of a ray on a given axis
+     * @param axis to check 'x' | 'y' | 'z'
+     * @param offset from axis interception (i.e. an offset of 1y is intercepted above ground)
+     * @returns a vector containing the coordinates where 'axis' is equal to zero (else offset), or null if there is no intercept.
+     */
+    public intersectsAxis(axis: string, offset: number = 0): Nullable<Vector3> {
+        switch (axis) {
+            case 'y':
+                var t = (this.origin.y - offset) / this.direction.y;
+                if (t > 0) {
+                    return null;
+                }
+                return new Vector3(this.origin.x + (this.direction.x * -t), offset, this.origin.z + (this.direction.z * -t));
+            case 'x':
+                var t = (this.origin.x - offset) / this.direction.x;
+                if (t > 0) {
+                    return null;
+                }
+                return new Vector3(offset, this.origin.y + (this.direction.y * -t), this.origin.z + (this.direction.z * -t));
+            case 'z':
+                var t = (this.origin.z - offset) / this.direction.z;
+                if (t > 0) {
+                    return null;
+                }
+                return new Vector3(this.origin.x + (this.direction.x * -t), this.origin.y + (this.direction.y * -t), offset);
+            default:
+                return null;
+        }
+    }
 
     /**
      * Checks if ray intersects a mesh
@@ -666,8 +696,8 @@ Scene.prototype._internalPick = function(rayFunction: (world: Matrix) => Ray, pr
 };
 
 Scene.prototype._internalMultiPick = function(rayFunction: (world: Matrix) => Ray,
-        predicate?: (mesh: AbstractMesh) => boolean,
-        trianglePredicate?: TrianglePickingPredicate): Nullable<PickingInfo[]> {
+    predicate?: (mesh: AbstractMesh) => boolean,
+    trianglePredicate?: TrianglePickingPredicate): Nullable<PickingInfo[]> {
     if (!PickingInfo) {
         return null;
     }
@@ -775,4 +805,4 @@ Camera.prototype.getForwardRay = function(length = 100, transform?: Matrix, orig
     var direction = Vector3.Normalize(forwardWorld);
 
     return new Ray(origin, direction, length);
-};
+};

+ 23 - 8
src/Gamepads/Controllers/poseEnabledController.ts

@@ -131,6 +131,10 @@ export class PoseEnabledControllerHelper {
  * Defines the PoseEnabledController object that contains state of a vr capable controller
  */
 export class PoseEnabledController extends Gamepad implements PoseControlled {
+    /**
+     * If the controller is used in a webXR session
+     */
+    public isXR = false;
     // Represents device position and rotation in room space. Should only be used to help calculate babylon space values
     private _deviceRoomPosition = Vector3.Zero();
     private _deviceRoomRotationQuaternion = new Quaternion();
@@ -228,6 +232,9 @@ export class PoseEnabledController extends Gamepad implements PoseControlled {
      * Updates the state of the pose enbaled controller and mesh based on the current position and rotation of the controller
      */
     public update() {
+        if (this.isXR) {
+            return;
+        }
         super.update();
         this._updatePoseAndMesh();
     }
@@ -236,6 +243,9 @@ export class PoseEnabledController extends Gamepad implements PoseControlled {
      * Updates only the pose device and mesh without doing any button event checking
      */
     protected _updatePoseAndMesh() {
+        if (this.isXR) {
+            return;
+        }
         var pose: GamepadPose = this.browserGamepad.pose;
         this.updateFromDevice(pose);
 
@@ -283,6 +293,9 @@ export class PoseEnabledController extends Gamepad implements PoseControlled {
      * @param poseData raw pose fromthe device
      */
     updateFromDevice(poseData: DevicePose) {
+        if (this.isXR) {
+            return;
+        }
         if (poseData) {
             this.rawPose = poseData;
             if (poseData.position) {
@@ -335,15 +348,17 @@ export class PoseEnabledController extends Gamepad implements PoseControlled {
         }
 
         // Sync controller mesh and pointing pose node's state with controller, this is done to avoid a frame where position is 0,0,0 when attaching mesh
-        this._updatePoseAndMesh();
-        if (this._pointingPoseNode) {
-            var parents = [];
-            var obj: Node = this._pointingPoseNode;
-            while (obj.parent) {
-                parents.push(obj.parent);
-                obj = obj.parent;
+        if (!this.isXR) {
+            this._updatePoseAndMesh();
+            if (this._pointingPoseNode) {
+                var parents = [];
+                var obj: Node = this._pointingPoseNode;
+                while (obj.parent) {
+                    parents.push(obj.parent);
+                    obj = obj.parent;
+                }
+                parents.reverse().forEach((p) => { p.computeWorldMatrix(true); });
             }
-            parents.reverse().forEach((p) => { p.computeWorldMatrix(true); });
         }
 
         this._meshAttachedObservable.notifyObservers(mesh);

+ 1 - 1
src/Gizmos/boundingBoxGizmo.ts

@@ -122,7 +122,7 @@ export class BoundingBoxGizmo extends Gizmo {
         super(gizmoLayer);
 
         // Do not update the gizmo's scale so it has a fixed size to the object its attached to
-        this._updateScale = false;
+        this.updateScale = false;
 
         this._anchorMesh = new AbstractMesh("anchor", gizmoLayer.utilityLayerScene);
         // Create Materials

+ 5 - 3
src/Gizmos/gizmo.ts

@@ -62,9 +62,9 @@ export class Gizmo implements IDisposable {
      */
     public updateGizmoPositionToMatchAttachedMesh = true;
     /**
-     * When set, the gizmo will always appear the same size no matter where the camera is (default: false)
+     * When set, the gizmo will always appear the same size no matter where the camera is (default: true)
      */
-    protected _updateScale = true;
+    public updateScale = true;
     protected _interactionsEnabled = true;
     protected _attachedMeshChanged(value: Nullable<AbstractMesh>) {
     }
@@ -109,7 +109,7 @@ export class Gizmo implements IDisposable {
             }
 
             // Scale
-            if (this._updateScale) {
+            if (this.updateScale) {
                 const activeCamera = this.gizmoLayer.utilityLayerScene.activeCamera!;
                 var cameraPosition = activeCamera.globalPosition;
                 if ((<WebVRFreeCamera>activeCamera).devicePosition) {
@@ -123,6 +123,8 @@ export class Gizmo implements IDisposable {
                 if (effectiveMesh._getWorldMatrixDeterminant() < 0) {
                     this._rootMesh.scaling.y *= -1;
                 }
+            }else {
+                this._rootMesh.scaling.setAll(this.scaleRatio);
             }
         }
     }

+ 8 - 15
src/Helpers/sceneHelpers.ts

@@ -12,16 +12,13 @@ import { IEnvironmentHelperOptions, EnvironmentHelper } from "./environmentHelpe
 import { FreeCamera } from "../Cameras/freeCamera";
 import { ArcRotateCamera } from "../Cameras/arcRotateCamera";
 import { TargetCamera } from "../Cameras/targetCamera";
-import { WebXRManagedOutputCanvas } from "../Cameras/XR/webXRManagedOutputCanvas";
-import { WebXRInput } from "../Cameras/XR/webXRInput";
-import { WebXREnterExitUI } from "../Cameras/XR/webXREnterExitUI";
-import { WebXRExperienceHelper } from "../Cameras/XR/webXRExperienceHelper";
 import { VRExperienceHelperOptions, VRExperienceHelper } from "../Cameras/VR/vrExperienceHelper";
 
 import "../Materials/Textures/Loaders/ddsTextureLoader";
 import "../Materials/Textures/Loaders/envTextureLoader";
 import "../Materials/Textures/Loaders/ktxTextureLoader";
 import "../Meshes/Builders/boxBuilder";
+import { WebXRDefaultExperience, WebXRDefaultExperienceOptions } from '../Cameras/XR/webXRDefaultExperience';
 
 /** @hidden */
 export var _forceSceneHelpersToBundle = true;
@@ -82,11 +79,12 @@ declare module "../scene" {
         createDefaultVRExperience(webVROptions?: VRExperienceHelperOptions): VRExperienceHelper;
 
         /**
-         * Creates a new XREXperienceHelper
+         * Creates a new WebXRDefaultExperience
          * @see http://doc.babylonjs.com/how_to/webxr
-         * @returns a promise for a new XREXperienceHelper
+         * @param options experience options
+         * @returns a promise for a new WebXRDefaultExperience
          */
-        createDefaultXRExperienceAsync(): Promise<WebXRExperienceHelper>;
+        createDefaultXRExperienceAsync(options: WebXRDefaultExperienceOptions): Promise<WebXRDefaultExperience>;
     }
 }
 
@@ -210,13 +208,8 @@ Scene.prototype.createDefaultVRExperience = function(webVROptions: VRExperienceH
     return new VRExperienceHelper(this, webVROptions);
 };
 
-Scene.prototype.createDefaultXRExperienceAsync = function(): Promise<WebXRExperienceHelper> {
-    return WebXRExperienceHelper.CreateAsync(this).then((helper) => {
-        var outputCanvas = new WebXRManagedOutputCanvas(helper);
-        return WebXREnterExitUI.CreateAsync(this, helper, { webXRManagedOutputCanvas: outputCanvas })
-            .then((ui) => {
-                new WebXRInput(helper);
-                return helper;
-            });
+Scene.prototype.createDefaultXRExperienceAsync = function(options: WebXRDefaultExperienceOptions): Promise<WebXRDefaultExperience> {
+    return WebXRDefaultExperience.CreateAsync(this, options).then((helper) => {
+        return helper;
     });
 };

+ 24 - 4
src/Layers/effectLayer.ts

@@ -479,8 +479,16 @@ export abstract class EffectLayer {
                 attribs.push(VertexBuffer.MatricesIndicesExtraKind);
                 attribs.push(VertexBuffer.MatricesWeightsExtraKind);
             }
+
             defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
-            defines.push("#define BonesPerMesh " + (mesh.skeleton ? (mesh.skeleton.bones.length + 1) : 0));
+
+            let skeleton = mesh.skeleton;
+            if (skeleton && skeleton.isUsingTextureForMatrices) {
+                defines.push("#define BONETEXTURE");
+            } else {
+                defines.push("#define BonesPerMesh " + (skeleton ? (skeleton.bones.length + 1) : 0));
+            }
+
             if (mesh.numBoneInfluencers > 0) {
                 fallbacks.addCPUSkinningFallback(0, mesh);
             }
@@ -515,9 +523,9 @@ export abstract class EffectLayer {
             this._effectLayerMapGenerationEffect = this._scene.getEngine().createEffect("glowMapGeneration",
                 attribs,
                 ["world", "mBones", "viewProjection",
-                    "glowColor", "morphTargetInfluences",
+                    "glowColor", "morphTargetInfluences", "boneTextureWidth",
                     "diffuseMatrix", "emissiveMatrix", "opacityMatrix", "opacityIntensity"],
-                ["diffuseSampler", "emissiveSampler", "opacitySampler"], join,
+                ["diffuseSampler", "emissiveSampler", "opacitySampler", "boneSampler"], join,
                 fallbacks, undefined, undefined, { maxSimultaneousMorphTargets: morphInfluencers });
         }
 
@@ -713,7 +721,19 @@ export abstract class EffectLayer {
 
             // Bones
             if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                this._effectLayerMapGenerationEffect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+                const skeleton = mesh.skeleton;
+
+                if (skeleton.isUsingTextureForMatrices) {
+                    const boneTexture = skeleton.getTransformMatrixTexture(mesh);
+                    if (!boneTexture) {
+                        return;
+                    }
+
+                    this._effectLayerMapGenerationEffect.setTexture("boneSampler", boneTexture);
+                    this._effectLayerMapGenerationEffect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
+                } else {
+                    this._effectLayerMapGenerationEffect.setMatrices("mBones", skeleton.getTransformMatrices((mesh)));
+                }
             }
 
             // Morph targets

+ 21 - 26
src/Materials/Node/Blocks/Dual/fogBlock.ts

@@ -9,11 +9,15 @@ import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPo
 import { AbstractMesh } from '../../../../Meshes/abstractMesh';
 import { MaterialHelper } from '../../../materialHelper';
 import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { InputBlock } from '../Input/inputBlock';
 
 /**
  * Block used to add support for scene fog
  */
 export class FogBlock extends NodeMaterialBlock {
+    private _fogDistanceName: string;
+    private _fogParameters: string;
+
     /**
      * Create a new FogBlock
      * @param name defines the block name
@@ -25,12 +29,9 @@ export class FogBlock extends NodeMaterialBlock {
         this.registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
         this.registerInput("view", NodeMaterialBlockConnectionPointTypes.Matrix, false, NodeMaterialBlockTargets.Vertex);
 
-        this.registerOutput("vFogDistance", NodeMaterialBlockConnectionPointTypes.Vector3, NodeMaterialBlockTargets.Vertex);
-
         // Fragment
         this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color3OrColor4, false, NodeMaterialBlockTargets.Fragment);
         this.registerInput("fogColor", NodeMaterialBlockConnectionPointTypes.Color3, false, NodeMaterialBlockTargets.Fragment);
-        this.registerInput("fogParameters", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Fragment);
 
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
     }
@@ -72,13 +73,6 @@ export class FogBlock extends NodeMaterialBlock {
     }
 
     /**
-     * Gets the for parameter input component
-     */
-    public get fogParameters(): NodeMaterialConnectionPoint {
-        return this._inputs[4];
-    }
-
-    /**
      * Gets the output component
      */
     public get output(): NodeMaterialConnectionPoint {
@@ -86,16 +80,16 @@ export class FogBlock extends NodeMaterialBlock {
     }
 
     public autoConfigure() {
-        if (this.view.isUndefined) {
-            this.view.setAsWellKnownValue(NodeMaterialWellKnownValues.View);
+        if (!this.view.isConnected) {
+            let viewInput = new InputBlock("view");
+            viewInput.setAsWellKnownValue(NodeMaterialWellKnownValues.View);
+            viewInput.output.connectTo(this.view);
         }
-        if (this.fogColor.isUndefined) {
-            this.fogColor.setAsWellKnownValue(NodeMaterialWellKnownValues.Automatic);
+        if (!this.fogColor.isConnected) {
+            let fogColorInput = new InputBlock("fogColor", undefined, NodeMaterialBlockConnectionPointTypes.Color3);
+            fogColorInput.setAsWellKnownValue(NodeMaterialWellKnownValues.FogColor);
+            fogColorInput.output.connectTo(this.fogColor);
         }
-        if (this.fogParameters.isUndefined) {
-            this.fogParameters.setAsWellKnownValue(NodeMaterialWellKnownValues.Automatic);
-        }
-        this._outputs[0].isVarying = true;
     }
 
     public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
@@ -109,8 +103,7 @@ export class FogBlock extends NodeMaterialBlock {
         }
 
         const scene = mesh.getScene();
-        effect.setColor3("u_fogColor", scene.fogColor);
-        effect.setFloat4("u_fogParameters", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
+        effect.setFloat4(this._fogParameters, scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
     }
 
     protected _buildBlock(state: NodeMaterialBuildState) {
@@ -131,20 +124,22 @@ export class FogBlock extends NodeMaterialBlock {
             let tempFogVariablename = state._getFreeVariableName("fog");
             let color = this.color;
             let fogColor = this.fogColor;
-            let fogParameters = this.fogParameters;
-            let output = this._outputs[1];
-            let vFogDistance = this._outputs[0];
+            this._fogParameters = state._getFreeVariableName("fogParameters");
+            let output = this._outputs[0];
+
+            state._emitUniformFromString(this._fogParameters, "vec4");
 
             state.compilationString += `#ifdef FOG\r\n`;
-            state.compilationString += `float ${tempFogVariablename} = CalcFogFactor(${vFogDistance.associatedVariableName}, ${fogParameters.associatedVariableName});\r\n`;
+            state.compilationString += `float ${tempFogVariablename} = CalcFogFactor(${this._fogDistanceName}, ${this._fogParameters});\r\n`;
             state.compilationString += this._declareOutput(output, state) + ` = ${tempFogVariablename} * ${color.associatedVariableName}.rgb + (1.0 - ${tempFogVariablename}) * ${fogColor.associatedVariableName};\r\n`;
             state.compilationString += `#else\r\n${this._declareOutput(output, state)} =  ${color.associatedVariableName}.rgb;\r\n`;
             state.compilationString += `#endif\r\n`;
         } else {
             let worldPos = this.worldPosition;
             let view = this.view;
-            let vFogDistance = this._outputs[0];
-            state.compilationString += this._declareOutput(vFogDistance, state) + ` = (${view.associatedVariableName} * ${worldPos.associatedVariableName}).xyz;\r\n`;
+            this._fogDistanceName = state._getFreeVariableName("vFogDistance");
+            state._emitVaryingFromString(this._fogDistanceName, "vec3");
+            state.compilationString += `${this._fogDistanceName} = (${view.associatedVariableName} * ${worldPos.associatedVariableName}).xyz;\r\n`;
         }
 
         return this;

+ 2 - 1
src/Materials/Node/Blocks/Dual/index.ts

@@ -1,3 +1,4 @@
 
 export * from "./fogBlock";
-export * from "./lightBlock";
+export * from "./lightBlock";
+export * from "./textureBlock";

+ 28 - 23
src/Materials/Node/Blocks/Dual/lightBlock.ts

@@ -9,6 +9,9 @@ import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
 import { Effect } from '../../../effect';
 import { Mesh } from '../../../../Meshes/mesh';
 import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+import { InputBlock } from '../Input/inputBlock';
+import { Light } from '../../../../Lights/light';
+import { Nullable } from '../../../../types';
 
 /**
  * Block used to add light in the fragment shader
@@ -17,6 +20,11 @@ export class LightBlock extends NodeMaterialBlock {
     private _lightId: number;
 
     /**
+     * Gets or sets the light associated with this block
+     */
+    public light: Nullable<Light>;
+
+    /**
      * Create a new LightBlock
      * @param name defines the block name
      */
@@ -26,7 +34,6 @@ export class LightBlock extends NodeMaterialBlock {
         this.registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
         this.registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
 
-        this.registerInput("light", NodeMaterialBlockConnectionPointTypes.Light, true, NodeMaterialBlockTargets.Fragment);
         this.registerInput("cameraPosition", NodeMaterialBlockConnectionPointTypes.Vector3, false, NodeMaterialBlockTargets.Fragment);
         this.registerOutput("diffuseOutput", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
         this.registerOutput("specularOutput", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
@@ -55,18 +62,10 @@ export class LightBlock extends NodeMaterialBlock {
     }
 
     /**
-    * Gets the light input component.
-    * If not defined, all lights will be considered
-    */
-    public get light(): NodeMaterialConnectionPoint {
-        return this._inputs[2];
-    }
-
-    /**
     * Gets the camera (or eye) position component
     */
     public get cameraPosition(): NodeMaterialConnectionPoint {
-        return this._inputs[3];
+        return this._inputs[2];
     }
 
     /**
@@ -84,15 +83,21 @@ export class LightBlock extends NodeMaterialBlock {
     }
 
     public autoConfigure() {
-        if (this.cameraPosition.isUndefined) {
-            this.cameraPosition.setAsWellKnownValue(NodeMaterialWellKnownValues.CameraPosition);
+        if (!this.cameraPosition.isConnected) {
+            let cameraPositionInput = new InputBlock("cameraPosition");
+            cameraPositionInput.setAsWellKnownValue(NodeMaterialWellKnownValues.CameraPosition);
+            cameraPositionInput.output.connectTo(this.cameraPosition);
         }
     }
 
     public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (!defines._areLightsDirty) {
+            return;
+        }
+
         const scene = mesh.getScene();
 
-        if (!this.light.value) {
+        if (!this.light) {
             MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, true, nodeMaterial.maxSimultaneousLights);
         } else {
             let state = {
@@ -103,7 +108,7 @@ export class LightBlock extends NodeMaterialBlock {
                 specularEnabled: false
             };
 
-            MaterialHelper.PrepareDefinesForLight(scene, mesh, this.light.value, this._lightId, defines, true, state);
+            MaterialHelper.PrepareDefinesForLight(scene, mesh, this.light, this._lightId, defines, true, state);
 
             if (state.needRebuild) {
                 defines.rebuild();
@@ -127,10 +132,10 @@ export class LightBlock extends NodeMaterialBlock {
 
         const scene = mesh.getScene();
 
-        if (!this.light.value) {
+        if (!this.light) {
             MaterialHelper.BindLights(scene, mesh, effect, true, nodeMaterial.maxSimultaneousLights, false);
         } else {
-            MaterialHelper.BindLight(this.light.value, this._lightId, scene, mesh, effect, true, false);
+            MaterialHelper.BindLight(this.light, this._lightId, scene, mesh, effect, true, false);
         }
     }
 
@@ -140,10 +145,10 @@ export class LightBlock extends NodeMaterialBlock {
 
         // Inject code in vertex
         let worldPosVaryingName = "v_" + worldPos.associatedVariableName;
-        state._emitVaryings(worldPos, undefined, true, false, worldPosVaryingName, NodeMaterialBlockConnectionPointTypes.Vector3);
+        state._emitVaryingFromString(worldPosVaryingName, "vec3");
 
         let worldNormalVaryingName = "v_" + worldNormal.associatedVariableName;
-        state._emitVaryings(worldNormal, undefined, true, false, worldNormalVaryingName, NodeMaterialBlockConnectionPointTypes.Vector3);
+        state._emitVaryingFromString(worldNormalVaryingName, "vec3");
 
         state.compilationString += `${worldPosVaryingName} = ${worldPos.associatedVariableName}.xyz;\r\n`;
         state.compilationString += `${worldNormalVaryingName} = ${worldNormal.associatedVariableName}.xyz;\r\n`;
@@ -153,12 +158,12 @@ export class LightBlock extends NodeMaterialBlock {
         super._buildBlock(state);
 
         if (state.target !== NodeMaterialBlockTargets.Fragment) {
+            // Vertex
+            this._injectVertexCode(state);
+
             return;
         }
 
-        // Vertex
-        this._injectVertexCode(state._vertexState);
-
         // Fragment
         state.sharedData.bindableBlocks.push(this);
         state.sharedData.blocksWithDefines.push(this);
@@ -172,7 +177,7 @@ export class LightBlock extends NodeMaterialBlock {
             ]
         });
 
-        if (!this.light.value) { // Emit for all lights
+        if (!this.light) { // Emit for all lights
             state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", comments, {
                 repeatKey: "maxSimultaneousLights"
             });
@@ -203,7 +208,7 @@ export class LightBlock extends NodeMaterialBlock {
             state.compilationString += `vec3 normalW = v_${this.worldNormal.associatedVariableName};\r\n`;
         }
 
-        if (this.light.value) {
+        if (this.light) {
             state.compilationString += state._emitCodeFromInclude("lightFragment", comments, {
                 replaceStrings: [
                     { search: /{X}/g, replace: this._lightId.toString() }

+ 185 - 0
src/Materials/Node/Blocks/Dual/textureBlock.ts

@@ -0,0 +1,185 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { BaseTexture } from '../../../Textures/baseTexture';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { InputBlock } from '../Input/inputBlock';
+import { Effect } from '../../../effect';
+import { Mesh } from '../../../../Meshes/mesh';
+import { Nullable } from '../../../../types';
+
+/**
+ * Block used to read a texture from a sampler
+ */
+export class TextureBlock extends NodeMaterialBlock {
+    private _defineName: string;
+    private _samplerName: string;
+    private _transformedUVName: string;
+    private _textureTransformName: string;
+    private _textureInfoName: string;
+    private _mainUVName: string;
+    private _mainUVDefineName: string;
+
+    /**
+     * Gets or sets the texture associated with the node
+     */
+    public texture: Nullable<BaseTexture>;
+
+    /**
+     * Create a new TextureBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.VertexAndFragment);
+
+        this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2, false, NodeMaterialBlockTargets.Vertex);
+
+        this.registerOutput("color", NodeMaterialBlockConnectionPointTypes.Color4, NodeMaterialBlockTargets.Fragment);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "TextureBlock";
+    }
+
+    /**
+     * Gets the uv input component
+     */
+    public get uv(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the output component
+     */
+    public get output(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    public autoConfigure() {
+        if (!this.uv.isConnected) {
+            let uvInput = new InputBlock("uv");
+            uvInput.setAsAttribute();
+            uvInput.output.connectTo(this.uv);
+        }
+    }
+
+    public initializeDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines, useInstances: boolean = false) {
+        if (!defines._areTexturesDirty) {
+            return;
+        }
+
+        defines.setValue(this._mainUVDefineName, false);
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (!defines._areTexturesDirty) {
+            return;
+        }
+
+        if (!this.texture || !this.texture.getTextureMatrix) {
+            return;
+        }
+
+        if (!this.texture.getTextureMatrix().isIdentityAs3x2()) {
+            defines.setValue(this._defineName, true);
+        } else {
+            defines.setValue(this._defineName, false);
+            defines.setValue(this._mainUVDefineName, true);
+        }
+    }
+
+    public isReady() {
+        if (this.texture && !this.texture.isReadyOrNotBlocking()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
+        if (!mesh || !this.texture) {
+            return;
+        }
+
+        effect.setFloat(this._textureInfoName, this.texture.level);
+        effect.setMatrix(this._textureTransformName, this.texture.getTextureMatrix());
+        effect.setTexture(this._samplerName, this.texture);
+    }
+
+    private _injectVertexCode(state: NodeMaterialBuildState) {
+        let uvInput = this.uv;
+
+        // Inject code in vertex
+        this._defineName = state._getFreeDefineName("UVTRANSFORM");
+        this._mainUVDefineName = state._getFreeDefineName("vMain" + uvInput.associatedVariableName);
+
+        if (uvInput.connectedPoint!.ownerBlock.isInput) {
+            let uvInputOwnerBlock = uvInput.connectedPoint!.ownerBlock as InputBlock;
+
+            if (!uvInputOwnerBlock.isAttribute) {
+                state._emitUniformFromString(uvInput.associatedVariableName, "vec2");
+            }
+        }
+
+        this._mainUVName = "vMain" + uvInput.associatedVariableName;
+        this._transformedUVName = state._getFreeVariableName("transformedUV");
+        this._textureTransformName = state._getFreeVariableName("textureTransform");
+        this._textureInfoName = state._getFreeVariableName("textureInfoName");
+
+        state._emitVaryingFromString(this._transformedUVName, "vec2", this._defineName);
+        state._emitVaryingFromString(this._mainUVName, "vec2", this._mainUVDefineName);
+
+        state._emitUniformFromString(this._textureTransformName, "mat4", this._defineName);
+
+        if (state.sharedData.emitComments) {
+            state.compilationString += `\r\n//${this.name}\r\n`;
+        }
+        state.compilationString += `#ifdef ${this._defineName}\r\n`;
+        state.compilationString += `${this._transformedUVName} = vec2(${this._textureTransformName} * vec4(${uvInput.associatedVariableName}, 1.0, 0.0));\r\n`;
+        state.compilationString += `#else\r\n`;
+        state.compilationString += `${this._mainUVName} = ${uvInput.associatedVariableName};\r\n`;
+        state.compilationString += `#endif\r\n`;
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        if (state.target !== NodeMaterialBlockTargets.Fragment) {
+            // Vertex
+            this._injectVertexCode(state);
+            return;
+        }
+
+        state.sharedData.blockingBlocks.push(this);
+        state.sharedData.textureBlocks.push(this);
+
+        this._samplerName = state._getFreeVariableName(this.name + "Sampler");
+        state.samplers.push(this._samplerName);
+        state._samplerDeclaration += `uniform sampler2D ${this._samplerName};\r\n`;
+
+        // Fragment
+        state.sharedData.blocksWithDefines.push(this);
+        state.sharedData.bindableBlocks.push(this);
+
+        state._emitUniformFromString(this._textureInfoName, "float");
+
+        let uvInput = this.uv;
+        let output = this._outputs[0];
+        const complement = ` * ${this._textureInfoName}`;
+
+        state.compilationString += `#ifdef ${this._defineName}\r\n`;
+        state.compilationString += `vec4 ${output.associatedVariableName} = texture2D(${this._samplerName}, ${this._transformedUVName})${complement};\r\n`;
+        state.compilationString += `#else\r\n`;
+        state.compilationString += `vec4 ${output.associatedVariableName} = texture2D(${this._samplerName}, ${"vMain" + uvInput.associatedVariableName})${complement};\r\n`;
+        state.compilationString += `#endif\r\n`;
+
+        return this;
+    }
+}

+ 4 - 2
src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts

@@ -19,7 +19,7 @@ export class FragmentOutputBlock extends NodeMaterialBlock {
     public constructor(name: string) {
         super(name, NodeMaterialBlockTargets.Fragment, true);
 
-        this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color3OrColor4);
+        this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Vector2OrVector3OrColor3OrVector4OrColor4);
     }
 
     /**
@@ -43,7 +43,9 @@ export class FragmentOutputBlock extends NodeMaterialBlock {
         let input = this.color;
         state.sharedData.hints.needAlphaBlending = this.alphaBlendingEnabled;
 
-        if (input.connectedPoint && input.connectedPoint!.type === NodeMaterialBlockConnectionPointTypes.Color3) {
+        if (input.connectedPoint && input.connectedPoint!.type === NodeMaterialBlockConnectionPointTypes.Vector2) {
+            state.compilationString += `gl_FragColor = vec4(${input.associatedVariableName}.r, ${input.associatedVariableName}.g, 0., 1.0);\r\n`;
+        } else if (input.connectedPoint && (input.connectedPoint!.type === NodeMaterialBlockConnectionPointTypes.Color3 || input.connectedPoint!.type === NodeMaterialBlockConnectionPointTypes.Vector3)) {
             state.compilationString += `gl_FragColor = vec4(${input.associatedVariableName}, 1.0);\r\n`;
         } else {
             state.compilationString += `gl_FragColor = ${input.associatedVariableName};\r\n`;

+ 0 - 1
src/Materials/Node/Blocks/Fragment/index.ts

@@ -5,5 +5,4 @@ export * from "./rgbaMergerBlock";
 export * from "./rgbMergerBlock";
 export * from "./rgbaSplitterBlock";
 export * from "./rgbSplitterBlock";
-export * from "./textureBlock";
 export * from "./imageProcessingBlock";

+ 0 - 209
src/Materials/Node/Blocks/Fragment/textureBlock.ts

@@ -1,209 +0,0 @@
-import { NodeMaterialBlock } from '../../nodeMaterialBlock';
-import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
-import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
-import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
-import { BaseTexture } from '../../../Textures/baseTexture';
-import { AbstractMesh } from '../../../../Meshes/abstractMesh';
-import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
-
-/**
- * Block used to read a texture from a sampler
- */
-export class TextureBlock extends NodeMaterialBlock {
-    private _defineName: string;
-
-    /**
-     * Gets or sets a boolean indicating that the block can automatically fetch the texture matrix
-     */
-    public autoConnectTextureMatrix = true;
-
-    /**
-     * Gets or sets a boolean indicating that the block can automatically select the uv channel based on texture
-     */
-    public autoSelectUV = true;
-
-    /**
-     * Create a new TextureBlock
-     * @param name defines the block name
-     */
-    public constructor(name: string) {
-        super(name, NodeMaterialBlockTargets.Fragment);
-
-        this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2);
-        this.registerInput("textureInfo", NodeMaterialBlockConnectionPointTypes.Vector2, true);
-
-        this.registerInput("transformedUV", NodeMaterialBlockConnectionPointTypes.Vector2, false, NodeMaterialBlockTargets.Vertex);
-        this.registerInput("texture", NodeMaterialBlockConnectionPointTypes.Texture, false, NodeMaterialBlockTargets.Fragment);
-        this.registerInput("textureTransform", NodeMaterialBlockConnectionPointTypes.Matrix, true, NodeMaterialBlockTargets.Vertex);
-
-        this.registerOutput("color", NodeMaterialBlockConnectionPointTypes.Color4);
-
-        // Setup
-        this._inputs[0]._needToEmitVarying = false;
-        this._inputs[0]._forceUniformInVertexShaderOnly = true;
-    }
-
-    /**
-     * Gets the current class name
-     * @returns the class name
-     */
-    public getClassName() {
-        return "TextureBlock";
-    }
-
-    /**
-     * Gets the uv input component
-     */
-    public get uv(): NodeMaterialConnectionPoint {
-        return this._inputs[0];
-    }
-
-    /**
-     * Gets the texture information input component
-     */
-    public get textureInfo(): NodeMaterialConnectionPoint {
-        return this._inputs[1];
-    }
-
-    /**
-     * Gets the transformed uv input component
-     */
-    public get transformedUV(): NodeMaterialConnectionPoint {
-        return this._inputs[2];
-    }
-
-    /**
-     * Gets the texture input component
-     */
-    public get texture(): NodeMaterialConnectionPoint {
-        return this._inputs[3];
-    }
-
-    /**
-     * Gets the texture transform input component
-     */
-    public get textureTransform(): NodeMaterialConnectionPoint {
-        return this._inputs[4];
-    }
-
-    /**
-     * Gets the output component
-     */
-    public get output(): NodeMaterialConnectionPoint {
-        return this._outputs[0];
-    }
-
-    public autoConfigure() {
-        if (this.uv.isUndefined) {
-            this.uv.setAsAttribute();
-            this.uv.connectTo(this.transformedUV);
-        }
-
-        if (this.transformedUV.isUndefined) {
-            this.uv.connectTo(this.transformedUV);
-        }
-    }
-
-    public initialize(state: NodeMaterialBuildState) {
-        if (this.texture.value && this.texture.value.getTextureMatrix) {
-            const texture = this.texture.value as BaseTexture;
-
-            if (this.autoConnectTextureMatrix) {
-                this.textureTransform.valueCallback = () => texture.getTextureMatrix();
-            }
-            if (this.autoSelectUV) {
-                this.uv.setAsAttribute("uv" + (texture.coordinatesIndex ? (texture.coordinatesIndex + 1) : ""));
-            }
-        }
-    }
-
-    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
-        if (!this.texture.value || !this.texture.value.getTextureMatrix) {
-            return;
-        }
-
-        let uvInput = this.uv;
-        let textureTransform = this.textureTransform;
-        let isTextureTransformConnected = textureTransform.connectedPoint != null || textureTransform.isUniform;
-
-        const texture = this.texture.value as BaseTexture;
-        let mainUVName = ("vMain" + uvInput.associatedVariableName).toUpperCase();
-
-        if (isTextureTransformConnected && !texture.getTextureMatrix().isIdentityAs3x2()) {
-            defines.setValue(this._defineName, true);
-            defines.setValue(mainUVName, false);
-        } else {
-            defines.setValue(this._defineName, false);
-            defines.setValue(mainUVName, true);
-        }
-    }
-
-    public isReady() {
-        let texture = this.texture.value as BaseTexture;
-        if (texture && !texture.isReadyOrNotBlocking()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private _injectVertexCode(state: NodeMaterialBuildState) {
-        let uvInput = this.uv;
-        let transformedUV = this.transformedUV;
-        let textureTransform = this.textureTransform;
-        let isTextureTransformConnected = textureTransform.connectedPoint != null || textureTransform.isUniform;
-
-        // Inject code in vertex
-        this._defineName = state._getFreeDefineName("UVTRANSFORM");
-        let mainUVName = "vMain" + uvInput.associatedVariableName;
-
-        transformedUV.associatedVariableName = state._getFreeVariableName(transformedUV.name);
-        state._emitVaryings(transformedUV, this._defineName, true);
-        state._emitVaryings(transformedUV, mainUVName.toUpperCase(), true, false, mainUVName);
-
-        textureTransform.associatedVariableName = state._getFreeVariableName(textureTransform.name);
-        state._emitUniformOrAttributes(textureTransform, this._defineName);
-
-        if (isTextureTransformConnected) {
-            if (state.sharedData.emitComments) {
-                state.compilationString += `\r\n//${this.name}\r\n`;
-            }
-            state.compilationString += `#ifdef ${this._defineName}\r\n`;
-            state.compilationString += `${transformedUV.associatedVariableName} = vec2(${textureTransform.associatedVariableName} * vec4(${uvInput.associatedVariableName}, 1.0, 0.0));\r\n`;
-            state.compilationString += `#else\r\n`;
-            state.compilationString += `${mainUVName} = ${uvInput.associatedVariableName};\r\n`;
-            state.compilationString += `#endif\r\n`;
-        } else {
-            state.compilationString += `${mainUVName} = ${uvInput.associatedVariableName};\r\n`;
-        }
-    }
-
-    protected _buildBlock(state: NodeMaterialBuildState) {
-        super._buildBlock(state);
-
-        state.sharedData.blockingBlocks.push(this);
-
-        // Vertex
-        this._injectVertexCode(state._vertexState);
-
-        // Fragment
-        state.sharedData.blocksWithDefines.push(this);
-
-        let uvInput = this.uv;
-        let transformedUV = this.transformedUV;
-        let textureInfo = this.textureInfo;
-        let samplerInput = this.texture;
-        let output = this._outputs[0];
-        let isTextureInfoConnected = textureInfo.connectedPoint != null || textureInfo.isUniform;
-        const complement = isTextureInfoConnected ? ` * ${textureInfo.associatedVariableName}.y` : "";
-
-        state.compilationString += `#ifdef ${this._defineName}\r\n`;
-        state.compilationString += `vec4 ${output.associatedVariableName} = texture2D(${samplerInput.associatedVariableName}, ${transformedUV.associatedVariableName})${complement};\r\n`;
-        state.compilationString += `#else\r\n`;
-        state.compilationString += `vec4 ${output.associatedVariableName} = texture2D(${samplerInput.associatedVariableName}, ${"vMain" + uvInput.associatedVariableName})${complement};\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        return this;
-    }
-}

+ 1 - 0
src/Materials/Node/Blocks/Input/index.ts

@@ -0,0 +1 @@
+export * from "./inputBlock";

+ 426 - 0
src/Materials/Node/Blocks/Input/inputBlock.ts

@@ -0,0 +1,426 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBlockConnectionPointMode } from '../../NodeMaterialBlockConnectionPointMode';
+import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+import { Nullable } from '../../../../types';
+import { Effect } from '../../../../Materials/effect';
+import { Matrix, Vector2, Vector3 } from '../../../../Maths/math';
+import { Scene } from '../../../../scene';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+
+/**
+ * Block used to expose an input value
+ */
+export class InputBlock extends NodeMaterialBlock {
+    private _mode = NodeMaterialBlockConnectionPointMode.Undefined;
+    private _associatedVariableName: string;
+    private _storedValue: any;
+    private _valueCallback: () => any;
+    private _type: NodeMaterialBlockConnectionPointTypes;
+
+    /** @hidden */
+    public _wellKnownValue: Nullable<NodeMaterialWellKnownValues> = null;
+
+    /**
+     * Gets or sets the connection point type (default is float)
+     */
+    public get type(): NodeMaterialBlockConnectionPointTypes {
+        if (this._type === NodeMaterialBlockConnectionPointTypes.AutoDetect) {
+            if (this.isUniform && this.value != null) {
+                if (!isNaN(this.value)) {
+                    return NodeMaterialBlockConnectionPointTypes.Float;
+                }
+
+                switch (this.value.getClassName()) {
+                    case "Vector2":
+                        return NodeMaterialBlockConnectionPointTypes.Vector2;
+                    case "Vector3":
+                        return NodeMaterialBlockConnectionPointTypes.Vector3;
+                    case "Vector4":
+                        return NodeMaterialBlockConnectionPointTypes.Vector4;
+                    case "Color3":
+                        return NodeMaterialBlockConnectionPointTypes.Color3;
+                    case "Color4":
+                        return NodeMaterialBlockConnectionPointTypes.Color4;
+                }
+            }
+
+            if (this.isAttribute) {
+                switch (this.name) {
+                    case "position":
+                    case "normal":
+                    case "tangent":
+                        return NodeMaterialBlockConnectionPointTypes.Vector3;
+                    case "uv":
+                    case "uv2":
+                        return NodeMaterialBlockConnectionPointTypes.Vector2;
+                }
+            }
+
+            if (this.isWellKnownValue) {
+                switch (this._wellKnownValue) {
+                    case NodeMaterialWellKnownValues.World:
+                    case NodeMaterialWellKnownValues.WorldView:
+                    case NodeMaterialWellKnownValues.WorldViewProjection:
+                    case NodeMaterialWellKnownValues.View:
+                    case NodeMaterialWellKnownValues.ViewProjection:
+                    case NodeMaterialWellKnownValues.Projection:
+                        return NodeMaterialBlockConnectionPointTypes.Matrix;
+                    case NodeMaterialWellKnownValues.CameraPosition:
+                        return NodeMaterialBlockConnectionPointTypes.Vector3;
+                }
+            }
+        }
+
+        return this._type;
+    }
+
+    /**
+     * Creates a new InputBlock
+     * @param name defines the block name
+     * @param target defines the target of that block (Vertex by default)
+     * @param type defines the type of the input (can be set to NodeMaterialBlockConnectionPointTypes.AutoDetect)
+     */
+    public constructor(name: string, target = NodeMaterialBlockTargets.Vertex, type: NodeMaterialBlockConnectionPointTypes = NodeMaterialBlockConnectionPointTypes.AutoDetect) {
+        super(name, target, false, true);
+
+        this._type = type;
+
+        this.registerOutput("output", type);
+    }
+
+    /**
+     * Gets the output component
+     */
+    public get output(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    /**
+     * Set the source of this connection point to a vertex attribute
+     * @param attributeName defines the attribute name (position, uv, normal, etc...). If not specified it will take the connection point name
+     * @returns the current connection point
+     */
+    public setAsAttribute(attributeName?: string): InputBlock {
+        if (attributeName) {
+            this.name = attributeName;
+        }
+        this._mode = NodeMaterialBlockConnectionPointMode.Attribute;
+        return this;
+    }
+
+    /**
+     * Set the source of this connection point to a well known value
+     * @param value define the well known value to use (world, view, etc...) or null to switch to manual value
+     * @returns the current connection point
+     */
+    public setAsWellKnownValue(value: Nullable<NodeMaterialWellKnownValues>): InputBlock {
+        this.wellKnownValue = value;
+        return this;
+    }
+
+    /**
+     * Gets or sets the value of that point.
+     * Please note that this value will be ignored if valueCallback is defined
+     */
+    public get value(): any {
+        return this._storedValue;
+    }
+
+    public set value(value: any) {
+        this._storedValue = value;
+        this._mode = NodeMaterialBlockConnectionPointMode.Uniform;
+    }
+
+    /**
+     * Gets or sets a callback used to get the value of that point.
+     * Please note that setting this value will force the connection point to ignore the value property
+     */
+    public get valueCallback(): () => any {
+        return this._valueCallback;
+    }
+
+    public set valueCallback(value: () => any) {
+        this._valueCallback = value;
+        this._mode = NodeMaterialBlockConnectionPointMode.Uniform;
+    }
+
+    /**
+     * Gets or sets the associated variable name in the shader
+     */
+    public get associatedVariableName(): string {
+        return this._associatedVariableName;
+    }
+
+    public set associatedVariableName(value: string) {
+        this._associatedVariableName = value;
+    }
+
+    /**
+     * Gets a boolean indicating that this connection point not defined yet
+     */
+    public get isUndefined(): boolean {
+        return this._mode === NodeMaterialBlockConnectionPointMode.Undefined;
+    }
+
+    /**
+     * Gets or sets a boolean indicating that this connection point is coming from an uniform.
+     * In this case the connection point name must be the name of the uniform to use.
+     * Can only be set on inputs
+     */
+    public get isUniform(): boolean {
+        return this._mode === NodeMaterialBlockConnectionPointMode.Uniform;
+    }
+
+    public set isUniform(value: boolean) {
+        this._mode = value ? NodeMaterialBlockConnectionPointMode.Uniform : NodeMaterialBlockConnectionPointMode.Undefined;
+        this.associatedVariableName = "";
+    }
+
+    /**
+     * Gets or sets a boolean indicating that this connection point is coming from an attribute.
+     * In this case the connection point name must be the name of the attribute to use
+     * Can only be set on inputs
+     */
+    public get isAttribute(): boolean {
+        return this._mode === NodeMaterialBlockConnectionPointMode.Attribute;
+    }
+
+    public set isAttribute(value: boolean) {
+        this._mode = value ? NodeMaterialBlockConnectionPointMode.Attribute : NodeMaterialBlockConnectionPointMode.Undefined;
+        this.associatedVariableName = "";
+    }
+
+    /**
+     * Gets or sets a boolean indicating that this connection point is generating a varying variable.
+     * Can only be set on exit points
+     */
+    public get isVarying(): boolean {
+        return this._mode === NodeMaterialBlockConnectionPointMode.Varying;
+    }
+
+    public set isVarying(value: boolean) {
+        this._mode = value ? NodeMaterialBlockConnectionPointMode.Varying : NodeMaterialBlockConnectionPointMode.Undefined;
+        this.associatedVariableName = "";
+    }
+
+    /**
+     * Gets a boolean indicating that the current connection point is a well known value
+     */
+    public get isWellKnownValue(): boolean {
+        return this._wellKnownValue != null;
+    }
+
+    /**
+     * Gets or sets the current well known value or null if not defined as well know value
+     */
+    public get wellKnownValue(): Nullable<NodeMaterialWellKnownValues> {
+        return this._wellKnownValue;
+    }
+
+    public set wellKnownValue(value: Nullable<NodeMaterialWellKnownValues>) {
+        this._mode = NodeMaterialBlockConnectionPointMode.Uniform;
+        this.associatedVariableName = "";
+        this._wellKnownValue = value;
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "InputBlock";
+    }
+
+    private _emitDefine(define: string): string {
+        if (define[0] === "!") {
+            return `#ifndef ${define.substring(1)}\r\n`;
+        }
+
+        return `#ifdef ${define}\r\n`;
+    }
+
+    /**
+     * Set the input block to its default value (based on its type)
+     */
+    public setDefaultValue() {
+        switch (this.type) {
+            case NodeMaterialBlockConnectionPointTypes.Float:
+                this.value = 0;
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector2:
+                this.value = Vector2.Zero();
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector3:
+            case NodeMaterialBlockConnectionPointTypes.Color3:
+            case NodeMaterialBlockConnectionPointTypes.Vector3OrColor3:
+                this.value = Vector3.Zero();
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Matrix:
+                this.value = Matrix.Identity();
+                break;
+        }
+    }
+
+    private _emit(state: NodeMaterialBuildState, define?: string) {
+        // Uniforms
+        if (this.isUniform) {
+            if (!this.associatedVariableName) {
+                this.associatedVariableName = state._getFreeVariableName("u_" + this.name);
+            }
+
+            if (state.uniforms.indexOf(this.associatedVariableName) !== -1) {
+                return;
+            }
+
+            state.uniforms.push(this.associatedVariableName);
+            if (define) {
+                state._uniformDeclaration += this._emitDefine(define);
+            }
+            state._uniformDeclaration += `uniform ${state._getGLType(this.type)} ${this.associatedVariableName};\r\n`;
+            if (define) {
+                state._uniformDeclaration += `#endif\r\n`;
+            }
+
+            // well known
+            let hints = state.sharedData.hints;
+            if (this._wellKnownValue !== null) {
+                switch (this._wellKnownValue) {
+                    case NodeMaterialWellKnownValues.WorldView:
+                        hints.needWorldViewMatrix = true;
+                        break;
+                    case NodeMaterialWellKnownValues.WorldViewProjection:
+                        hints.needWorldViewProjectionMatrix = true;
+                        break;
+                }
+            }
+
+            return;
+        }
+
+        // Attribute
+        if (this.isAttribute) {
+            this.associatedVariableName = this.name;
+
+            if (this.target === NodeMaterialBlockTargets.Vertex && state._vertexState) { // Attribute for fragment need to be carried over by varyings
+                this._emit(state._vertexState, define);
+                return;
+            }
+
+            if (state.attributes.indexOf(this.associatedVariableName) !== -1) {
+                return;
+            }
+
+            state.attributes.push(this.associatedVariableName);
+            if (define) {
+                state._attributeDeclaration += this._emitDefine(define);
+            }
+            state._attributeDeclaration += `attribute ${state._getGLType(this.type)} ${this.associatedVariableName};\r\n`;
+            if (define) {
+                state._attributeDeclaration += `#endif\r\n`;
+            }
+        }
+    }
+
+    /** @hidden */
+    public _transmitWorld(effect: Effect, world: Matrix, worldView: Matrix, worldViewProjection: Matrix) {
+        if (!this._wellKnownValue) {
+            return;
+        }
+
+        let variableName = this.associatedVariableName;
+        switch (this._wellKnownValue) {
+            case NodeMaterialWellKnownValues.World:
+                effect.setMatrix(variableName, world);
+                break;
+            case NodeMaterialWellKnownValues.WorldView:
+                effect.setMatrix(variableName, worldView);
+                break;
+            case NodeMaterialWellKnownValues.WorldViewProjection:
+                effect.setMatrix(variableName, worldViewProjection);
+                break;
+        }
+    }
+
+    /** @hidden */
+    public _transmit(effect: Effect, scene: Scene) {
+        if (this.isAttribute) {
+            return;
+        }
+
+        let variableName = this.associatedVariableName;
+        if (this._wellKnownValue) {
+            switch (this._wellKnownValue) {
+                case NodeMaterialWellKnownValues.World:
+                case NodeMaterialWellKnownValues.WorldView:
+                case NodeMaterialWellKnownValues.WorldViewProjection:
+                    return;
+                case NodeMaterialWellKnownValues.View:
+                    effect.setMatrix(variableName, scene.getViewMatrix());
+                    break;
+                case NodeMaterialWellKnownValues.Projection:
+                    effect.setMatrix(variableName, scene.getProjectionMatrix());
+                    break;
+                case NodeMaterialWellKnownValues.ViewProjection:
+                    effect.setMatrix(variableName, scene.getTransformMatrix());
+                    break;
+                case NodeMaterialWellKnownValues.CameraPosition:
+                    effect.setVector3(variableName, scene.activeCamera!.globalPosition);
+                    break;
+                case NodeMaterialWellKnownValues.FogColor:
+                    effect.setColor3(variableName, scene.fogColor);
+                    break;
+            }
+            return;
+        }
+
+        let value = this._valueCallback ? this._valueCallback() : this._storedValue;
+
+        if (value === null) {
+            return;
+        }
+
+        switch (this.type) {
+            case NodeMaterialBlockConnectionPointTypes.Float:
+                effect.setFloat(variableName, value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Int:
+                effect.setInt(variableName, value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Color3:
+                effect.setColor3(variableName, value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Color4:
+                effect.setDirectColor4(variableName, value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector2:
+                effect.setVector2(variableName, value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector3:
+                effect.setVector3(variableName, value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Color3OrColor4:
+                effect.setFloat4(variableName, value.r, value.g, value.b, value.a || 1.0);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector4OrColor4:
+            case NodeMaterialBlockConnectionPointTypes.Vector4:
+                effect.setVector4(variableName, value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Matrix:
+                effect.setMatrix(variableName, value);
+                break;
+        }
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        if (this.isUniform || this.isWellKnownValue) {
+            state.sharedData.inputBlocks.push(this);
+        }
+
+        this._emit(state);
+    }
+}

+ 21 - 10
src/Materials/Node/Blocks/Vertex/bonesBlock.ts

@@ -9,6 +9,7 @@ import { Effect, EffectFallbacks } from '../../../effect';
 import { MaterialHelper } from '../../../materialHelper';
 import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
 import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { InputBlock } from '../Input/inputBlock';
 
 /**
  * Block used to add support for vertex skinning (bones)
@@ -92,20 +93,30 @@ export class BonesBlock extends NodeMaterialBlock {
     }
 
     public autoConfigure() {
-        if (this.matricesIndices.isUndefined) {
-            this.matricesIndices.setAsAttribute();
+        if (!this.matricesIndices.isConnected) {
+            let matricesIndicesInput = new InputBlock("matricesIndices", undefined, NodeMaterialBlockConnectionPointTypes.Vector4);
+            matricesIndicesInput.setAsAttribute("matricesIndices");
+            matricesIndicesInput.output.connectTo(this.matricesIndices);
         }
-        if (this.matricesWeights.isUndefined) {
-            this.matricesWeights.setAsAttribute();
+        if (!this.matricesWeights.isConnected) {
+            let matricesWeightsInput = new InputBlock("matricesWeights", undefined, NodeMaterialBlockConnectionPointTypes.Vector4);
+            matricesWeightsInput.setAsAttribute("matricesWeights");
+            matricesWeightsInput.output.connectTo(this.matricesWeights);
         }
-        if (this.matricesIndicesExtra.isUndefined) {
-            this.matricesIndicesExtra.setAsAttribute();
+        if (!this.matricesIndicesExtra.isConnected) {
+            let matricesIndicesExtraInput = new InputBlock("matricesIndicesExtra", undefined, NodeMaterialBlockConnectionPointTypes.Vector4);
+            matricesIndicesExtraInput.setAsAttribute("matricesIndicesExtra");
+            matricesIndicesExtraInput.output.connectTo(this.matricesIndicesExtra);
         }
-        if (this.matricesWeightsExtra.isUndefined) {
-            this.matricesWeightsExtra.setAsAttribute();
+        if (!this.matricesWeightsExtra.isConnected) {
+            let matricesWeightsExtraInput = new InputBlock("matricesWeightsExtra", undefined, NodeMaterialBlockConnectionPointTypes.Vector4);
+            matricesWeightsExtraInput.setAsAttribute("matricesWeightsExtra");
+            matricesWeightsExtraInput.output.connectTo(this.matricesWeightsExtra);
         }
-        if (this.world.isUndefined) {
-            this.world.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+        if (!this.world.isConnected) {
+            let worldInput = new InputBlock("world", undefined, NodeMaterialBlockConnectionPointTypes.Matrix);
+            worldInput.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+            worldInput.output.connectTo(this.world);
         }
     }
 

+ 16 - 5
src/Materials/Node/Blocks/Vertex/instancesBlock.ts

@@ -6,6 +6,7 @@ import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { AbstractMesh } from '../../../../Meshes/abstractMesh';
 import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
 import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+import { InputBlock } from '../Input/inputBlock';
 
 /**
  * Block used to add support for instances
@@ -80,19 +81,29 @@ export class InstancesBlock extends NodeMaterialBlock {
 
     public autoConfigure() {
         if (!this.world0.connectedPoint) {
-            this.world0.setAsAttribute();
+            let world0Input = new InputBlock("world0");
+            world0Input.setAsAttribute("world0");
+            world0Input.output.connectTo(this.world0);
         }
         if (!this.world1.connectedPoint) {
-            this.world1.setAsAttribute();
+            let world1Input = new InputBlock("world1");
+            world1Input.setAsAttribute("world1");
+            world1Input.output.connectTo(this.world1);
         }
         if (!this.world2.connectedPoint) {
-            this.world2.setAsAttribute();
+            let world2Input = new InputBlock("world2");
+            world2Input.setAsAttribute("world2");
+            world2Input.output.connectTo(this.world2);
         }
         if (!this.world3.connectedPoint) {
-            this.world3.setAsAttribute();
+            let world3Input = new InputBlock("world3");
+            world3Input.setAsAttribute("world3");
+            world3Input.output.connectTo(this.world3);
         }
         if (!this.world.connectedPoint) {
-            this.world.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+            let worldInput = new InputBlock("world");
+            worldInput.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+            worldInput.output.connectTo(this.world);
         }
 
         this.world.define = "!INSTANCES";

+ 18 - 8
src/Materials/Node/Blocks/Vertex/morphTargetsBlock.ts

@@ -9,6 +9,7 @@ import { Effect } from '../../../effect';
 import { Mesh } from '../../../../Meshes/mesh';
 import { MaterialHelper } from '../../../materialHelper';
 import { VertexBuffer } from '../../../../Meshes/buffer';
+import { InputBlock } from '../Input/inputBlock';
 
 /**
  * Block used to add morph targets support to vertex shader
@@ -103,16 +104,25 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
     }
 
     public autoConfigure() {
-        if (this.position.isUndefined) {
-            this.position.setAsAttribute();
+        if (!this.position.isConnected) {
+            let positionInput = new InputBlock("position");
+            positionInput.setAsAttribute("position");
+            positionInput.output.connectTo(this.position);
         }
-        if (this.normal.isUndefined) {
-            this.normal.setAsAttribute();
-            this.normal.define = "NORMAL";
+        if (!this.normal.isConnected) {
+            let normalInput = new InputBlock("normal");
+            normalInput.setAsAttribute("normal");
+            normalInput.output.connectTo(this.normal);
         }
-        if (this.tangent.isUndefined) {
-            this.tangent.setAsAttribute();
-            this.tangent.define = "TANGENT";
+        if (!this.tangent.isConnected) {
+            let tangentInput = new InputBlock("tangent");
+            tangentInput.setAsAttribute("tangent");
+            tangentInput.output.connectTo(this.tangent);
+        }
+        if (!this.uv.isConnected) {
+            let uvInput = new InputBlock("uv");
+            uvInput.setAsAttribute("uv");
+            uvInput.output.connectTo(this.uv);
         }
     }
 

+ 2 - 1
src/Materials/Node/Blocks/addBlock.ts

@@ -2,6 +2,7 @@ import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 import { NodeMaterialConnectionPoint } from '../nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 /**
  * Block used to add 2 vector4
  */
@@ -11,7 +12,7 @@ export class AddBlock extends NodeMaterialBlock {
      * @param name defines the block name
      */
     public constructor(name: string) {
-        super(name);
+        super(name, NodeMaterialBlockTargets.Neutral);
 
         this.registerInput("left", NodeMaterialBlockConnectionPointTypes.AutoDetect);
         this.registerInput("right", NodeMaterialBlockConnectionPointTypes.AutoDetect);

+ 0 - 0
src/Materials/Node/Blocks/clampBlock.ts


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