Browse Source

Merge pull request #6339 from BabylonJS/master

Nightly
David Catuhe 6 years ago
parent
commit
e6a5479738
100 changed files with 218266 additions and 83284 deletions
  1. 1388 1
      Playground/babylon.d.txt
  2. 56 2
      Tools/Config/config.json
  3. 1 1
      Tools/Gulp/gulpfile.js
  4. 6 0
      Tools/Gulp/tasks/gulpTasks-intellisense.js
  5. 37 38
      bower.json
  6. 1407 1
      dist/preview release/babylon.d.ts
  7. 2 2
      dist/preview release/babylon.js
  8. 4921 358
      dist/preview release/babylon.max.js
  9. 1 1
      dist/preview release/babylon.max.js.map
  10. 44003 41029
      dist/preview release/babylon.module.d.ts
  11. 68164 0
      dist/preview release/documentation.d.ts
  12. 1 1
      dist/preview release/glTF2Interface/package.json
  13. 40 40
      dist/preview release/gui/babylon.gui.js
  14. 1 1
      dist/preview release/gui/babylon.gui.js.map
  15. 2 2
      dist/preview release/gui/package.json
  16. 7 7
      dist/preview release/inspector/babylon.inspector.bundle.js
  17. 132 147
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  18. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  19. 26 23
      dist/preview release/inspector/babylon.inspector.d.ts
  20. 54 47
      dist/preview release/inspector/babylon.inspector.module.d.ts
  21. 6 6
      dist/preview release/inspector/package.json
  22. 3 3
      dist/preview release/loaders/package.json
  23. 2 2
      dist/preview release/materialsLibrary/package.json
  24. 353 0
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  25. 41 0
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  26. 48284 0
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  27. 1 0
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  28. 770 0
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  29. 27 0
      dist/preview release/nodeEditor/package.json
  30. 1 0
      dist/preview release/nodeEditor/readme-es6.md
  31. 1 0
      dist/preview release/nodeEditor/readme.md
  32. 1 1
      dist/preview release/package.json
  33. 1 1
      dist/preview release/packagesSizeBaseLine.json
  34. 2 2
      dist/preview release/postProcessesLibrary/package.json
  35. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  36. 3 3
      dist/preview release/serializers/package.json
  37. 44003 41029
      dist/preview release/viewer/babylon.module.d.ts
  38. 460 320
      dist/preview release/viewer/babylon.viewer.js
  39. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  40. 3 0
      dist/preview release/what's new.md
  41. 2 2
      inspector/README.md
  42. 1 0
      inspector/src/components/actionTabs/actionTabs.scss
  43. 46 33
      inspector/src/components/actionTabs/actionTabsComponent.tsx
  44. 4 4
      inspector/src/components/actionTabs/lineContainerComponent.tsx
  45. 17 5
      inspector/src/components/actionTabs/lines/textureLineComponent.tsx
  46. 25 0
      inspector/src/components/actionTabs/lines/textureLinkLineComponent.tsx
  47. 10 14
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx
  48. 6 11
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMetallicRoughnessMaterialPropertyGridComponent.tsx
  49. 6 11
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrSpecularGlossinessMaterialPropertyGridComponent.tsx
  50. 9 13
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/standardMaterialPropertyGridComponent.tsx
  51. 0 0
      inspector/src/index.css
  52. 0 2
      inspector/src/index.ts
  53. 25 20
      inspector/src/inspector.ts
  54. BIN
      inspector/test/environment.dds
  55. BIN
      inspector/test/explosion.wav
  56. 0 91
      inspector/test/index.js
  57. BIN
      inspector/test/jump.wav
  58. BIN
      inspector/test/test_1.dds
  59. 1 0
      nodeEditor/README-ES6.md
  60. 18 0
      nodeEditor/README.md
  61. 34 0
      nodeEditor/src/components/customDiragramNodes/generic/genericNodeFactory.tsx
  62. 50 0
      nodeEditor/src/components/customDiragramNodes/generic/genericNodeModel.ts
  63. 162 0
      nodeEditor/src/components/customDiragramNodes/generic/genericNodeWidget.tsx
  64. 73 0
      nodeEditor/src/components/customDiragramNodes/generic/genericPortModel.ts
  65. 385 0
      nodeEditor/src/components/graphEditor.tsx
  66. 892 0
      nodeEditor/src/components/main.scss
  67. 6 0
      nodeEditor/src/globalState.ts
  68. 1 0
      nodeEditor/src/index.ts
  69. 9 0
      nodeEditor/src/legacy/legacy.ts
  70. 57 0
      nodeEditor/src/nodeEditor.ts
  71. 21 0
      nodeEditor/src/sharedComponents/buttonLineComponent.tsx
  72. 33 0
      nodeEditor/src/sharedComponents/fileButtonLineComponent.tsx
  73. 120 0
      nodeEditor/src/sharedComponents/lineContainerComponent.tsx
  74. 69 0
      nodeEditor/src/sharedComponents/numericInputComponent.tsx
  75. 70 0
      nodeEditor/src/sharedComponents/popup.ts
  76. 6 0
      nodeEditor/src/sharedComponents/propertyChangedEvent.ts
  77. 202 0
      nodeEditor/src/sharedComponents/textureLineComponent.tsx
  78. 114 0
      nodeEditor/src/sharedComponents/vector2LineComponent.tsx
  79. 124 0
      nodeEditor/src/sharedComponents/vector3LineComponent.tsx
  80. 28 0
      nodeEditor/tsconfig.json
  81. 34 0
      nodeEditor/webpack.config.js
  82. 3 2
      package.json
  83. 2 2
      src/Engines/engine.ts
  84. 2 2
      src/Loading/sceneLoader.ts
  85. 143 0
      src/Materials/Node/Blocks/Dual/fogBlock.ts
  86. 2 0
      src/Materials/Node/Blocks/Dual/index.ts
  87. 51 0
      src/Materials/Node/Blocks/Fragment/alphaTestBlock.ts
  88. 54 0
      src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts
  89. 134 0
      src/Materials/Node/Blocks/Fragment/imageProcessingBlock.ts
  90. 9 0
      src/Materials/Node/Blocks/Fragment/index.ts
  91. 67 0
      src/Materials/Node/Blocks/Fragment/rgbMergerBlock.ts
  92. 59 0
      src/Materials/Node/Blocks/Fragment/rgbSplitterBlock.ts
  93. 88 0
      src/Materials/Node/Blocks/Fragment/rgbaMergerBlock.ts
  94. 65 0
      src/Materials/Node/Blocks/Fragment/rgbaSplitterBlock.ts
  95. 194 0
      src/Materials/Node/Blocks/Fragment/textureBlock.ts
  96. 170 0
      src/Materials/Node/Blocks/Vertex/bonesBlock.ts
  97. 4 0
      src/Materials/Node/Blocks/Vertex/index.ts
  98. 133 0
      src/Materials/Node/Blocks/Vertex/instancesBlock.ts
  99. 211 0
      src/Materials/Node/Blocks/Vertex/morphTargetsBlock.ts
  100. 0 0
      src/Materials/Node/Blocks/Vertex/vertexOutputBlock.ts

File diff suppressed because it is too large
+ 1388 - 1
Playground/babylon.d.txt


+ 56 - 2
Tools/Config/config.json

@@ -13,6 +13,7 @@
         "packageES6DevFolderName": "packageES6Dev",
         "playgroundDirectory": "../../Playground/",
         "intellisenseFile": "babylon.d.txt",
+        "documentationFile": "documentation.d.ts",
         "intellisenseSources": [
             "../../dist/preview release/babylon.d.ts",
             "../../dist/preview release/gui/babylon.gui.d.ts",
@@ -45,7 +46,8 @@
         "loaders",
         "serializers",
         "gui",
-        "inspector"
+        "inspector",
+        "nodeEditor"
     ],
     "es6modules": [
         "core",
@@ -56,7 +58,8 @@
         "serializers",
         "gui",
         "inspector",
-        "viewer"
+        "viewer",
+        "nodeEditor"
     ],
     "lintModules": [
         "core",
@@ -580,6 +583,57 @@
             }
         }
     },
+    "nodeEditor": {
+        "libraries": [
+            {
+                "output": "babylon.nodeEditor.js",
+                "entry": "./legacy/legacy.ts"
+            }
+        ],
+        "build": {
+            "ignoreInTestMode": true,
+            "mainFolder": "./nodeEditor/",
+            "uncheckedLintImports": [
+                "react",
+                "react-dom",
+                "re-resizable",
+                "glTF"
+            ],
+            "umd": {
+                "packageName": "babylonjs-node-editor",
+                "webpackRoot": "NODEEDITOR",
+                "processDeclaration": {
+                    "filename": "babylon.nodeEditor.module.d.ts",
+                    "moduleName": "NODEEDITOR",
+                    "importsToRemove": [],
+                    "classMap": {
+                        "babylonjs": "BABYLON",
+                        "react": "React",
+                        "@babylonjs/core": "BABYLON",
+                        "@fortawesome": false,
+                        "react-contextmenu": false
+                    }
+                }
+            },
+            "es6": {
+                "webpackBuild": true,
+                "buildDependencies": [
+                    "node_modules/re-resizable/lib/index.es5.js",
+                    "Tools/**/*"
+                ],
+                "packageName": "@babylonjs/node-editor",
+                "readme": "dist/preview release/nodeEditor/readme-es6.md",
+                "packagesFiles": [
+                    "babylon.nodeEditor.max.js",
+                    "babylon.nodeEditor.max.js.map",
+                    "babylon.nodeEditor.module.d.ts",
+                    "readme.md"
+                ],
+                "typings": "babylon.nodeEditor.module.d.ts",
+                "index": "babylon.nodeEditor.max.js"
+            }
+        }
+    },
     "viewer": {
         "libraries": [
             {

+ 1 - 1
Tools/Gulp/gulpfile.js

@@ -90,4 +90,4 @@ gulp.task("npmPackages", gulp.series("npmPackages-all"));
 /**
  * The default task, concat and min the main BJS files.
  */
-gulp.task("default", gulp.series("cleanup", "tsLint", "importLint", "circularDependencies", "typescript-all", "intellisense", "typedoc-all", "tests-all"));
+gulp.task("default", gulp.series("cleanup", "tsLint", "importLint", "circularDependencies", "typescript-all", "intellisense", "documentation", "typedoc-all", "tests-all"));

+ 6 - 0
Tools/Gulp/tasks/gulpTasks-intellisense.js

@@ -18,4 +18,10 @@ gulp.task("intellisense", function() {
         .pipe(replace(/^\s* _/gm, "private _"))
         .pipe(replace(/^\s*_/gm, "private _"))
         .pipe(gulp.dest(config.build.playgroundDirectory));
+});
+
+gulp.task("documentation", function() {
+    return gulp.src(config.build.intellisenseSources)
+        .pipe(concat(config.build.documentationFile))
+        .pipe(gulp.dest(config.build.outputDirectory));
 });

+ 37 - 38
bower.json

@@ -1,39 +1,38 @@
 {
-  "name": "babylonjs",
-  "description": "Babylon.js is a complete JavaScript framework for building 3D games with HTML 5 and WebGL",
-  "main": "./dist/babylon.2.5.js",
-  "homepage": "https://www.babylonjs.com",
-  "repository": {
-    "type": "git",
-    "url": "git://github.com/BabylonJS/Babylon.js.git"
-  },
-  "authors": [
-    "David Catuhe",
-    "David Rousset"
-  ],
-  "keywords": [
-    "3D",
-    "WebGL",
-    "WebAudio",
-    "Shaders",
-    "Realtime"
-  ],
-  "license": "Apache-2.0",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "Babylon",
-    "Exporters",
-    "Loaders",
-    "Previous releases",
-    "/Tools",
-    "gulpfile.js",
-    "package.json",
-    "babylon.2.1*.*",
-    "*.md",
-    "*.yml",
-    "cannon.js",
-    "Oimo.js",
-    "poly2tri.js"
-  ]
-}
+    "name": "babylonjs",
+    "description": "Babylon.js is a complete JavaScript framework for building 3D games with HTML 5 and WebGL",
+    "main": "./dist/babylon.js",
+    "homepage": "https://www.babylonjs.com",
+    "repository": {
+        "type": "git",
+        "url": "git://github.com/BabylonJS/Babylon.js.git"
+    },
+    "authors": [
+        "David Catuhe"
+    ],
+    "keywords": [
+        "3D",
+        "WebGL",
+        "WebAudio",
+        "Shaders",
+        "Realtime"
+    ],
+    "license": "Apache-2.0",
+    "ignore": [
+        "**/.*",
+        "node_modules",
+        "Babylon",
+        "Exporters",
+        "Loaders",
+        "Previous releases",
+        "/Tools",
+        "gulpfile.js",
+        "package.json",
+        "babylon.2.1*.*",
+        "*.md",
+        "*.yml",
+        "cannon.js",
+        "Oimo.js",
+        "poly2tri.js"
+    ]
+}

File diff suppressed because it is too large
+ 1407 - 1
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 2 - 2
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 4921 - 358
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.max.js.map


File diff suppressed because it is too large
+ 44003 - 41029
dist/preview release/babylon.module.d.ts


File diff suppressed because it is too large
+ 68164 - 0
dist/preview release/documentation.d.ts


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

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

+ 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_tools__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__) {
 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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___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/tools");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -15636,14 +15636,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Misc/tools":
+/***/ "babylonjs/Misc/observable":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_tools__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__;
 
 /***/ })
 

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


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

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

File diff suppressed because it is too large
+ 7 - 7
dist/preview release/inspector/babylon.inspector.bundle.js


File diff suppressed because it is too large
+ 132 - 147
dist/preview release/inspector/babylon.inspector.bundle.max.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


+ 26 - 23
dist/preview release/inspector/babylon.inspector.d.ts

@@ -90,7 +90,7 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface ILineContainerComponentProps {
-        globalState: GlobalState;
+        globalState?: GlobalState;
         title: string;
         children: any[] | any;
         closed?: boolean;
@@ -400,12 +400,25 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface IFileButtonLineComponentProps {
+        label: string;
+        onClick: (file: File) => void;
+        accept: string;
+    }
+    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        constructor(props: IFileButtonLineComponentProps);
+        onChange(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     export interface ITextureLinkLineComponentProps {
         label: string;
         texture: BABYLON.Nullable<BABYLON.BaseTexture>;
         material?: BABYLON.Material;
         onSelectionChangedObservable?: BABYLON.Observable<any>;
         onDebugSelectionChangeObservable?: BABYLON.Observable<BABYLON.BaseTexture>;
+        propertyName?: string;
     }
     export class TextureLinkLineComponent extends React.Component<ITextureLinkLineComponentProps, {
         isDebugSelected: boolean;
@@ -416,6 +429,7 @@ declare module INSPECTOR {
         componentWillUnmount(): void;
         debugTexture(): void;
         onLink(): void;
+        updateTexture(file: File): void;
         render(): JSX.Element | null;
     }
 }
@@ -430,7 +444,7 @@ declare module INSPECTOR {
     export class StandardMaterialPropertyGridComponent extends React.Component<IStandardMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IStandardMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -439,7 +453,8 @@ declare module INSPECTOR {
         texture: BABYLON.BaseTexture;
         width: number;
         height: number;
-        globalState: GlobalState;
+        globalState?: GlobalState;
+        hideChannelSelect?: boolean;
     }
     export class TextureLineComponent extends React.Component<ITextureLineComponentProps, {
         displayRed: boolean;
@@ -491,18 +506,6 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface IFileButtonLineComponentProps {
-        label: string;
-        onClick: (file: File) => void;
-        accept: string;
-    }
-    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
-        constructor(props: IFileButtonLineComponentProps);
-        onChange(evt: any): void;
-        render(): JSX.Element;
-    }
-}
-declare module INSPECTOR {
     interface ITexturePropertyGridComponentProps {
         texture: BABYLON.BaseTexture;
         lockObject: LockObject;
@@ -558,7 +561,7 @@ declare module INSPECTOR {
     export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRMaterialPropertyGridComponentProps);
-        renderTextures(onDebugSelectionChangeObservable: BABYLON.Observable<BABYLON.BaseTexture>): JSX.Element | null;
+        renderTextures(onDebugSelectionChangeObservable: BABYLON.Observable<BABYLON.BaseTexture>): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -1006,7 +1009,7 @@ declare module INSPECTOR {
     export class PBRMetallicRoughnessMaterialPropertyGridComponent extends React.Component<IPBRMetallicRoughnessMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRMetallicRoughnessMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -1021,7 +1024,7 @@ declare module INSPECTOR {
     export class PBRSpecularGlossinessMaterialPropertyGridComponent extends React.Component<IPBRSpecularGlossinessMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRSpecularGlossinessMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -1289,7 +1292,7 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface IActionTabsComponentProps {
-        scene: BABYLON.Scene;
+        scene?: BABYLON.Scene;
         noCommands?: boolean;
         noHeader?: boolean;
         noExpand?: boolean;
@@ -1297,7 +1300,7 @@ declare module INSPECTOR {
         popupMode?: boolean;
         onPopup?: () => void;
         onClose?: () => void;
-        globalState: GlobalState;
+        globalState?: GlobalState;
     }
     export class ActionTabsComponent extends React.Component<IActionTabsComponentProps, {
         selectedEntity: any;
@@ -1310,7 +1313,7 @@ declare module INSPECTOR {
         componentWillMount(): void;
         componentWillUnmount(): void;
         changeSelectedTab(index: number): void;
-        renderContent(): JSX.Element;
+        renderContent(): JSX.Element | null;
         onClose(): void;
         onPopup(): void;
         render(): JSX.Element;
@@ -1713,11 +1716,11 @@ declare module INSPECTOR {
         private static _CreateSceneExplorer;
         private static _CreateActionTabs;
         private static _CreateEmbedHost;
-        private static _CreatePopup;
+        static _CreatePopup(title: string, windowVariableName: string, width?: number, height?: number): BABYLON.Nullable<HTMLDivElement>;
         static readonly IsVisible: boolean;
         static EarlyAttachToLoader(): void;
         static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
-        private static _CreateCanvasContainer;
+        static _CreateCanvasContainer(parentControl: HTMLElement): void;
         private static _DestroyCanvasContainer;
         private static _Cleanup;
         private static _RemoveElementFromDOM;

+ 54 - 47
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -111,7 +111,7 @@ declare module "babylonjs-inspector/components/actionTabs/lineContainerComponent
     import * as React from "react";
     import { GlobalState } from "babylonjs-inspector/components/globalState";
     interface ILineContainerComponentProps {
-        globalState: GlobalState;
+        globalState?: GlobalState;
         title: string;
         children: any[] | any;
         closed?: boolean;
@@ -466,6 +466,19 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/actionTabs/lines/fileButtonLineComponent" {
+    import * as React from "react";
+    interface IFileButtonLineComponentProps {
+        label: string;
+        onClick: (file: File) => void;
+        accept: string;
+    }
+    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        constructor(props: IFileButtonLineComponentProps);
+        onChange(evt: any): void;
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/lines/textureLinkLineComponent" {
     import * as React from "react";
     import { Nullable } from "babylonjs/types";
@@ -478,6 +491,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/textureLinkLineC
         material?: Material;
         onSelectionChangedObservable?: Observable<any>;
         onDebugSelectionChangeObservable?: Observable<BaseTexture>;
+        propertyName?: string;
     }
     export class TextureLinkLineComponent extends React.Component<ITextureLinkLineComponentProps, {
         isDebugSelected: boolean;
@@ -488,6 +502,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/textureLinkLineC
         componentWillUnmount(): void;
         debugTexture(): void;
         onLink(): void;
+        updateTexture(file: File): void;
         render(): JSX.Element | null;
     }
 }
@@ -508,7 +523,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     export class StandardMaterialPropertyGridComponent extends React.Component<IStandardMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IStandardMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -520,7 +535,8 @@ declare module "babylonjs-inspector/components/actionTabs/lines/textureLineCompo
         texture: BaseTexture;
         width: number;
         height: number;
-        globalState: GlobalState;
+        globalState?: GlobalState;
+        hideChannelSelect?: boolean;
     }
     export class TextureLineComponent extends React.Component<ITextureLineComponentProps, {
         displayRed: boolean;
@@ -575,19 +591,6 @@ declare module "babylonjs-inspector/components/actionTabs/lines/floatLineCompone
         render(): JSX.Element;
     }
 }
-declare module "babylonjs-inspector/components/actionTabs/lines/fileButtonLineComponent" {
-    import * as React from "react";
-    interface IFileButtonLineComponentProps {
-        label: string;
-        onClick: (file: File) => void;
-        accept: string;
-    }
-    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
-        constructor(props: IFileButtonLineComponentProps);
-        onChange(evt: any): void;
-        render(): JSX.Element;
-    }
-}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent" {
     import * as React from "react";
     import { Observable } from "babylonjs/Misc/observable";
@@ -661,7 +664,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRMaterialPropertyGridComponentProps);
-        renderTextures(onDebugSelectionChangeObservable: Observable<BaseTexture>): JSX.Element | null;
+        renderTextures(onDebugSelectionChangeObservable: Observable<BaseTexture>): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -1292,7 +1295,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     export class PBRMetallicRoughnessMaterialPropertyGridComponent extends React.Component<IPBRMetallicRoughnessMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRMetallicRoughnessMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -1313,7 +1316,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     export class PBRSpecularGlossinessMaterialPropertyGridComponent extends React.Component<IPBRSpecularGlossinessMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRSpecularGlossinessMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -1678,7 +1681,7 @@ declare module "babylonjs-inspector/components/actionTabs/actionTabsComponent" {
     import { Scene } from "babylonjs/scene";
     import { GlobalState } from "babylonjs-inspector/components/globalState";
     interface IActionTabsComponentProps {
-        scene: Scene;
+        scene?: Scene;
         noCommands?: boolean;
         noHeader?: boolean;
         noExpand?: boolean;
@@ -1686,7 +1689,7 @@ declare module "babylonjs-inspector/components/actionTabs/actionTabsComponent" {
         popupMode?: boolean;
         onPopup?: () => void;
         onClose?: () => void;
-        globalState: GlobalState;
+        globalState?: GlobalState;
     }
     export class ActionTabsComponent extends React.Component<IActionTabsComponentProps, {
         selectedEntity: any;
@@ -1699,7 +1702,7 @@ declare module "babylonjs-inspector/components/actionTabs/actionTabsComponent" {
         componentWillMount(): void;
         componentWillUnmount(): void;
         changeSelectedTab(index: number): void;
-        renderContent(): JSX.Element;
+        renderContent(): JSX.Element | null;
         onClose(): void;
         onPopup(): void;
         render(): JSX.Element;
@@ -2152,6 +2155,7 @@ declare module "babylonjs-inspector/components/embedHost/embedHostComponent" {
 }
 declare module "babylonjs-inspector/inspector" {
     import { IInspectorOptions } from "babylonjs/Debug/debugLayer";
+    import { Nullable } from "babylonjs/types";
     import { Observable } from "babylonjs/Misc/observable";
     import { Scene } from "babylonjs/scene";
     import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
@@ -2174,11 +2178,11 @@ declare module "babylonjs-inspector/inspector" {
         private static _CreateSceneExplorer;
         private static _CreateActionTabs;
         private static _CreateEmbedHost;
-        private static _CreatePopup;
+        static _CreatePopup(title: string, windowVariableName: string, width?: number, height?: number): Nullable<HTMLDivElement>;
         static readonly IsVisible: boolean;
         static EarlyAttachToLoader(): void;
         static Show(scene: Scene, userOptions: Partial<IInspectorOptions>): void;
-        private static _CreateCanvasContainer;
+        static _CreateCanvasContainer(parentControl: HTMLElement): void;
         private static _DestroyCanvasContainer;
         private static _Cleanup;
         private static _RemoveElementFromDOM;
@@ -2286,7 +2290,7 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface ILineContainerComponentProps {
-        globalState: GlobalState;
+        globalState?: GlobalState;
         title: string;
         children: any[] | any;
         closed?: boolean;
@@ -2596,12 +2600,25 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface IFileButtonLineComponentProps {
+        label: string;
+        onClick: (file: File) => void;
+        accept: string;
+    }
+    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        constructor(props: IFileButtonLineComponentProps);
+        onChange(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     export interface ITextureLinkLineComponentProps {
         label: string;
         texture: BABYLON.Nullable<BABYLON.BaseTexture>;
         material?: BABYLON.Material;
         onSelectionChangedObservable?: BABYLON.Observable<any>;
         onDebugSelectionChangeObservable?: BABYLON.Observable<BABYLON.BaseTexture>;
+        propertyName?: string;
     }
     export class TextureLinkLineComponent extends React.Component<ITextureLinkLineComponentProps, {
         isDebugSelected: boolean;
@@ -2612,6 +2629,7 @@ declare module INSPECTOR {
         componentWillUnmount(): void;
         debugTexture(): void;
         onLink(): void;
+        updateTexture(file: File): void;
         render(): JSX.Element | null;
     }
 }
@@ -2626,7 +2644,7 @@ declare module INSPECTOR {
     export class StandardMaterialPropertyGridComponent extends React.Component<IStandardMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IStandardMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -2635,7 +2653,8 @@ declare module INSPECTOR {
         texture: BABYLON.BaseTexture;
         width: number;
         height: number;
-        globalState: GlobalState;
+        globalState?: GlobalState;
+        hideChannelSelect?: boolean;
     }
     export class TextureLineComponent extends React.Component<ITextureLineComponentProps, {
         displayRed: boolean;
@@ -2687,18 +2706,6 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface IFileButtonLineComponentProps {
-        label: string;
-        onClick: (file: File) => void;
-        accept: string;
-    }
-    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
-        constructor(props: IFileButtonLineComponentProps);
-        onChange(evt: any): void;
-        render(): JSX.Element;
-    }
-}
-declare module INSPECTOR {
     interface ITexturePropertyGridComponentProps {
         texture: BABYLON.BaseTexture;
         lockObject: LockObject;
@@ -2754,7 +2761,7 @@ declare module INSPECTOR {
     export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRMaterialPropertyGridComponentProps);
-        renderTextures(onDebugSelectionChangeObservable: BABYLON.Observable<BABYLON.BaseTexture>): JSX.Element | null;
+        renderTextures(onDebugSelectionChangeObservable: BABYLON.Observable<BABYLON.BaseTexture>): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -3202,7 +3209,7 @@ declare module INSPECTOR {
     export class PBRMetallicRoughnessMaterialPropertyGridComponent extends React.Component<IPBRMetallicRoughnessMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRMetallicRoughnessMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -3217,7 +3224,7 @@ declare module INSPECTOR {
     export class PBRSpecularGlossinessMaterialPropertyGridComponent extends React.Component<IPBRSpecularGlossinessMaterialPropertyGridComponentProps> {
         private _onDebugSelectionChangeObservable;
         constructor(props: IPBRSpecularGlossinessMaterialPropertyGridComponentProps);
-        renderTextures(): JSX.Element | null;
+        renderTextures(): JSX.Element;
         render(): JSX.Element;
     }
 }
@@ -3485,7 +3492,7 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface IActionTabsComponentProps {
-        scene: BABYLON.Scene;
+        scene?: BABYLON.Scene;
         noCommands?: boolean;
         noHeader?: boolean;
         noExpand?: boolean;
@@ -3493,7 +3500,7 @@ declare module INSPECTOR {
         popupMode?: boolean;
         onPopup?: () => void;
         onClose?: () => void;
-        globalState: GlobalState;
+        globalState?: GlobalState;
     }
     export class ActionTabsComponent extends React.Component<IActionTabsComponentProps, {
         selectedEntity: any;
@@ -3506,7 +3513,7 @@ declare module INSPECTOR {
         componentWillMount(): void;
         componentWillUnmount(): void;
         changeSelectedTab(index: number): void;
-        renderContent(): JSX.Element;
+        renderContent(): JSX.Element | null;
         onClose(): void;
         onPopup(): void;
         render(): JSX.Element;
@@ -3909,11 +3916,11 @@ declare module INSPECTOR {
         private static _CreateSceneExplorer;
         private static _CreateActionTabs;
         private static _CreateEmbedHost;
-        private static _CreatePopup;
+        static _CreatePopup(title: string, windowVariableName: string, width?: number, height?: number): BABYLON.Nullable<HTMLDivElement>;
         static readonly IsVisible: boolean;
         static EarlyAttachToLoader(): void;
         static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
-        private static _CreateCanvasContainer;
+        static _CreateCanvasContainer(parentControl: HTMLElement): void;
         private static _DestroyCanvasContainer;
         private static _Cleanup;
         private static _RemoveElementFromDOM;

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.1.0-alpha.0",
+    "version": "4.1.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -31,11 +31,11 @@
     "dependencies": {
         "@types/react": "~16.7.3",
         "@types/react-dom": "~16.0.9",
-        "babylonjs": "4.1.0-alpha.0",
-        "babylonjs-gui": "4.1.0-alpha.0",
-        "babylonjs-loaders": "4.1.0-alpha.0",
-        "babylonjs-serializers": "4.1.0-alpha.0",
-        "babylonjs-gltf2interface": "4.1.0-alpha.0"
+        "babylonjs": "4.1.0-alpha.1",
+        "babylonjs-gui": "4.1.0-alpha.1",
+        "babylonjs-loaders": "4.1.0-alpha.1",
+        "babylonjs-serializers": "4.1.0-alpha.1",
+        "babylonjs-gltf2interface": "4.1.0-alpha.1"
     },
     "engines": {
         "node": "*"

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

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

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

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

+ 353 - 0
dist/preview release/nodeEditor/babylon.nodeEditor.d.ts

@@ -0,0 +1,353 @@
+/// <reference types="react" />
+declare module NODEEDITOR {
+    export class GlobalState {
+        nodeMaterial?: BABYLON.NodeMaterial;
+        hostDocument?: BABYLON.Nullable<Document>;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Port model for the generic node
+     */
+    export class GenericPortModel extends PortModel {
+        /**
+         * If the port is input or output
+         */
+        position: string | "input" | "output";
+        /**
+         * What the port is connected to
+         */
+        connection: BABYLON.Nullable<BABYLON.NodeMaterialConnectionPoint>;
+        static idCounter: number;
+        constructor(name: string, type?: string);
+        syncWithNodeMaterialConnectionPoint(connection: BABYLON.NodeMaterialConnectionPoint): void;
+        getNodeModel(): GenericNodeModel;
+        link(outPort: GenericPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
+        getInputFromBlock(): void;
+        createLinkModel(): LinkModel;
+        getValue: Function;
+        static SortInputOutput(a: BABYLON.Nullable<GenericPortModel>, b: BABYLON.Nullable<GenericPortModel>): {
+            input: GenericPortModel;
+            output: GenericPortModel;
+        } | null;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Generic node model which stores information about a node editor block
+     */
+    export class GenericNodeModel extends NodeModel {
+        /**
+         * The babylon block this node represents
+         */
+        block: BABYLON.Nullable<BABYLON.NodeMaterialBlock>;
+        /**
+         * Labels for the block
+         */
+        headerLabels: Array<{
+            text: string;
+        }>;
+        /**
+         * BABYLON.Texture for the node if it exists
+         */
+        texture: BABYLON.Nullable<BABYLON.Texture>;
+        /**
+         * BABYLON.Vector2 for the node if it exists
+         */
+        vector2: BABYLON.Nullable<BABYLON.Vector2>;
+        /**
+         * BABYLON.Vector3 for the node if it exists
+         */
+        vector3: BABYLON.Nullable<BABYLON.Vector3>;
+        /**
+         * BABYLON.Vector4 for the node if it exists
+         */
+        vector4: BABYLON.Nullable<BABYLON.Vector4>;
+        /**
+         * BABYLON.Matrix for the node if it exists
+         */
+        matrix: BABYLON.Nullable<BABYLON.Matrix>;
+        ports: {
+            [s: string]: GenericPortModel;
+        };
+        /**
+         * Constructs the node model
+         */
+        constructor();
+    }
+}
+declare module NODEEDITOR {
+    interface ITextureLineComponentProps {
+        texture: BABYLON.BaseTexture;
+        width: number;
+        height: number;
+        globalState?: any;
+        hideChannelSelect?: boolean;
+    }
+    export class TextureLineComponent extends React.Component<ITextureLineComponentProps, {
+        displayRed: boolean;
+        displayGreen: boolean;
+        displayBlue: boolean;
+        displayAlpha: boolean;
+        face: number;
+    }> {
+        constructor(props: ITextureLineComponentProps);
+        shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
+            displayRed: boolean;
+            displayGreen: boolean;
+            displayBlue: boolean;
+            displayAlpha: boolean;
+            face: number;
+        }): boolean;
+        componentDidMount(): void;
+        componentDidUpdate(): void;
+        updatePreview(): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    interface IFileButtonLineComponentProps {
+        label: string;
+        onClick: (file: File) => void;
+        accept: string;
+    }
+    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        constructor(props: IFileButtonLineComponentProps);
+        onChange(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    interface INumericInputComponentProps {
+        label: string;
+        value: number;
+        step?: number;
+        onChange: (value: number) => void;
+    }
+    export class NumericInputComponent extends React.Component<INumericInputComponentProps, {
+        value: string;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: INumericInputComponentProps);
+        shouldComponentUpdate(nextProps: INumericInputComponentProps, nextState: {
+            value: string;
+        }): boolean;
+        updateValue(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    export class PropertyChangedEvent {
+        object: any;
+        property: string;
+        value: any;
+        initialValue: any;
+    }
+}
+declare module NODEEDITOR {
+    interface IVector2LineComponentProps {
+        label: string;
+        target: any;
+        propertyName: string;
+        step?: number;
+        onChange?: (newvalue: BABYLON.Vector2) => void;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class Vector2LineComponent extends React.Component<IVector2LineComponentProps, {
+        isExpanded: boolean;
+        value: BABYLON.Vector2;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: IVector2LineComponentProps);
+        shouldComponentUpdate(nextProps: IVector2LineComponentProps, nextState: {
+            isExpanded: boolean;
+            value: BABYLON.Vector2;
+        }): boolean;
+        switchExpandState(): void;
+        raiseOnPropertyChanged(previousValue: BABYLON.Vector2): void;
+        updateStateX(value: number): void;
+        updateStateY(value: number): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    interface IVector3LineComponentProps {
+        label: string;
+        target: any;
+        propertyName: string;
+        step?: number;
+        onChange?: (newvalue: BABYLON.Vector3) => void;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, {
+        isExpanded: boolean;
+        value: BABYLON.Vector3;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: IVector3LineComponentProps);
+        shouldComponentUpdate(nextProps: IVector3LineComponentProps, nextState: {
+            isExpanded: boolean;
+            value: BABYLON.Vector3;
+        }): boolean;
+        switchExpandState(): void;
+        raiseOnPropertyChanged(previousValue: BABYLON.Vector3): void;
+        updateVector3(): void;
+        updateStateX(value: number): void;
+        updateStateY(value: number): void;
+        updateStateZ(value: number): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * GenericNodeWidgetProps
+     */
+    export interface GenericNodeWidgetProps {
+        node: BABYLON.Nullable<GenericNodeModel>;
+    }
+    /**
+     * GenericNodeWidgetState
+     */
+    export interface GenericNodeWidgetState {
+    }
+    /**
+     * Used to display a node block for the node editor
+     */
+    export class GenericNodeWidget extends React.Component<GenericNodeWidgetProps, GenericNodeWidgetState> {
+        /**
+         * Creates a GenericNodeWidget
+         * @param props
+         */
+        constructor(props: GenericNodeWidgetProps);
+        /**
+         * Replaces the texture of the node
+         * @param file the file of the texture to use
+         */
+        replaceTexture(file: File): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Node factory which creates editor nodes
+     */
+    export class GenericNodeFactory extends SRD.AbstractNodeFactory {
+        /**
+         * Constructs a GenericNodeFactory
+         */
+        constructor();
+        /**
+         * Generates a node widget
+         * @param diagramEngine diagram engine
+         * @param node node to generate
+         * @returns node widget jsx
+         */
+        generateReactWidget(diagramEngine: SRD.DiagramEngine, node: GenericNodeModel): JSX.Element;
+        /**
+         * Gets a new instance of a node model
+         * @returns generic node model
+         */
+        getNewInstance(): GenericNodeModel;
+    }
+}
+declare module NODEEDITOR {
+    interface ILineContainerComponentProps {
+        globalState?: any;
+        title: string;
+        children: any[] | any;
+        closed?: boolean;
+    }
+    export class LineContainerComponent extends React.Component<ILineContainerComponentProps, {
+        isExpanded: boolean;
+        isHighlighted: boolean;
+    }> {
+        private static _InMemoryStorage;
+        constructor(props: ILineContainerComponentProps);
+        switchExpandedState(): void;
+        componentDidMount(): void;
+        renderHeader(): JSX.Element;
+        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 IGraphEditorProps {
+        globalState: GlobalState;
+    }
+    export class GraphEditor extends React.Component<IGraphEditorProps> {
+        private _engine;
+        private _model;
+        private _nodes;
+        /**
+         * 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
+         */
+        createNodeFromObject(options: {
+            column: number;
+            nodeMaterialBlock?: BABYLON.NodeMaterialBlock;
+        }): GenericNodeModel;
+        componentDidMount(): void;
+        componentWillUnmount(): void;
+        constructor(props: IGraphEditorProps);
+        addNodeFromClass(ObjectClass: typeof BABYLON.NodeMaterialBlock): GenericNodeModel;
+        addValueNode(type: string, column?: number, connection?: BABYLON.NodeMaterialConnectionPoint): GenericNodeModel;
+        allBlocks: {
+            Fragment: (typeof BABYLON.AlphaTestBlock | typeof BABYLON.FragmentOutputBlock | typeof BABYLON.ImageProcessingBlock | typeof BABYLON.RGBAMergerBlock | typeof BABYLON.RGBASplitterBlock | typeof BABYLON.TextureBlock)[];
+            Vertex: (typeof BABYLON.BonesBlock | typeof BABYLON.InstancesBlock | typeof BABYLON.MorphTargetsBlock | typeof BABYLON.VertexOutputBlock)[];
+            Dual: (typeof BABYLON.FogBlock)[];
+            Other: (typeof BABYLON.AddBlock | typeof BABYLON.ClampBlock | typeof BABYLON.MatrixMultiplicationBlock | typeof BABYLON.MultiplyBlock | typeof BABYLON.Vector2TransformBlock | typeof BABYLON.Vector3TransformBlock | typeof BABYLON.Vector4TransformBlock)[];
+            Value: string[];
+        };
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    export class Popup {
+        static CreatePopup(title: string, windowVariableName: string, width?: number, height?: number): HTMLDivElement | null;
+        private static _CopyStyles;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Interface used to specify creation options for the node editor
+     */
+    export interface INodeEditorOptions {
+        /**
+         * Defines the DOM element that will host the node editor
+         */
+        hostElement?: HTMLDivElement;
+        nodeMaterial?: BABYLON.NodeMaterial;
+    }
+    /**
+     * Class used to create a node editor
+     */
+    export class NodeEditor {
+        /**
+         * Show the node editor
+         * @param options defines the options to use to configure the node editor
+         */
+        static Show(options: INodeEditorOptions): void;
+    }
+}

File diff suppressed because it is too large
+ 41 - 0
dist/preview release/nodeEditor/babylon.nodeEditor.js


File diff suppressed because it is too large
+ 48284 - 0
dist/preview release/nodeEditor/babylon.nodeEditor.max.js


File diff suppressed because it is too large
+ 1 - 0
dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map


+ 770 - 0
dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts

@@ -0,0 +1,770 @@
+/// <reference types="react" />
+declare module "babylonjs-node-editor/globalState" {
+    import { NodeMaterial } from "babylonjs/Materials/Node/nodeMaterial";
+    import { Nullable } from "babylonjs/types";
+    export class GlobalState {
+        nodeMaterial?: NodeMaterial;
+        hostDocument?: Nullable<Document>;
+    }
+}
+declare module "babylonjs-node-editor/components/customDiragramNodes/generic/genericPortModel" {
+    import { LinkModel, PortModel } from "storm-react-diagrams";
+    import { Nullable } from 'babylonjs/types';
+    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+    import { GenericNodeModel } from "babylonjs-node-editor/components/customDiragramNodes/generic/genericNodeModel";
+    /**
+     * Port model for the generic node
+     */
+    export class GenericPortModel extends PortModel {
+        /**
+         * If the port is input or output
+         */
+        position: string | "input" | "output";
+        /**
+         * What the port is connected to
+         */
+        connection: Nullable<NodeMaterialConnectionPoint>;
+        static idCounter: number;
+        constructor(name: string, type?: string);
+        syncWithNodeMaterialConnectionPoint(connection: NodeMaterialConnectionPoint): void;
+        getNodeModel(): GenericNodeModel;
+        link(outPort: GenericPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
+        getInputFromBlock(): void;
+        createLinkModel(): LinkModel;
+        getValue: Function;
+        static SortInputOutput(a: Nullable<GenericPortModel>, b: Nullable<GenericPortModel>): {
+            input: GenericPortModel;
+            output: GenericPortModel;
+        } | null;
+    }
+}
+declare module "babylonjs-node-editor/components/customDiragramNodes/generic/genericNodeModel" {
+    import { NodeModel } from "storm-react-diagrams";
+    import { Nullable } from 'babylonjs/types';
+    import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
+    import { Texture } from 'babylonjs/Materials/Textures/texture';
+    import { Vector2, Vector3, Vector4, Matrix } from 'babylonjs/Maths/math';
+    import { GenericPortModel } from "babylonjs-node-editor/components/customDiragramNodes/generic/genericPortModel";
+    /**
+     * Generic node model which stores information about a node editor block
+     */
+    export class GenericNodeModel extends NodeModel {
+        /**
+         * The babylon block this node represents
+         */
+        block: Nullable<NodeMaterialBlock>;
+        /**
+         * Labels for the block
+         */
+        headerLabels: Array<{
+            text: string;
+        }>;
+        /**
+         * Texture for the node if it exists
+         */
+        texture: Nullable<Texture>;
+        /**
+         * Vector2 for the node if it exists
+         */
+        vector2: Nullable<Vector2>;
+        /**
+         * Vector3 for the node if it exists
+         */
+        vector3: Nullable<Vector3>;
+        /**
+         * Vector4 for the node if it exists
+         */
+        vector4: Nullable<Vector4>;
+        /**
+         * Matrix for the node if it exists
+         */
+        matrix: Nullable<Matrix>;
+        ports: {
+            [s: string]: GenericPortModel;
+        };
+        /**
+         * Constructs the node model
+         */
+        constructor();
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/textureLineComponent" {
+    import * as React from "react";
+    import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
+    interface ITextureLineComponentProps {
+        texture: BaseTexture;
+        width: number;
+        height: number;
+        globalState?: any;
+        hideChannelSelect?: boolean;
+    }
+    export class TextureLineComponent extends React.Component<ITextureLineComponentProps, {
+        displayRed: boolean;
+        displayGreen: boolean;
+        displayBlue: boolean;
+        displayAlpha: boolean;
+        face: number;
+    }> {
+        constructor(props: ITextureLineComponentProps);
+        shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
+            displayRed: boolean;
+            displayGreen: boolean;
+            displayBlue: boolean;
+            displayAlpha: boolean;
+            face: number;
+        }): boolean;
+        componentDidMount(): void;
+        componentDidUpdate(): void;
+        updatePreview(): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/fileButtonLineComponent" {
+    import * as React from "react";
+    interface IFileButtonLineComponentProps {
+        label: string;
+        onClick: (file: File) => void;
+        accept: string;
+    }
+    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        constructor(props: IFileButtonLineComponentProps);
+        onChange(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/numericInputComponent" {
+    import * as React from "react";
+    interface INumericInputComponentProps {
+        label: string;
+        value: number;
+        step?: number;
+        onChange: (value: number) => void;
+    }
+    export class NumericInputComponent extends React.Component<INumericInputComponentProps, {
+        value: string;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: INumericInputComponentProps);
+        shouldComponentUpdate(nextProps: INumericInputComponentProps, nextState: {
+            value: string;
+        }): boolean;
+        updateValue(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/propertyChangedEvent" {
+    export class PropertyChangedEvent {
+        object: any;
+        property: string;
+        value: any;
+        initialValue: any;
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/vector2LineComponent" {
+    import * as React from "react";
+    import { Vector2 } from "babylonjs/Maths/math";
+    import { Observable } from "babylonjs/Misc/observable";
+    import { PropertyChangedEvent } from "babylonjs-node-editor/sharedComponents/propertyChangedEvent";
+    interface IVector2LineComponentProps {
+        label: string;
+        target: any;
+        propertyName: string;
+        step?: number;
+        onChange?: (newvalue: Vector2) => void;
+        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    }
+    export class Vector2LineComponent extends React.Component<IVector2LineComponentProps, {
+        isExpanded: boolean;
+        value: Vector2;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: IVector2LineComponentProps);
+        shouldComponentUpdate(nextProps: IVector2LineComponentProps, nextState: {
+            isExpanded: boolean;
+            value: Vector2;
+        }): boolean;
+        switchExpandState(): void;
+        raiseOnPropertyChanged(previousValue: Vector2): void;
+        updateStateX(value: number): void;
+        updateStateY(value: number): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/vector3LineComponent" {
+    import * as React from "react";
+    import { Vector3 } from "babylonjs/Maths/math";
+    import { Observable } from "babylonjs/Misc/observable";
+    import { PropertyChangedEvent } from "babylonjs-node-editor/sharedComponents/propertyChangedEvent";
+    interface IVector3LineComponentProps {
+        label: string;
+        target: any;
+        propertyName: string;
+        step?: number;
+        onChange?: (newvalue: Vector3) => void;
+        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    }
+    export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, {
+        isExpanded: boolean;
+        value: Vector3;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: IVector3LineComponentProps);
+        shouldComponentUpdate(nextProps: IVector3LineComponentProps, nextState: {
+            isExpanded: boolean;
+            value: Vector3;
+        }): boolean;
+        switchExpandState(): void;
+        raiseOnPropertyChanged(previousValue: Vector3): void;
+        updateVector3(): void;
+        updateStateX(value: number): void;
+        updateStateY(value: number): void;
+        updateStateZ(value: number): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-node-editor/components/customDiragramNodes/generic/genericNodeWidget" {
+    import * as React from "react";
+    import { GenericNodeModel } from "babylonjs-node-editor/components/customDiragramNodes/generic/genericNodeModel";
+    import { Nullable } from 'babylonjs/types';
+    /**
+     * GenericNodeWidgetProps
+     */
+    export interface GenericNodeWidgetProps {
+        node: Nullable<GenericNodeModel>;
+    }
+    /**
+     * GenericNodeWidgetState
+     */
+    export interface GenericNodeWidgetState {
+    }
+    /**
+     * Used to display a node block for the node editor
+     */
+    export class GenericNodeWidget extends React.Component<GenericNodeWidgetProps, GenericNodeWidgetState> {
+        /**
+         * Creates a GenericNodeWidget
+         * @param props
+         */
+        constructor(props: GenericNodeWidgetProps);
+        /**
+         * Replaces the texture of the node
+         * @param file the file of the texture to use
+         */
+        replaceTexture(file: File): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-node-editor/components/customDiragramNodes/generic/genericNodeFactory" {
+    import * as SRD from "storm-react-diagrams";
+    import { GenericNodeModel } from "babylonjs-node-editor/components/customDiragramNodes/generic/genericNodeModel";
+    /**
+     * Node factory which creates editor nodes
+     */
+    export class GenericNodeFactory extends SRD.AbstractNodeFactory {
+        /**
+         * Constructs a GenericNodeFactory
+         */
+        constructor();
+        /**
+         * Generates a node widget
+         * @param diagramEngine diagram engine
+         * @param node node to generate
+         * @returns node widget jsx
+         */
+        generateReactWidget(diagramEngine: SRD.DiagramEngine, node: GenericNodeModel): JSX.Element;
+        /**
+         * Gets a new instance of a node model
+         * @returns generic node model
+         */
+        getNewInstance(): GenericNodeModel;
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/lineContainerComponent" {
+    import * as React from "react";
+    interface ILineContainerComponentProps {
+        globalState?: any;
+        title: string;
+        children: any[] | any;
+        closed?: boolean;
+    }
+    export class LineContainerComponent extends React.Component<ILineContainerComponentProps, {
+        isExpanded: boolean;
+        isHighlighted: boolean;
+    }> {
+        private static _InMemoryStorage;
+        constructor(props: ILineContainerComponentProps);
+        switchExpandedState(): void;
+        componentDidMount(): void;
+        renderHeader(): JSX.Element;
+        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/graphEditor" {
+    import * as React from "react";
+    import { GlobalState } from "babylonjs-node-editor/globalState";
+    import { GenericNodeModel } from "babylonjs-node-editor/components/customDiragramNodes/generic/genericNodeModel";
+    import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
+    import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+    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 { MatrixMultiplicationBlock } from 'babylonjs/Materials/Node/Blocks/matrixMultiplicationBlock';
+    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';
+    interface IGraphEditorProps {
+        globalState: GlobalState;
+    }
+    export class GraphEditor extends React.Component<IGraphEditorProps> {
+        private _engine;
+        private _model;
+        private _nodes;
+        /**
+         * 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
+         */
+        createNodeFromObject(options: {
+            column: number;
+            nodeMaterialBlock?: NodeMaterialBlock;
+        }): GenericNodeModel;
+        componentDidMount(): void;
+        componentWillUnmount(): void;
+        constructor(props: IGraphEditorProps);
+        addNodeFromClass(ObjectClass: typeof NodeMaterialBlock): GenericNodeModel;
+        addValueNode(type: string, column?: number, connection?: NodeMaterialConnectionPoint): GenericNodeModel;
+        allBlocks: {
+            Fragment: (typeof AlphaTestBlock | typeof FragmentOutputBlock | typeof ImageProcessingBlock | typeof RGBAMergerBlock | typeof RGBASplitterBlock | typeof TextureBlock)[];
+            Vertex: (typeof BonesBlock | typeof InstancesBlock | typeof MorphTargetsBlock | typeof VertexOutputBlock)[];
+            Dual: (typeof FogBlock)[];
+            Other: (typeof AddBlock | typeof ClampBlock | typeof MatrixMultiplicationBlock | typeof MultiplyBlock | typeof Vector2TransformBlock | typeof Vector3TransformBlock | typeof Vector4TransformBlock)[];
+            Value: string[];
+        };
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-node-editor/sharedComponents/popup" {
+    export class Popup {
+        static CreatePopup(title: string, windowVariableName: string, width?: number, height?: number): HTMLDivElement | null;
+        private static _CopyStyles;
+    }
+}
+declare module "babylonjs-node-editor/nodeEditor" {
+    import { NodeMaterial } from "babylonjs/Materials/Node/nodeMaterial";
+    /**
+     * Interface used to specify creation options for the node editor
+     */
+    export interface INodeEditorOptions {
+        /**
+         * Defines the DOM element that will host the node editor
+         */
+        hostElement?: HTMLDivElement;
+        nodeMaterial?: NodeMaterial;
+    }
+    /**
+     * Class used to create a node editor
+     */
+    export class NodeEditor {
+        /**
+         * Show the node editor
+         * @param options defines the options to use to configure the node editor
+         */
+        static Show(options: INodeEditorOptions): void;
+    }
+}
+declare module "babylonjs-node-editor/index" {
+    export * from "babylonjs-node-editor/nodeEditor";
+}
+declare module "babylonjs-node-editor/legacy/legacy" {
+    export * from "babylonjs-node-editor/index";
+}
+declare module "babylonjs-node-editor" {
+    export * from "babylonjs-node-editor/legacy/legacy";
+}
+/// <reference types="react" />
+declare module NODEEDITOR {
+    export class GlobalState {
+        nodeMaterial?: BABYLON.NodeMaterial;
+        hostDocument?: BABYLON.Nullable<Document>;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Port model for the generic node
+     */
+    export class GenericPortModel extends PortModel {
+        /**
+         * If the port is input or output
+         */
+        position: string | "input" | "output";
+        /**
+         * What the port is connected to
+         */
+        connection: BABYLON.Nullable<BABYLON.NodeMaterialConnectionPoint>;
+        static idCounter: number;
+        constructor(name: string, type?: string);
+        syncWithNodeMaterialConnectionPoint(connection: BABYLON.NodeMaterialConnectionPoint): void;
+        getNodeModel(): GenericNodeModel;
+        link(outPort: GenericPortModel): LinkModel<import("storm-react-diagrams").LinkModelListener>;
+        getInputFromBlock(): void;
+        createLinkModel(): LinkModel;
+        getValue: Function;
+        static SortInputOutput(a: BABYLON.Nullable<GenericPortModel>, b: BABYLON.Nullable<GenericPortModel>): {
+            input: GenericPortModel;
+            output: GenericPortModel;
+        } | null;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Generic node model which stores information about a node editor block
+     */
+    export class GenericNodeModel extends NodeModel {
+        /**
+         * The babylon block this node represents
+         */
+        block: BABYLON.Nullable<BABYLON.NodeMaterialBlock>;
+        /**
+         * Labels for the block
+         */
+        headerLabels: Array<{
+            text: string;
+        }>;
+        /**
+         * BABYLON.Texture for the node if it exists
+         */
+        texture: BABYLON.Nullable<BABYLON.Texture>;
+        /**
+         * BABYLON.Vector2 for the node if it exists
+         */
+        vector2: BABYLON.Nullable<BABYLON.Vector2>;
+        /**
+         * BABYLON.Vector3 for the node if it exists
+         */
+        vector3: BABYLON.Nullable<BABYLON.Vector3>;
+        /**
+         * BABYLON.Vector4 for the node if it exists
+         */
+        vector4: BABYLON.Nullable<BABYLON.Vector4>;
+        /**
+         * BABYLON.Matrix for the node if it exists
+         */
+        matrix: BABYLON.Nullable<BABYLON.Matrix>;
+        ports: {
+            [s: string]: GenericPortModel;
+        };
+        /**
+         * Constructs the node model
+         */
+        constructor();
+    }
+}
+declare module NODEEDITOR {
+    interface ITextureLineComponentProps {
+        texture: BABYLON.BaseTexture;
+        width: number;
+        height: number;
+        globalState?: any;
+        hideChannelSelect?: boolean;
+    }
+    export class TextureLineComponent extends React.Component<ITextureLineComponentProps, {
+        displayRed: boolean;
+        displayGreen: boolean;
+        displayBlue: boolean;
+        displayAlpha: boolean;
+        face: number;
+    }> {
+        constructor(props: ITextureLineComponentProps);
+        shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
+            displayRed: boolean;
+            displayGreen: boolean;
+            displayBlue: boolean;
+            displayAlpha: boolean;
+            face: number;
+        }): boolean;
+        componentDidMount(): void;
+        componentDidUpdate(): void;
+        updatePreview(): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    interface IFileButtonLineComponentProps {
+        label: string;
+        onClick: (file: File) => void;
+        accept: string;
+    }
+    export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        constructor(props: IFileButtonLineComponentProps);
+        onChange(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    interface INumericInputComponentProps {
+        label: string;
+        value: number;
+        step?: number;
+        onChange: (value: number) => void;
+    }
+    export class NumericInputComponent extends React.Component<INumericInputComponentProps, {
+        value: string;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: INumericInputComponentProps);
+        shouldComponentUpdate(nextProps: INumericInputComponentProps, nextState: {
+            value: string;
+        }): boolean;
+        updateValue(evt: any): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    export class PropertyChangedEvent {
+        object: any;
+        property: string;
+        value: any;
+        initialValue: any;
+    }
+}
+declare module NODEEDITOR {
+    interface IVector2LineComponentProps {
+        label: string;
+        target: any;
+        propertyName: string;
+        step?: number;
+        onChange?: (newvalue: BABYLON.Vector2) => void;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class Vector2LineComponent extends React.Component<IVector2LineComponentProps, {
+        isExpanded: boolean;
+        value: BABYLON.Vector2;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: IVector2LineComponentProps);
+        shouldComponentUpdate(nextProps: IVector2LineComponentProps, nextState: {
+            isExpanded: boolean;
+            value: BABYLON.Vector2;
+        }): boolean;
+        switchExpandState(): void;
+        raiseOnPropertyChanged(previousValue: BABYLON.Vector2): void;
+        updateStateX(value: number): void;
+        updateStateY(value: number): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    interface IVector3LineComponentProps {
+        label: string;
+        target: any;
+        propertyName: string;
+        step?: number;
+        onChange?: (newvalue: BABYLON.Vector3) => void;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, {
+        isExpanded: boolean;
+        value: BABYLON.Vector3;
+    }> {
+        static defaultProps: {
+            step: number;
+        };
+        private _localChange;
+        constructor(props: IVector3LineComponentProps);
+        shouldComponentUpdate(nextProps: IVector3LineComponentProps, nextState: {
+            isExpanded: boolean;
+            value: BABYLON.Vector3;
+        }): boolean;
+        switchExpandState(): void;
+        raiseOnPropertyChanged(previousValue: BABYLON.Vector3): void;
+        updateVector3(): void;
+        updateStateX(value: number): void;
+        updateStateY(value: number): void;
+        updateStateZ(value: number): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * GenericNodeWidgetProps
+     */
+    export interface GenericNodeWidgetProps {
+        node: BABYLON.Nullable<GenericNodeModel>;
+    }
+    /**
+     * GenericNodeWidgetState
+     */
+    export interface GenericNodeWidgetState {
+    }
+    /**
+     * Used to display a node block for the node editor
+     */
+    export class GenericNodeWidget extends React.Component<GenericNodeWidgetProps, GenericNodeWidgetState> {
+        /**
+         * Creates a GenericNodeWidget
+         * @param props
+         */
+        constructor(props: GenericNodeWidgetProps);
+        /**
+         * Replaces the texture of the node
+         * @param file the file of the texture to use
+         */
+        replaceTexture(file: File): void;
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Node factory which creates editor nodes
+     */
+    export class GenericNodeFactory extends SRD.AbstractNodeFactory {
+        /**
+         * Constructs a GenericNodeFactory
+         */
+        constructor();
+        /**
+         * Generates a node widget
+         * @param diagramEngine diagram engine
+         * @param node node to generate
+         * @returns node widget jsx
+         */
+        generateReactWidget(diagramEngine: SRD.DiagramEngine, node: GenericNodeModel): JSX.Element;
+        /**
+         * Gets a new instance of a node model
+         * @returns generic node model
+         */
+        getNewInstance(): GenericNodeModel;
+    }
+}
+declare module NODEEDITOR {
+    interface ILineContainerComponentProps {
+        globalState?: any;
+        title: string;
+        children: any[] | any;
+        closed?: boolean;
+    }
+    export class LineContainerComponent extends React.Component<ILineContainerComponentProps, {
+        isExpanded: boolean;
+        isHighlighted: boolean;
+    }> {
+        private static _InMemoryStorage;
+        constructor(props: ILineContainerComponentProps);
+        switchExpandedState(): void;
+        componentDidMount(): void;
+        renderHeader(): JSX.Element;
+        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 IGraphEditorProps {
+        globalState: GlobalState;
+    }
+    export class GraphEditor extends React.Component<IGraphEditorProps> {
+        private _engine;
+        private _model;
+        private _nodes;
+        /**
+         * 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
+         */
+        createNodeFromObject(options: {
+            column: number;
+            nodeMaterialBlock?: BABYLON.NodeMaterialBlock;
+        }): GenericNodeModel;
+        componentDidMount(): void;
+        componentWillUnmount(): void;
+        constructor(props: IGraphEditorProps);
+        addNodeFromClass(ObjectClass: typeof BABYLON.NodeMaterialBlock): GenericNodeModel;
+        addValueNode(type: string, column?: number, connection?: BABYLON.NodeMaterialConnectionPoint): GenericNodeModel;
+        allBlocks: {
+            Fragment: (typeof BABYLON.AlphaTestBlock | typeof BABYLON.FragmentOutputBlock | typeof BABYLON.ImageProcessingBlock | typeof BABYLON.RGBAMergerBlock | typeof BABYLON.RGBASplitterBlock | typeof BABYLON.TextureBlock)[];
+            Vertex: (typeof BABYLON.BonesBlock | typeof BABYLON.InstancesBlock | typeof BABYLON.MorphTargetsBlock | typeof BABYLON.VertexOutputBlock)[];
+            Dual: (typeof BABYLON.FogBlock)[];
+            Other: (typeof BABYLON.AddBlock | typeof BABYLON.ClampBlock | typeof BABYLON.MatrixMultiplicationBlock | typeof BABYLON.MultiplyBlock | typeof BABYLON.Vector2TransformBlock | typeof BABYLON.Vector3TransformBlock | typeof BABYLON.Vector4TransformBlock)[];
+            Value: string[];
+        };
+        render(): JSX.Element;
+    }
+}
+declare module NODEEDITOR {
+    export class Popup {
+        static CreatePopup(title: string, windowVariableName: string, width?: number, height?: number): HTMLDivElement | null;
+        private static _CopyStyles;
+    }
+}
+declare module NODEEDITOR {
+    /**
+     * Interface used to specify creation options for the node editor
+     */
+    export interface INodeEditorOptions {
+        /**
+         * Defines the DOM element that will host the node editor
+         */
+        hostElement?: HTMLDivElement;
+        nodeMaterial?: BABYLON.NodeMaterial;
+    }
+    /**
+     * Class used to create a node editor
+     */
+    export class NodeEditor {
+        /**
+         * Show the node editor
+         * @param options defines the options to use to configure the node editor
+         */
+        static Show(options: INodeEditorOptions): void;
+    }
+}

+ 27 - 0
dist/preview release/nodeEditor/package.json

@@ -0,0 +1,27 @@
+{
+    "author": {
+        "name": "David CATUHE"
+    },
+    "name": "babylonjs-node-editor",
+    "description": "The Babylon.js node material editor.",
+    "version": "4.1.0-alpha.0",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/BabylonJS/Babylon.js.git"
+    },
+    "license": "Apache-2.0",
+    "dependencies": {
+        "babylonjs": "4.1.0-alpha.0"
+    },
+    "files": [
+        "babylon.nodeEditor.max.js.map",
+        "babylon.nodeEditor.max.js",
+        "babylon.nodeEditor.js",
+        "babylon.nodeEditor.module.d.ts",
+        "readme.md",
+        "package.json"
+    ],
+    "engines": {
+        "node": "*"
+    }
+}

+ 1 - 0
dist/preview release/nodeEditor/readme-es6.md

@@ -0,0 +1 @@
+Node Editor es6

+ 1 - 0
dist/preview release/nodeEditor/readme.md

@@ -0,0 +1 @@
+Node Editor

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

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

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

@@ -1 +1 @@
-{"engineOnly":290833,"sceneOnly":500617,"minGridMaterial":627677,"minStandardMaterial":752502}
+{"engineOnly":291219,"sceneOnly":501003,"minGridMaterial":628371,"minStandardMaterial":753196}

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

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

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

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

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

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

File diff suppressed because it is too large
+ 44003 - 41029
dist/preview release/viewer/babylon.module.d.ts


File diff suppressed because it is too large
+ 460 - 320
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -9,6 +9,9 @@
 
 ### Inspector
 - Added support for `ShadowGenerator` ([Deltakosh](https://github.com/deltakosh/))
+- Added support for scene normalization ([Deltakosh](https://github.com/deltakosh/))
+- Added support for texture creation and assignments per material ([Deltakosh](https://github.com/deltakosh/))
+- Node material editor ([Deltakosh](https://github.com/deltakosh/)/[TrevorDev](https://github.com/TrevorDev))
 
 ### Tools
 - Added `Color3.toHSV()`, `Color3.toHSVToRef()` and `Color3.HSVtoRGBToRef()` ([Deltakosh](https://github.com/deltakosh/))

+ 2 - 2
inspector/README.md

@@ -9,13 +9,13 @@ Call the method `show` of the scene debugLayer:
 ```
 scene.debugLayer.show();
 ```
-This method will retrieve dynamically the library `inspector.js`, download it and add
+This method will retrieve dynamically the library `babylon.inspector.bundle.js`, download it and add
 it to the html page.
 
 ### Offline method
 If you don't have access to internet, the inspector should be imported manually in your HTML page :
 ```
-<script src="babylon.inspector.js" />
+<script src="babylon.inspector.bundle.js" />
 ``` 
 Then, call the method `show` of the scene debugLayer: 
 ```

+ 1 - 0
inspector/src/components/actionTabs/actionTabs.scss

@@ -163,6 +163,7 @@ $line-padding-left: 2px;
                     .debug {
                         grid-column: 1;
                         margin-left: 5px;
+                        margin-right: 5px;
                         display: grid;
                         align-items: center; 
                         justify-items: center;                          

+ 46 - 33
inspector/src/components/actionTabs/actionTabsComponent.tsx

@@ -15,7 +15,7 @@ import { GlobalState } from "../../components/globalState";
 require("./actionTabs.scss");
 
 interface IActionTabsComponentProps {
-    scene: Scene,
+    scene?: Scene,
     noCommands?: boolean,
     noHeader?: boolean,
     noExpand?: boolean,
@@ -23,7 +23,7 @@ interface IActionTabsComponentProps {
     popupMode?: boolean,
     onPopup?: () => void,
     onClose?: () => void,
-    globalState: GlobalState
+    globalState?: GlobalState
 }
 
 export class ActionTabsComponent extends React.Component<IActionTabsComponentProps, { selectedEntity: any, selectedIndex: number }> {
@@ -36,53 +36,66 @@ export class ActionTabsComponent extends React.Component<IActionTabsComponentPro
 
         let initialIndex = 0;
 
-        const validationResutls = this.props.globalState.validationResults;
-        if (validationResutls) {
-            if (validationResutls.issues.numErrors || validationResutls.issues.numWarnings) {
-                initialIndex = 3;
+        if(this.props.globalState){
+            const validationResutls = this.props.globalState.validationResults;
+            if (validationResutls) {
+                if (validationResutls.issues.numErrors || validationResutls.issues.numWarnings) {
+                    initialIndex = 3;
+                }
             }
         }
+        
 
         this.state = { selectedEntity: null, selectedIndex: initialIndex }
     }
 
     componentWillMount() {
-        this._onSelectionChangeObserver = this.props.globalState.onSelectionChangedObservable.add((entity) => {
-            this.setState({ selectedEntity: entity, selectedIndex: 0 });
-        });
-
-        this._onTabChangedObserver = this.props.globalState.onTabChangedObservable.add(index => {
-            this.setState({ selectedIndex: index });
-        });
+        if(this.props.globalState){
+            this._onSelectionChangeObserver = this.props.globalState.onSelectionChangedObservable.add((entity) => {
+                this.setState({ selectedEntity: entity, selectedIndex: 0 });
+            });
+
+            this._onTabChangedObserver = this.props.globalState.onTabChangedObservable.add(index => {
+                this.setState({ selectedIndex: index });
+            });
+        }
     }
 
     componentWillUnmount() {
-        if (this._onSelectionChangeObserver) {
-            this.props.globalState.onSelectionChangedObservable.remove(this._onSelectionChangeObserver);
-        }
+        if(this.props.globalState){
+            if (this._onSelectionChangeObserver) {
+                this.props.globalState.onSelectionChangedObservable.remove(this._onSelectionChangeObserver);
+            }
 
-        if (this._onTabChangedObserver) {
-            this.props.globalState.onTabChangedObservable.remove(this._onTabChangedObserver);
+            if (this._onTabChangedObserver) {
+                this.props.globalState.onTabChangedObservable.remove(this._onTabChangedObserver);
+            }
         }
     }
 
     changeSelectedTab(index: number) {
-        this.props.globalState.onTabChangedObservable.notifyObservers(index);
+        if(this.props.globalState){
+            this.props.globalState.onTabChangedObservable.notifyObservers(index);
+        }
     }
 
     renderContent() {
-        return (
-            <TabsComponent selectedIndex={this.state.selectedIndex} onSelectedIndexChange={(value) => this.changeSelectedTab(value)}>
-                <PropertyGridTabComponent
-                    title="Properties" icon={faFileAlt} scene={this.props.scene} selectedEntity={this.state.selectedEntity}
-                    globalState={this.props.globalState}
-                    onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable}
-                    onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />
-                <DebugTabComponent title="Debug" icon={faBug} scene={this.props.scene} globalState={this.props.globalState} />
-                <StatisticsTabComponent title="Statistics" icon={faChartBar} scene={this.props.scene} globalState={this.props.globalState} />
-                <ToolsTabComponent title="Tools" icon={faWrench} scene={this.props.scene} globalState={this.props.globalState} />
-            </TabsComponent>
-        )
+        if(this.props.globalState && this.props.scene){
+            return (
+                <TabsComponent selectedIndex={this.state.selectedIndex} onSelectedIndexChange={(value) => this.changeSelectedTab(value)}>
+                    <PropertyGridTabComponent
+                        title="Properties" icon={faFileAlt} scene={this.props.scene} selectedEntity={this.state.selectedEntity}
+                        globalState={this.props.globalState}
+                        onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable}
+                        onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />
+                    <DebugTabComponent title="Debug" icon={faBug} scene={this.props.scene} globalState={this.props.globalState} />
+                    <StatisticsTabComponent title="Statistics" icon={faChartBar} scene={this.props.scene} globalState={this.props.globalState} />
+                    <ToolsTabComponent title="Tools" icon={faWrench} scene={this.props.scene} globalState={this.props.globalState} />
+                </TabsComponent>
+            )
+        }else{
+            return null;
+        }
     }
 
     onClose() {
@@ -105,7 +118,7 @@ export class ActionTabsComponent extends React.Component<IActionTabsComponentPro
                 <div id="actionTabs">
                     {
                         !this.props.noHeader &&
-                        <HeaderComponent title="INSPECTOR" handleBack={true} noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
+                        <HeaderComponent title="INSPECTOR" handleBack={true} noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState ? this.props.globalState.onSelectionChangedObservable : undefined} />
                     }
                     {this.renderContent()}
                 </div>
@@ -128,7 +141,7 @@ export class ActionTabsComponent extends React.Component<IActionTabsComponentPro
             <Resizable id="actionTabs" minWidth={300} maxWidth={600} size={{ height: "100%" }} minHeight="100%" enable={{ top: false, right: false, bottom: false, left: true, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }}>
                 {
                     !this.props.noHeader &&
-                    <HeaderComponent title="INSPECTOR" handleBack={true} noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
+                    <HeaderComponent title="INSPECTOR" handleBack={true} noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState ? this.props.globalState.onSelectionChangedObservable : undefined} />
                 }
                 {this.renderContent()}
             </Resizable>

+ 4 - 4
inspector/src/components/actionTabs/lineContainerComponent.tsx

@@ -4,7 +4,7 @@ import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
 import { GlobalState } from '../../components/globalState';
 
 interface ILineContainerComponentProps {
-    globalState: GlobalState;
+    globalState?: GlobalState;
     title: string;
     children: any[] | any;
     closed?: boolean;
@@ -55,13 +55,13 @@ export class LineContainerComponent extends React.Component<ILineContainerCompon
     }
 
     componentDidMount() {
-        if (!this.props.globalState.selectedLineContainerTitle) {
+        if (this.props.globalState && !this.props.globalState.selectedLineContainerTitle) {
             return;
         }
 
-        if (this.props.globalState.selectedLineContainerTitle === this.props.title) {
+        if (this.props.globalState && this.props.globalState.selectedLineContainerTitle === this.props.title) {
             setTimeout(() => {
-                this.props.globalState.selectedLineContainerTitle = "";
+                this.props.globalState!.selectedLineContainerTitle = "";
             });
 
             this.setState({ isExpanded: true, isHighlighted: true });

+ 17 - 5
inspector/src/components/actionTabs/lines/textureLineComponent.tsx

@@ -13,7 +13,8 @@ interface ITextureLineComponentProps {
     texture: BaseTexture;
     width: number;
     height: number;
-    globalState: GlobalState;
+    globalState?: GlobalState;
+    hideChannelSelect?:boolean;
 }
 
 export class TextureLineComponent extends React.Component<ITextureLineComponentProps, { displayRed: boolean, displayGreen: boolean, displayBlue: boolean, displayAlpha: boolean, face: number }> {
@@ -43,6 +44,11 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
 
     updatePreview() {
         var texture = this.props.texture;
+        if(!texture.isReady() && texture._texture){
+            texture._texture.onLoadedObservable.addOnce(()=>{
+                this.updatePreview();
+            })
+        }
         var scene = texture.getScene()!;
         var engine = scene.getEngine();
         var size = texture.getSize();
@@ -72,7 +78,10 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
 
         const previewCanvas = this.refs.canvas as HTMLCanvasElement;
 
-        this.props.globalState.blockMutationUpdates = true;
+        if(this.props.globalState){
+            this.props.globalState.blockMutationUpdates = true;
+        }
+        
         let rtt = new RenderTargetTexture(
             "temp",
             { width: width, height: height },
@@ -156,7 +165,10 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
         passPostProcess.dispose();
 
         previewCanvas.style.height = height + "px";
-        this.props.globalState.blockMutationUpdates = false;
+        if(this.props.globalState){
+            this.props.globalState.blockMutationUpdates = false;
+        }
+        
     }
 
     render() {
@@ -165,7 +177,7 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
         return (
             <div className="textureLine">
                 {
-                    texture.isCube &&
+                    !this.props.hideChannelSelect && texture.isCube &&
                     <div className="control3D">
                         <button className={this.state.face === 0 ? "px command selected" : "px command"} onClick={() => this.setState({ face: 0 })}>PX</button>
                         <button className={this.state.face === 1 ? "nx command selected" : "nx command"} onClick={() => this.setState({ face: 1 })}>NX</button>
@@ -176,7 +188,7 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
                     </div>
                 }
                 {
-                    !texture.isCube &&
+                    !this.props.hideChannelSelect && !texture.isCube &&
                     <div className="control">
                         <button className={this.state.displayRed && !this.state.displayGreen ? "red command selected" : "red command"} onClick={() => this.setState({ displayRed: true, displayGreen: false, displayBlue: false, displayAlpha: false })}>R</button>
                         <button className={this.state.displayGreen && !this.state.displayBlue ? "green command selected" : "green command"} onClick={() => this.setState({ displayRed: false, displayGreen: true, displayBlue: false, displayAlpha: false })}>G</button>

+ 25 - 0
inspector/src/components/actionTabs/lines/textureLinkLineComponent.tsx

@@ -9,6 +9,9 @@ import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
 import { TextLineComponent } from "./textLineComponent";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faWrench } from '@fortawesome/free-solid-svg-icons';
+import { Texture } from 'babylonjs/Materials/Textures/texture';
+import { FileButtonLineComponent } from './fileButtonLineComponent';
+import { Tools } from 'babylonjs/Misc/tools';
 
 export interface ITextureLinkLineComponentProps {
     label: string;
@@ -16,6 +19,7 @@ export interface ITextureLinkLineComponentProps {
     material?: Material;
     onSelectionChangedObservable?: Observable<any>;
     onDebugSelectionChangeObservable?: Observable<BaseTexture>;
+    propertyName?: string;
 }
 
 export class TextureLinkLineComponent extends React.Component<ITextureLinkLineComponentProps, { isDebugSelected: boolean }> {
@@ -121,10 +125,31 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
         this.props.onSelectionChangedObservable.notifyObservers(texture!);
     }
 
+
+    updateTexture(file: File) {
+        let material = this.props.material!;
+        Tools.ReadFile(file, (data) => {
+            var blob = new Blob([data], { type: "octet/stream" });
+            var url = URL.createObjectURL(blob);
+
+            let texture = new Texture(url, material.getScene());
+
+            (material as any)[this.props.propertyName!] = texture;
+
+            this.forceUpdate();
+
+        }, undefined, true);
+    }
+
     render() {
         const texture = this.props.texture;
 
         if (!texture) {
+            if (this.props.propertyName) {
+                return (
+                    <FileButtonLineComponent label={`Add ${this.props.label} texture`} onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
+                )
+            }
             return null;
         }
         return (

+ 10 - 14
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx

@@ -33,22 +33,18 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
     renderTextures(onDebugSelectionChangeObservable: Observable<BaseTexture>) {
         const material = this.props.material;
 
-        if (material.getActiveTextures().length === 0) {
-            return null;
-        }
-
         return (
             <LineContainerComponent globalState={this.props.globalState} title="TEXTURES">
-                <TextureLinkLineComponent label="Albedo" texture={material.albedoTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Metallic" texture={material.metallicTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Reflection" texture={material.reflectionTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Refraction" texture={material.refractionTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Micro-surface" texture={material.microSurfaceTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Bump" texture={material.bumpTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Opacity" texture={material.opacityTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Ambient" texture={material.ambientTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Albedo" texture={material.albedoTexture} propertyName="albedoTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Metallic" texture={material.metallicTexture} propertyName="metallicTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Reflection" texture={material.reflectionTexture} propertyName="reflectionTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Refraction" texture={material.refractionTexture} propertyName="refractionTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Micro-surface" texture={material.microSurfaceTexture} propertyName="microSurfaceTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Bump" texture={material.bumpTexture} propertyName="bumpTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} propertyName="emissiveTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Opacity" texture={material.opacityTexture} propertyName="opacityTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Ambient" texture={material.ambientTexture} propertyName="ambientTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} propertyName="lightmapTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
             </LineContainerComponent>
         );
     }

+ 6 - 11
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMetallicRoughnessMaterialPropertyGridComponent.tsx

@@ -30,21 +30,16 @@ export class PBRMetallicRoughnessMaterialPropertyGridComponent extends React.Com
 
     renderTextures() {
         const material = this.props.material;
-
-        if (material.getActiveTextures().length === 0) {
-            return null;
-        }
-
         const onDebugSelectionChangeObservable = this._onDebugSelectionChangeObservable;
 
         return (
             <LineContainerComponent globalState={this.props.globalState} title="TEXTURES">
-                <TextureLinkLineComponent label="Base" texture={material.baseTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Metallic roughness" texture={material.metallicRoughnessTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Normal" texture={material.normalTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Environment" texture={material.environmentTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Base" texture={material.baseTexture} propertyName="baseTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Metallic roughness" texture={material.metallicRoughnessTexture} propertyName="metallicRoughnessTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Normal" texture={material.normalTexture} propertyName="normalTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Environment" texture={material.environmentTexture} propertyName="environmentTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} propertyName="emissiveTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} propertyName="lightmapTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
             </LineContainerComponent>
         )
     }

+ 6 - 11
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrSpecularGlossinessMaterialPropertyGridComponent.tsx

@@ -30,21 +30,16 @@ export class PBRSpecularGlossinessMaterialPropertyGridComponent extends React.Co
 
     renderTextures() {
         const material = this.props.material;
-
-        if (material.getActiveTextures().length === 0) {
-            return null;
-        }
-
         const onDebugSelectionChangeObservable = this._onDebugSelectionChangeObservable;
 
         return (
             <LineContainerComponent globalState={this.props.globalState} title="TEXTURES">
-                <TextureLinkLineComponent label="Diffuse" texture={material.diffuseTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Specular glossiness" texture={material.specularGlossinessTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Normal" texture={material.normalTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Environment" texture={material.environmentTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Diffuse" texture={material.diffuseTexture} propertyName="diffuseTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Specular glossiness" texture={material.specularGlossinessTexture} propertyName="specularGlossinessTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Normal" texture={material.normalTexture} propertyName="normalTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Environment" texture={material.environmentTexture} propertyName="environmentTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} propertyName="emissiveTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} propertyName="lightmapTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
             </LineContainerComponent>
         )
     }

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

@@ -31,23 +31,19 @@ export class StandardMaterialPropertyGridComponent extends React.Component<IStan
     renderTextures() {
         const material = this.props.material;
 
-        if (material.getActiveTextures().length === 0) {
-            return null;
-        }
-
         const onDebugSelectionChangeObservable = this._onDebugSelectionChangeObservable;
 
         return (
             <LineContainerComponent globalState={this.props.globalState} title="TEXTURES">
-                <TextureLinkLineComponent label="Diffuse" texture={material.diffuseTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Specular" texture={material.specularTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Reflection" texture={material.reflectionTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Refraction" texture={material.refractionTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Bump" texture={material.bumpTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Opacity" texture={material.opacityTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Ambient" texture={material.ambientTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
-                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Diffuse" texture={material.diffuseTexture} propertyName="diffuseTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Specular" texture={material.specularTexture} propertyName="specularTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Reflection" texture={material.reflectionTexture} propertyName="reflectionTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Refraction" texture={material.refractionTexture} propertyName="refractionTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Emissive" texture={material.emissiveTexture} propertyName="emissiveTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Bump" texture={material.bumpTexture} propertyName="bumpTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Opacity" texture={material.opacityTexture} propertyName="opacityTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Ambient" texture={material.ambientTexture} propertyName="ambientTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} propertyName="lightmapTexture" material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
             </LineContainerComponent>
         );
     }

+ 0 - 0
inspector/src/index.css


+ 0 - 2
inspector/src/index.ts

@@ -1,3 +1 @@
-require("./index.css");
-
 export * from "./inspector";

+ 25 - 20
inspector/src/inspector.ts

@@ -48,22 +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];
-            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));
+            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);
                 }
-
-                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){
+                console.log(e)
             }
+            
         }
     }
 
@@ -250,12 +255,12 @@ export class Inspector {
             ReactDOM.render(embedHostElement, this._EmbedHost);
         }
     }
-    private static _CreatePopup(title: string, windowVariableName: string) {
+    public static _CreatePopup(title: string, windowVariableName: string, width = 300, height = 800) {
         const windowCreationOptionsList = {
-            width: 300,
-            height: 800,
-            top: (window.innerHeight - 800) / 2 + window.screenY,
-            left: (window.innerWidth - 300) / 2 + window.screenX
+            width: width,
+            height: height,
+            top: (window.innerHeight - width) / 2 + window.screenY,
+            left: (window.innerWidth - height) / 2 + window.screenX
         };
 
         var windowCreationOptions = Object.keys(windowCreationOptionsList)
@@ -410,7 +415,7 @@ export class Inspector {
         }
     }
 
-    private static _CreateCanvasContainer(parentControl: HTMLElement) {
+    public static _CreateCanvasContainer(parentControl: HTMLElement) {
         // Create a container for previous elements
         this._NewCanvasContainer = parentControl.ownerDocument!.createElement("div");
         this._NewCanvasContainer.style.display = parentControl.style.display;

BIN
inspector/test/environment.dds


BIN
inspector/test/explosion.wav


+ 0 - 91
inspector/test/index.js

@@ -1,91 +0,0 @@
-/// <reference path="../../dist/preview release/babylon.d.ts"/>
-
-var Test = (function() {
-    function Test(canvasId) {
-        var _this = this;
-        var canvas = document.getElementById(canvasId);
-        this.engine = new BABYLON.Engine(canvas, true);
-        this.scene = null;
-        window.addEventListener("resize", function() {
-            _this.engine.resize();
-        });
-        this._run();
-    }
-    Test.prototype._run = function() {
-        var _this = this;
-        this._initScene();
-        this.scene.executeWhenReady(function() {
-            _this.engine.runRenderLoop(function() {
-                _this.scene.render();
-            });
-        });
-    };
-    Test.prototype._initScene = function() {
-        var scene = new BABYLON.Scene(this.engine);
-        var canvas = scene.getEngine().getRenderingCanvas();
-
-        var camera = new BABYLON.FreeCamera("Camera", new BABYLON.Vector3(0, 2, -2), scene);
-
-        var camera2 = new BABYLON.ArcRotateCamera("Camera2", 0, 0, 5, new BABYLON.Vector3(0, 0, 0), scene);
-
-        var camera3 = new BABYLON.ArcRotateCamera("Camera3", 0, 0, 10, new BABYLON.Vector3(0, 0, 0), scene);
-
-        var camera4 = new BABYLON.ArcRotateCamera("Camera4", 0, 0, 15, new BABYLON.Vector3(0, 0, 0), scene);
-
-        var camera5 = new BABYLON.ArcRotateCamera("Camera5", 0, 0, 20, new BABYLON.Vector3(0, 0, 0), scene);
-
-        var camera6 = new BABYLON.ArcRotateCamera("Camera6", 0, 0, 25, new BABYLON.Vector3(0, 0, 0), scene);
-
-        scene.activeCamera = camera2;
-
-        camera2.attachControl(canvas);
-
-        var sceneRoot = new BABYLON.TransformNode("abstractmesh");
-
-        var tn = new BABYLON.TransformNode("transform node");
-
-        let DDSTexture = new BABYLON.CubeTexture("test/environment.dds", scene);
-        let DDSTexture2 = new BABYLON.Texture("test/test_1.dds", scene);
-
-        // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
-        var ground = BABYLON.Mesh.CreateGround("node_damagedHelmet_-6514", 6, 6, 2, scene);
-        ground.parent = tn;
-
-        let num = 5;
-        let angStep = 6.283185307 / num;
-        let rad = 2;
-        let p = sceneRoot;
-        for (let i = 0; i < num; i++) {
-            // Our built-in 'sphere' shape. Params: name, subdivs, size, scene
-            let sphere = BABYLON.Mesh.CreateSphere('sphere' + i, 16, 2, scene);
-
-            // Move the sphere upward 1/2 its height        
-            sphere.position.y = 0.2;
-            sphere.position.x = Math.sin(i * angStep) * rad;
-            sphere.position.z = Math.cos(i * angStep) * rad;
-            sphere.parent = p;
-            p = sphere;
-        }
-
-        let t = 0;
-        scene.registerBeforeRender(() => {
-            ground.rotation.y += 0.01;
-            ground.position.y = Math.cos(t += 0.01);
-        });
-
-        scene.createDefaultCameraOrLight(true);
-        scene.activeCamera.attachControl(canvas);
-        
-        scene.debugLayer.show({embedMode: true});
-        //scene.debugLayer.show();
-        scene.debugLayer.onPropertyChangedObservable.add((result) => {
-            console.log(result.object);
-            console.log("Property : " + result.property);
-            console.log("New value : " + result.value);
-            console.log("Old value : " + result.initialValue);
-        });
-
-        this.scene = scene;
-    };
-    return Test;
-}());

BIN
inspector/test/jump.wav


BIN
inspector/test/test_1.dds


+ 1 - 0
nodeEditor/README-ES6.md

@@ -0,0 +1 @@
+Node Editor

+ 18 - 0
nodeEditor/README.md

@@ -0,0 +1,18 @@
+# Babylon.js Node Editor
+
+An extension to easily create or update any NodeMaterial.
+
+## Usage
+### Online method
+Call the method `Show` of the `BABYLON.NoteMaterial` class: 
+```
+BABYLON.NoteMaterial.Show({hostElement: document.getElementById("host")});
+```
+This method will retrieve dynamically the library `nodeEditor.js`, download it and add
+it to the html page.
+
+### Offline method
+If you don't have access to internet, the node editor should be imported manually in your HTML page :
+```
+<script src="babylon.nodeEditor.js" />
+``` 

+ 34 - 0
nodeEditor/src/components/customDiragramNodes/generic/genericNodeFactory.tsx

@@ -0,0 +1,34 @@
+import * as SRD from "storm-react-diagrams";
+import { GenericNodeWidget } from "./genericNodeWidget";
+import { GenericNodeModel } from "./genericNodeModel";
+import * as React from "react";
+
+/**
+ * Node factory which creates editor nodes
+ */
+export class GenericNodeFactory extends SRD.AbstractNodeFactory {
+	/**
+	 * Constructs a GenericNodeFactory
+	 */
+	constructor() {
+		super("generic");
+	}
+
+	/**
+	 * Generates a node widget
+	 * @param diagramEngine diagram engine
+	 * @param node node to generate
+	 * @returns node widget jsx
+	 */
+	generateReactWidget(diagramEngine: SRD.DiagramEngine, node: GenericNodeModel): JSX.Element {
+		return <GenericNodeWidget node={node} />;
+	}
+
+	/**
+	 * Gets a new instance of a node model
+	 * @returns generic node model
+	 */
+	getNewInstance() {
+		return new GenericNodeModel();
+	}
+}

+ 50 - 0
nodeEditor/src/components/customDiragramNodes/generic/genericNodeModel.ts

@@ -0,0 +1,50 @@
+import { NodeModel } from "storm-react-diagrams";
+import { Nullable } from 'babylonjs/types';
+import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
+import { Texture } from 'babylonjs/Materials/Textures/texture';
+import { Vector2, Vector3, Vector4, Matrix } from 'babylonjs/Maths/math';
+import { GenericPortModel } from './genericPortModel';
+
+/**
+ * Generic node model which stores information about a node editor block
+ */
+export class GenericNodeModel extends NodeModel {
+	/**
+	 * The babylon block this node represents
+	 */
+	public block:Nullable<NodeMaterialBlock> = null;
+	/**
+	 * Labels for the block
+	 */
+	public headerLabels:Array<{text: string}> = []
+	/**
+	 * Texture for the node if it exists
+	 */
+	public texture: Nullable<Texture> = null;
+	/**
+	 * Vector2 for the node if it exists
+	 */
+	public vector2: Nullable<Vector2> = null;
+	/**
+	 * Vector3 for the node if it exists
+	 */
+	public vector3: Nullable<Vector3> = null;
+	/**
+	 * Vector4 for the node if it exists
+	 */
+	public vector4: Nullable<Vector4> = null;
+	/**
+	 * Matrix for the node if it exists
+	 */
+	public matrix: Nullable<Matrix> = null;
+
+	public ports: {[s:string]:GenericPortModel};
+
+	/**
+	 * Constructs the node model
+	 */
+	constructor() {
+		super("generic");
+	}
+
+}

+ 162 - 0
nodeEditor/src/components/customDiragramNodes/generic/genericNodeWidget.tsx

@@ -0,0 +1,162 @@
+import * as React from "react";
+import { PortWidget } from "storm-react-diagrams";
+import { GenericNodeModel } from './genericNodeModel';
+import { GenericPortModel } from './genericPortModel';
+import {TextureLineComponent} from "../../../sharedComponents/textureLineComponent"
+import {FileButtonLineComponent} from "../../../sharedComponents/fileButtonLineComponent"
+import { Vector2LineComponent } from '../../../sharedComponents/vector2LineComponent';
+import { Vector3LineComponent } from '../../../sharedComponents/vector3LineComponent';
+import { Nullable } from 'babylonjs/types';
+import { Texture } from 'babylonjs/Materials/Textures/texture';
+import { Engine } from 'babylonjs/Engines/engine';
+import { Tools } from 'babylonjs/Misc/tools';
+
+/**
+ * GenericNodeWidgetProps
+ */
+export interface GenericNodeWidgetProps {
+	node: Nullable<GenericNodeModel>;
+}
+
+/**
+ * GenericNodeWidgetState
+ */
+export interface GenericNodeWidgetState {}
+
+/**
+ * Used to display a node block for the node editor
+ */
+export class GenericNodeWidget extends React.Component<GenericNodeWidgetProps, GenericNodeWidgetState> {
+	/**
+	 * Creates a GenericNodeWidget
+	 * @param props 
+	 */
+	constructor(props: GenericNodeWidgetProps) {
+		super(props);
+		this.state = {}
+	}
+
+	/**
+	 * Replaces the texture of the node
+	 * @param file the file of the texture to use
+	 */
+	replaceTexture(file: File) {
+		if(!this.props.node){
+			return;
+		}
+
+		let texture = this.props.node.texture as Texture;
+		if(!texture){
+			this.props.node.texture = new Texture(null, Engine.LastCreatedScene)
+			texture = this.props.node.texture;
+		}
+
+        Tools.ReadFile(file, (data) => {
+            var blob = new Blob([data], { type: "octet/stream" });
+            var url = URL.createObjectURL(blob);
+
+            if (texture.isCube) {
+                let extension: string | undefined = undefined;
+                if (file.name.toLowerCase().indexOf(".dds") > 0) {
+                    extension = ".dds";
+                } else if (file.name.toLowerCase().indexOf(".env") > 0) {
+                    extension = ".env";
+                }
+
+                (texture as Texture).updateURL(url, extension, () => this.forceUpdate());
+            } else {
+                (texture as Texture).updateURL(url, null, () => this.forceUpdate());
+            }
+			(this.refs.textureView as TextureLineComponent).updatePreview()
+        }, undefined, true);
+    }
+
+	render() {
+		var headers = new Array<JSX.Element>()
+		var inputPorts = new Array<JSX.Element>()
+		var outputPorts = new Array<JSX.Element>()
+		var value = <div></div>
+		if(this.props.node){
+			// Header labels
+			this.props.node.headerLabels.forEach((h, i)=>{
+				headers.push(<div style={{fontWeight: "bold", borderBottomStyle: "solid"}} key={i}>{h.text}</div>)
+			})
+
+			// Input/Output ports
+			for(var key in this.props.node.ports){
+				var port = this.props.node.ports[key] as GenericPortModel;
+				if(port.position == "input"){
+					var control = <div></div>
+
+					// Color of the connection
+					var color = "black"
+					if(port.connection){
+						if(port.connection.isAttribute){
+							color = "red"
+						}else if(port.connection.isUniform){
+							color = "brown"
+						}
+						else if(port.connection.isVarying){
+							color = "purple"
+						}
+					}
+
+					inputPorts.push(
+						<div key={key} style={{paddingBottom: "8px"}}>
+							<div style={{display: "inline-block", borderStyle: "solid", marginBottom: "-4px", position: "absolute", left: "-17px", background: "#777777"}}>
+								<PortWidget key={key} name={port.name} node={this.props.node} />
+							</div>
+							<div style={{display: "inline-block", color: color}}>
+								{port.name} 
+							</div>
+							{control}
+						</div>
+					)
+				}else{
+					outputPorts.push(
+						<div key={key} style={{paddingBottom: "8px"}}>
+							<div style={{display: "inline-block"}}>
+								{port.name}
+							</div>
+							<div style={{display: "inline-block", borderStyle: "solid", marginBottom: "-4px", position: "absolute", right: "-17px", background: "#777777"}}>
+								<PortWidget key={key} name={port.name} node={this.props.node} />
+							</div>
+						</div>
+					)
+				}
+				
+			}
+
+			// Display the view depending on the value type of the node
+			if(this.props.node.texture){
+				value = (
+					<div>
+						<TextureLineComponent ref="textureView" width={100} height={100} texture={this.props.node.texture} hideChannelSelect={true}/>
+						<FileButtonLineComponent label="" onClick={(file) => this.replaceTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
+					</div>
+				)
+			} else if(this.props.node.vector3){
+				value = (
+					<div style={{width: "220px"}}>
+						<Vector3LineComponent label="" target={this.props.node} propertyName="vector3"></Vector3LineComponent>
+					</div>
+				)
+			} else if(this.props.node.vector2){
+				value = (
+					<div style={{width: "220px"}}>
+						<Vector2LineComponent label="" target={this.props.node} propertyName="vector2"></Vector2LineComponent>
+					</div>
+				)
+			}
+		}
+
+		return (
+			<div style={{background: "white", borderStyle: "solid", padding: "10px"}}>
+				{headers}
+				{inputPorts}
+				{outputPorts}
+				{value}
+			</div>
+		);
+	}
+}

+ 73 - 0
nodeEditor/src/components/customDiragramNodes/generic/genericPortModel.ts

@@ -0,0 +1,73 @@
+import { LinkModel, PortModel, DefaultLinkModel } from "storm-react-diagrams";
+import { Nullable } from 'babylonjs/types';
+import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+import { GenericNodeModel } from './genericNodeModel';
+
+/**
+ * Port model for the generic node
+ */
+export class GenericPortModel extends PortModel {
+	/**
+	 * If the port is input or output
+	 */
+	public position: string | "input" | "output";
+	/**
+	 * What the port is connected to
+	 */
+	public connection: Nullable<NodeMaterialConnectionPoint> = null;
+
+	
+	static idCounter = 0;
+
+	constructor(name:string, type: string = "input") {
+		super(name, "generic");
+		this.position = type;
+		GenericPortModel.idCounter++;
+	}
+
+	syncWithNodeMaterialConnectionPoint(connection:NodeMaterialConnectionPoint){
+		this.connection = connection;
+		this.name = connection.name;
+	}
+
+	getNodeModel(){
+		return this.parent as GenericNodeModel
+	}
+
+	link(outPort:GenericPortModel){
+		var link = this.createLinkModel()
+		link.setSourcePort(this)
+		link.setTargetPort(outPort)
+		return link;
+	}
+
+	getInputFromBlock(){
+
+	}
+
+	createLinkModel(): LinkModel {
+		return new DefaultLinkModel();
+	}
+
+	getValue:Function = ()=>{
+		return null;
+	}
+
+	static SortInputOutput(a:Nullable<GenericPortModel>, b:Nullable<GenericPortModel>){
+		if(!a || !b){
+			return null;
+		}else if(a.position == "output" && b.position == "input"){
+			return {
+				input: b,
+				output: a
+			}
+		}else if(b.position == "output" && a.position == "input"){
+			return {
+				input: a,
+				output: b
+			}
+		}else{
+			return null;
+		}
+	}
+}

+ 385 - 0
nodeEditor/src/components/graphEditor.tsx

@@ -0,0 +1,385 @@
+import {
+	DiagramEngine,
+	DiagramModel,
+	DiagramWidget,
+    MoveCanvasAction
+} from "storm-react-diagrams";
+
+import * as React from "react";
+import { GlobalState } from '../globalState';
+
+import { GenericNodeFactory } from './customDiragramNodes/generic/genericNodeFactory';
+import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPointTypes';
+import { GenericNodeModel } from './customDiragramNodes/generic/genericNodeModel';
+import { GenericPortModel } from './customDiragramNodes/generic/genericPortModel';
+import { Engine } from 'babylonjs/Engines/engine';
+import { LineContainerComponent } from "../sharedComponents/lineContainerComponent"
+import { ButtonLineComponent } from '../sharedComponents/buttonLineComponent';
+import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
+import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+import { Texture } from 'babylonjs/Materials/Textures/texture';
+import { Vector2, Vector3, Vector4, Matrix } from 'babylonjs/Maths/math';
+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 { MatrixMultiplicationBlock } from 'babylonjs/Materials/Node/Blocks/matrixMultiplicationBlock';
+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");
+
+/*
+Graph Editor Overview
+
+Storm React setup:
+GenericNodeModel - Represents the nodes in the graph and can be any node type (eg. texture, vector2, etc)
+GenericNodeWidget - Renders the node model in the graph 
+GenericPortModel - Represents the input/output of a node (contained within each GenericNodeModel)
+
+Generating/modifying the graph:
+Generating node graph - the createNodeFromObject method is used to recursively create the graph
+Modifications to the graph - The listener in the constructor of GraphEditor listens for port changes and updates the node material based on changes
+Saving the graph/generating code - Not yet done
+*/
+
+interface IGraphEditorProps {
+    globalState: GlobalState;
+}
+
+export class GraphEditor extends React.Component<IGraphEditorProps> {
+    private _engine:DiagramEngine;
+    private _model: DiagramModel;
+
+    private _nodes = new Array<GenericNodeModel>();
+
+    /**
+     * 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:{
+            column:number,
+            nodeMaterialBlock?:NodeMaterialBlock                      
+        }
+    ){
+        // 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 outputNode = new GenericNodeModel();
+        this._nodes.push(outputNode)
+        outputNode.setPosition(1600-(300*options.column), 200*this._rowPos[options.column])
+        this._model.addAll(outputNode);
+
+        if(options.nodeMaterialBlock){
+            outputNode.block = options.nodeMaterialBlock
+            outputNode.headerLabels.push({text: options.nodeMaterialBlock.getClassName()})
+
+            // Create output ports
+            options.nodeMaterialBlock._outputs.forEach((connection:any)=>{
+                var outputPort = new GenericPortModel(connection.name, "output");
+                outputPort.syncWithNodeMaterialConnectionPoint(connection);
+                outputNode.addPort(outputPort)
+            })
+
+            // Create input ports and nodes if they exist
+            options.nodeMaterialBlock._inputs.forEach((connection)=>{
+                var inputPort = new GenericPortModel(connection.name, "input");
+                inputPort.connection = connection;
+                outputNode.addPort(inputPort)
+                
+                if(connection._connectedPoint){
+                    // Block is not a leaf node, create node for the given block type
+                    var connectedNode;
+                    var existingNodes = this._nodes.filter((n)=>{return n.block == (connection as any)._connectedPoint._ownerBlock});
+                    if(existingNodes.length == 0){
+                        connectedNode = this.createNodeFromObject({column: options.column+1, nodeMaterialBlock: connection._connectedPoint._ownerBlock});
+                    }else{
+                        connectedNode = existingNodes[0];
+                    }
+           
+                    let link = connectedNode.ports[connection._connectedPoint.name].link(inputPort);
+                    this._model.addAll(link);
+                    
+                }else {
+                    // 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"
+                    }
+                    
+                    // Create links
+                    var localNode = this.addValueNode(type, options.column+1, connection);
+                    var ports = localNode.getPorts()
+                    for(var key in ports){
+                        let link = (ports[key] as GenericPortModel).link(inputPort);
+                        this._model.addAll(link);
+                    }
+                }
+            })
+        }
+        
+        return outputNode;
+    }
+
+    componentDidMount(){
+        if(this.props.globalState.hostDocument){
+            var widget = (this.refs["test"] as DiagramWidget);
+            widget.setState({document: this.props.globalState.hostDocument})
+            this.props.globalState.hostDocument!.addEventListener("keyup", widget.onKeyUpPointer as any, false);
+        }
+    }
+
+    componentWillUnmount(){
+        if(this.props.globalState.hostDocument){
+            var widget = (this.refs["test"] as DiagramWidget);
+            this.props.globalState.hostDocument!.removeEventListener("keyup", widget.onKeyUpPointer as any, false);
+        }
+    }
+
+    constructor(props: IGraphEditorProps) {
+        super(props);
+
+        // setup the diagram engine
+        this._engine = new DiagramEngine();
+        this._engine.installDefaultFactories()
+        this._engine.registerNodeFactory(new GenericNodeFactory());
+
+        // setup the diagram model
+        this._model = new DiagramModel();
+
+        // Listen to events to connect/disconnect blocks or
+        this._model.addListener({
+            linksUpdated: (e)=>{
+                if(!e.isCreated){
+                    // Link is deleted
+                    console.log("link deleted");
+                    var link = GenericPortModel.SortInputOutput(e.link.sourcePort as GenericPortModel, e.link.targetPort as GenericPortModel);
+                    console.log(link)
+                    if(link){
+                        if(link.output.connection && link.input.connection){
+                            // Disconnect standard nodes
+                            console.log("disconnected "+link.output.connection.name+" from "+link.input.connection.name)
+                            link.output.connection.disconnectFrom(link.input.connection)
+                            link.input.syncWithNodeMaterialConnectionPoint(link.input.connection)
+                            link.output.syncWithNodeMaterialConnectionPoint(link.output.connection)
+                        }else if(link.input.connection && link.input.connection.value){
+                            console.log("value link removed");
+                            link.input.connection.value = null;
+                        }else{
+                            console.log("invalid link error");
+                        }   
+                    }
+                }else{
+                    console.log("link created")
+                    console.log(e.link.sourcePort)
+                }
+                e.link.addListener({
+                    sourcePortChanged: ()=>{
+                        console.log("port change")
+                    },
+                    targetPortChanged: ()=>{
+                        // Link is created with a target port
+                        console.log("Link set to target")
+                        var link = GenericPortModel.SortInputOutput(e.link.sourcePort as GenericPortModel, e.link.targetPort as GenericPortModel);
+                        
+                        if(link){
+                            if(link.output.connection && link.input.connection){
+                               console.log("link standard blocks")
+                               link.output.connection.connectTo(link.input.connection)
+                            }else if(link.input.connection){
+                                console.log("link value to standard block")
+                                link.input.connection.value = link.output.getValue();
+                                
+                            }
+                            if(this.props.globalState.nodeMaterial){
+                                this.props.globalState.nodeMaterial.build()
+                            }
+                        }
+                    }
+                    
+                })
+                
+            },
+            nodesUpdated: (e)=>{
+                if(e.isCreated){
+                    console.log("new node")
+                }else{
+                    console.log("node deleted")
+                }
+            }
+        })
+
+        // Load graph of nodes from the material
+        if(this.props.globalState.nodeMaterial){
+            var material:any = this.props.globalState.nodeMaterial;
+            material._vertexOutputNodes.forEach((n:any)=>{
+                this.createNodeFromObject({column: 0, nodeMaterialBlock: n});
+            })
+            material._fragmentOutputNodes.forEach((n:any)=>{
+                this.createNodeFromObject({column: 0, nodeMaterialBlock: n});
+            })
+        }
+
+        // Zoom out a bit at the start
+        this._model.setZoomLevel(20)
+
+        // load model into engine
+        this._engine.setDiagramModel(this._model);
+    }
+
+    addNodeFromClass(ObjectClass:typeof NodeMaterialBlock){
+        var block = new ObjectClass(ObjectClass.prototype.getClassName()+"sdfsdf")
+        var localNode = this.createNodeFromObject({column: 0, nodeMaterialBlock: block})
+        var widget = (this.refs["test"] as DiagramWidget);
+       
+        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));
+        }, 500);
+
+        return localNode
+    }
+
+    addValueNode(type: string, column = 0, connection?: NodeMaterialConnectionPoint){
+        var localNode = this.createNodeFromObject({column: column})
+        var outPort = new GenericPortModel(type, "output");
+        if(type == "Texture"){
+            outPort.getValue = ()=>{
+                return localNode.texture;
+            }
+            if(connection && connection.value){
+                localNode.texture = connection.value
+            }else{
+                localNode.texture = new Texture(null, Engine.LastCreatedScene)
+            }
+        }else if(type == "Vector2"){
+            outPort.getValue = ()=>{
+                return localNode.vector2;
+            }
+            if(connection && connection.value){
+                localNode.vector2 = connection.value
+            }else{
+                localNode.vector2 = new Vector2()
+            }
+        }else if(type == "Vector3"){
+            outPort.getValue = ()=>{
+                return localNode.vector3;
+            }
+            if(connection && connection.value){
+                localNode.vector3 = connection.value
+            }else{
+                localNode.vector3 = new Vector3()
+            }
+        }else if(type == "Vector4"){
+            outPort.getValue = ()=>{
+                return localNode.vector4;
+            }
+            if(connection && connection.value){
+                localNode.vector4 = connection.value
+            }else{
+                localNode.vector4 = new Vector4(0,0,0,1)
+            }
+        }else if(type == "Matrix"){
+            outPort.getValue = ()=>{
+                return localNode.matrix;
+            }
+            if(connection && connection.value){
+                localNode.matrix = connection.value
+            }else{
+                localNode.matrix = new Matrix()
+            }
+        }else{
+            console.log("Node type "+type+"is not supported")
+        }
+        localNode.addPort(outPort)
+        this.forceUpdate()
+
+        return localNode;
+    }
+
+    
+
+    // Block types used to create the menu from
+    allBlocks = {
+        Fragment: [AlphaTestBlock, FragmentOutputBlock, ImageProcessingBlock, RGBAMergerBlock, RGBASplitterBlock, TextureBlock],
+        Vertex: [BonesBlock, InstancesBlock, MorphTargetsBlock, VertexOutputBlock],
+        Dual: [FogBlock],
+        Other: [AddBlock, ClampBlock, MatrixMultiplicationBlock, MultiplyBlock, Vector2TransformBlock, Vector3TransformBlock, Vector4TransformBlock],
+        Value: ["Texture", "Vector2", "Vector3", "Matrix"],
+    }
+
+    render() {
+        // Create node menu
+        var blockMenu = []
+        for(var key in this.allBlocks){
+            var blockList = (this.allBlocks as any)[key].map((b:any)=>{
+                var label = typeof b === "string" ? b : b.prototype.getClassName()
+                var onClick =typeof b === "string" ? () => {this.addValueNode(b)} : () => {this.addNodeFromClass(b)};
+                return  <ButtonLineComponent label={label} onClick={onClick} />
+            })
+            blockMenu.push(
+                <LineContainerComponent  title={key+" blocks"}>
+                    {blockList}
+                </LineContainerComponent>
+            )
+        }
+
+        return (
+            <div id="node-editor-graph-root" style={{
+                display: "flex",
+                height: "100%",
+                background: "#464646",
+            }}>
+                {/* Node creation menu */}
+                <div id="actionTabs" style={{width: "170px", borderRightStyle: "solid", borderColor: "grey", borderWidth: "1px" }} >
+                    <div className="tabs" style={{gridTemplateRows: "0px 1fr"}}>
+                        <div className="labels"/>
+                        <div className="panes">
+                            <div className="pane">
+                                {blockMenu}
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                
+                {/* The node graph diagram */}
+                <DiagramWidget deleteKeys={[46]} ref={"test"} inverseZoom={true} className="srd-demo-canvas" diagramEngine={this._engine} maxNumberPointsPerLink={0} />
+            </div>
+        );
+
+    }
+}

+ 892 - 0
nodeEditor/src/components/main.scss

@@ -0,0 +1,892 @@
+
+#node-editor-graph-root {
+    $line-padding-left: 2px;
+
+    #inspector-host {
+        position: absolute;
+        right: 0px;
+        top:0px;
+        bottom: 0px;
+    }
+    
+    #__resizable_base__ {
+        display: none;
+    }
+    
+    #actionTabs {
+        background: #333333;
+        height: 100%;
+        margin: 0;
+        padding: 0;
+        display: grid;
+        grid-template-rows: auto 1fr;
+        font: 14px "Arial";    
+        overflow: hidden;
+    
+        .hoverIcon:hover {
+            opacity: 0.8;
+        }
+    
+        #header {
+            height: 30px;
+            font-size: 16px;
+            color: white;
+            background: #222222;
+            grid-row: 1;
+            text-align: center;
+            display: grid;
+            grid-template-columns: 30px 1fr 50px;        
+            -webkit-user-select: none; 
+            -moz-user-select: none;   
+            -ms-user-select: none;    
+            user-select: none;                
+    
+            #logo {
+                grid-column: 1; 
+                width: 24px;
+                height: 24px;
+                display: flex;
+                align-self: center;   
+                justify-self: center;
+            }        
+    
+            #back {
+                grid-column: 1; 
+                display: grid;
+                align-self: center;   
+                justify-self: center;
+                cursor: pointer;
+            }              
+    
+            #title {
+                grid-column: 2; 
+                display: grid;
+                align-items: center;   
+                text-align: center;
+            }
+    
+            #commands {
+                grid-column: 3; 
+                display: grid;
+                align-items: center;  
+                grid-template-columns: 1fr 1fr;   
+                
+                .expand {
+                    grid-column: 1;
+                    display: grid;
+                    align-items: center;   
+                    justify-items: center;
+                    cursor: pointer;     
+                }
+    
+                .close {
+                    grid-column: 2;
+                    display: grid;
+                    align-items: center;   
+                    justify-items: center;
+                    cursor: pointer;     
+                }        
+            }
+        }
+    
+        .tabs {
+            display: grid;
+            grid-row: 2;
+            grid-template-rows: 40px 1fr;
+            font: 14px "Arial";
+            overflow: hidden;
+    
+            .labels {
+                grid-row: 1;
+                display: flex;
+                align-items: center;
+                justify-items: center;
+                border-bottom: 1px solid #ffffff; 
+                margin: 0;
+                padding: 0;         
+    
+                .label {
+                    font-size: 24px;
+                    color: white;
+                    width: 40px;
+                    display: flex;
+                    align-content: center;
+                    justify-content: center;
+                    border: 1px solid transparent;            
+                    border-bottom: none;    
+                    background: #333333;
+                    padding: 5px;  
+                    height: 28px;
+                    cursor: pointer;
+    
+                    &.active {
+                        border-color: #ffffff;  
+                        border-bottom: 2px solid transparent;           
+                        margin-bottom: -2px;
+                    }
+                }
+            }
+    
+            .panes {
+                grid-row: 2;
+    
+                display: grid;
+                grid-template-rows: 100%;
+    
+                overflow: hidden;
+    
+                .infoMessage {
+                    opacity: 0.5;
+                    color: white;
+                    margin: 15px 5px 0px 5px;
+                                    
+                }
+    
+                .pane {
+                    color: white;
+    
+                    overflow-x: hidden;
+                    overflow-y: auto;
+                    height: 100%;
+    
+                    -webkit-user-select: none; 
+                    -moz-user-select: none;   
+                    -ms-user-select: none;    
+                    user-select: none;     
+    
+                    .underline {
+                        border-bottom: 0.5px solid rgba(255, 255, 255, 0.5);
+                    }
+                    
+                    .textureLinkLine {
+                        display: grid;
+                        grid-template-columns: auto 1fr;
+    
+                        .debug {
+                            grid-column: 1;
+                            margin-left: 5px;
+                            margin-right: 5px;
+                            display: grid;
+                            align-items: center; 
+                            justify-items: center;                          
+                            cursor: pointer;
+                            opacity: 0.5;
+    
+                            &.selected {
+                                opacity: 1.0;
+                            }
+                        }
+    
+                        .textLine {
+                            grid-column: 2;
+                        }
+                    }
+    
+                    .messageLine {
+                        text-align: center;
+                        font-size: 12px;
+                        font-style: italic;
+                        opacity: 0.6;
+                    }
+    
+                    .iconMessageLine {
+                        padding-left: $line-padding-left;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 30px 1fr;
+    
+                        .icon {
+                            grid-column: 1;
+                            display: grid;
+                            align-items: center;
+                            justify-items: center;
+                        }
+    
+                        .value {
+                            grid-column: 2;
+                            display: flex;
+                            align-items: center;
+                        }
+                    }
+    
+                    .textLine {
+                        padding-left: $line-padding-left;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 1fr auto;
+    
+                        .label {
+                            grid-column: 1;
+                            display: flex;
+                            align-items: center;
+                        }
+    
+                        .link-value {
+                            grid-column: 2;
+                            white-space: nowrap;
+                            text-overflow: ellipsis;
+                            overflow: hidden;
+                            text-align: end;
+                            opacity: 0.8;
+                            margin:5px;
+                            margin-top: 6px;
+                            max-width: 140px;
+                            text-decoration: underline;
+                            cursor: pointer;
+                        }
+    
+                        .value {
+                            grid-column: 2;
+                            white-space: nowrap;
+                            text-overflow: ellipsis;
+                            overflow: hidden;
+                            text-align: end;
+                            opacity: 0.8;
+                            margin:5px;
+                            margin-top: 6px;
+                            max-width: 200px;
+                            -webkit-user-select: text; 
+                            -moz-user-select: text;   
+                            -ms-user-select: text;    
+                            user-select: text;                
+    
+                            &.check {
+                                color: green;
+                            }
+    
+                            &.uncheck {
+                                color: red;
+                            }  
+                        }
+                    }
+    
+                    .textInputLine {
+                        padding-left: $line-padding-left;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 1fr 120px;
+    
+                        .label {
+                            grid-column: 1;
+                            display: flex;
+                            align-items: center;
+                        }
+    
+                        .value {                        
+                            display: flex;
+                            align-items: center;
+                            grid-column: 2;
+                            
+                            input {
+                                width: 110px;
+                            }
+                        }
+                    }
+    
+                    .buttonLine {
+                        height: 30px;
+                        display: grid;
+                        align-items: center;
+                        justify-items: stretch;
+    
+                        input[type="file"] {
+                            display: none;
+                        }
+    
+                        .file-upload {
+                            background: transparent;
+                            border: 1px solid rgb(51, 122, 183);
+                            margin: 0px 10px;
+                            color:white;
+                            padding: 4px 5px;
+                            opacity: 0.9;
+                            cursor: pointer;
+                            text-align: center;
+                        }
+    
+                        .file-upload:hover {
+                            opacity: 1.0;
+                        }
+    
+                        .file-upload:active {
+                            transform: scale(0.98);
+                            transform-origin: 0.5 0.5;
+                        }
+    
+                        button {
+                            background: #222222;
+                            border: 1px solid rgb(51, 122, 183);
+                            margin: 5px 10px 5px 10px;
+                            color:white;
+                            padding: 4px 5px;
+                            opacity: 0.9;
+                        }
+    
+                        button:hover {
+                            opacity: 1.0;
+                        }
+    
+                        button:active {
+                            background: #282828;
+                        }   
+                        
+                        button:focus {
+                            border: 1px solid rgb(51, 122, 183);
+                            outline: 0px;
+                        }  
+                    }
+    
+                    .radioLine {
+                        padding-left: $line-padding-left;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 1fr 24px;
+    
+                        .label {
+                            grid-column: 1;
+                            display: flex;
+                            align-items: center;
+                        }
+    
+                        .radioContainer {
+                            display: flex;
+                            align-items: center;
+    
+                            .radio {
+                                grid-column: 2;                        
+                                display: none;
+    
+                                &:checked + label:before {
+                                    border-color: rgb(51, 122, 183);
+                                }
+                                &:checked + label:after {
+                                    transform: scale(1);
+                                }                        
+                            }
+    
+                            .labelForRadio {
+                                display: inline-block;
+                                height: 14px;
+                                position: relative;
+                                padding: 0 24px;
+                                margin-bottom: 0;
+                                cursor: pointer;
+                                vertical-align: bottom;
+                                &:before, &:after {
+                                    position: absolute;            
+                                    content: '';  
+                                    border-radius: 50%;
+                                    transition: all .3s ease;
+                                    transition-property: transform, border-color;
+                                }
+                                &:before {
+                                    left: 0px;
+                                    top: 0;
+                                    width: 16px;
+                                    height: 16px;
+                                    border: 2px solid white;
+                                }
+                                &:after {
+                                    top: 6px;
+                                    left: 6px;
+                                    width: 8px;
+                                    height: 8px;
+                                    transform: scale(0);
+                                    background:rgb(51, 122, 183);
+                                }
+                            }
+                        }
+                    }
+    
+                    .vector3Line {
+                        padding-left:$line-padding-left;                    
+                        display: grid;
+    
+                        .firstLine {
+                            display: grid;
+                            grid-template-columns: 1fr auto 20px;
+                            height: 30px;
+    
+                            .label {
+                                grid-column: 1;
+                                display: flex;
+                                align-items: center;
+                            }
+    
+                            .vector {
+                                grid-column: 2;
+                                display: flex;
+                                align-items: center;
+                                text-align: right;
+                                opacity: 0.8;
+                            }
+    
+                            .expand {
+                                grid-column: 3;
+                                display: grid;
+                                align-items: center;
+                                justify-items: center;
+                                cursor: pointer;
+                            }
+                        }
+    
+                        .secondLine {
+                            display: grid;
+                            padding-right: 5px;  
+                            border-left: 1px solid rgb(51, 122, 183);
+    
+                            .numeric {
+                                display: grid;
+                                grid-template-columns: 1fr auto;
+                            }
+    
+                            .numeric-label {
+                                text-align: right;
+                                grid-column: 1;
+                                display: flex;
+                                align-items: center;                            
+                                justify-self: right;
+                                margin-right: 10px;                          
+                            }
+    
+                            .numeric-value {
+                                width: 120px;
+                                grid-column: 2;
+                                display: flex;
+                                align-items: center;  
+                                border: 1px solid  rgb(51, 122, 183);
+                            }                        
+                        }
+                    }
+    
+                    .checkBoxLine {
+                        padding-left: $line-padding-left;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 1fr auto;
+    
+    
+                        .label {
+                            grid-column: 1;
+                            display: flex;
+                            align-items: center;
+                        }
+    
+                        .checkBox {
+                            grid-column: 2;
+                            
+                            display: flex;
+                            align-items: center;
+    
+                            .lbl {
+                                position: relative;
+                                display: block;
+                                height: 14px;
+                                width: 34px;
+                                margin-right: 5px;
+                                background: #898989;
+                                border-radius: 100px;
+                                cursor: pointer;
+                                transition: all 0.3s ease;
+                            }
+    
+                            .lbl:after {
+                                position: absolute;
+                                left: 3px;
+                                top: 2px;
+                                display: block;
+                                width: 10px;
+                                height: 10px;
+                                border-radius: 100px;
+                                background: #fff;
+                                box-shadow: 0px 3px 3px rgba(0,0,0,0.05);
+                                content: '';
+                                transition: all 0.15s ease;
+                            }
+    
+                            .lbl:active:after { 
+                                transform: scale(1.15, 0.85); 
+                            }
+    
+                            .cbx:checked ~ label { 
+                                background: rgb(51, 122, 183);
+                            }
+    
+                            .cbx:checked ~ label:after {
+                                left: 20px;
+                                background: rgb(22, 73, 117);
+                            }
+    
+                            .hidden { 
+                                display: none; 
+                            }               
+                        }                    
+                    }                   
+    
+                    .textureLine {                   
+                        display: grid;
+                        grid-template-rows: 30px auto;
+    
+                        .control {
+                            margin-top: 2px;
+                            grid-row: 1;
+                            display: grid;
+                            grid-template-columns: 1fr 40px 40px 40px 40px 40px 1fr;
+    
+                            .red {
+                                grid-column: 2;
+                            }
+    
+                            .green {
+                                grid-column: 3;
+                            }
+    
+                            .blue {
+                                grid-column: 4;
+                            }
+    
+                            .alpha {
+                                grid-column: 5;
+                            }                        
+    
+                            .all {
+                                grid-column: 6;
+                            }                        
+                        }
+    
+                        .control3D {
+                            margin-top: 2px;
+                            grid-row: 1;
+                            display: grid;
+                            grid-template-columns: 1fr 40px 40px 40px 40px 40px 40px 1fr;
+    
+                            .px {
+                                grid-column: 2;
+                            }
+    
+                            .nx {
+                                grid-column: 3;
+                            }
+    
+                            .py {
+                                grid-column: 4;
+                            }
+    
+                            .ny {
+                                grid-column: 5;
+                            }   
+    
+                            .pz {
+                                grid-column: 6;
+                            }
+    
+                            .nz {
+                                grid-column: 7;
+                            }                     
+                        }                    
+    
+                        .command {
+                            border: 1px solid transparent;
+                            background:transparent;
+                            color: white;
+                        }
+    
+                        .selected {
+                            border: 1px solid rgb(51, 122, 183);
+                        }
+    
+                        .preview {
+                            grid-row: 2;
+                            display: grid;
+                            align-self: center;
+                            justify-self: center;
+                            height: 256px;
+                            width: 256px;
+                            margin-top: 5px;
+                            margin-bottom: 5px;
+                            border: 2px solid rgba(255, 255, 255, 0.4);
+                        }
+                    }
+    
+                    .gltf-extension-property {
+                        margin-left: 30px;
+                        border-left: 1px solid rgb(51, 122, 183);
+                    }
+    
+                    .floatLine {
+                        padding-left: $line-padding-left;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 1fr 120px;
+    
+    
+                        .label {
+                            grid-column: 1;
+                            display: flex;
+                            align-items: center;
+                        }
+    
+                        .value {
+                            grid-column: 2;
+                            
+                            display: flex;
+                            align-items: center;
+                            
+                            input {
+                                width: 110px;
+                            }
+                        }
+                    }
+    
+                    .sliderLine {
+                        padding-left: 2px;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 1fr auto;
+    
+                        .label {
+                            grid-column: 1;
+                            display: flex;
+                            align-items: center;
+                        }
+    
+                        .slider {
+                            grid-column: 2;
+                            margin-right: 5px;
+                            
+                            display: flex;
+                            align-items: center;
+    
+                            .range {
+                                -webkit-appearance: none;
+                                width: 120px;
+                                height: 6px;
+                                background: #d3d3d3;
+                                border-radius: 5px;
+                                outline: none;
+                                opacity: 0.7;
+                                -webkit-transition: .2s;
+                                transition: opacity .2s;
+                            }
+                            
+                            .range:hover {
+                                opacity: 1;
+                            }
+                            
+                            .range::-webkit-slider-thumb {
+                                -webkit-appearance: none;
+                                appearance: none;
+                                width: 14px;
+                                height: 14px;
+                                border-radius: 50%;
+                                background: rgb(51, 122, 183);
+                                cursor: pointer;
+                            }
+                            
+                            .range::-moz-range-thumb {
+                                width: 14px;
+                                height: 14px;
+                                border-radius: 50%;
+                                background: rgb(51, 122, 183);
+                                cursor: pointer;
+                            }
+                        }                    
+                    }       
+                    
+                    .color3Line {
+                        padding-left: $line-padding-left;
+                        display: grid;
+    
+                        .firstLine {
+                            height: 30px;
+                            display: grid;
+                            grid-template-columns: 1fr auto 20px 20px;
+    
+                            .label {
+                                grid-column: 1;
+                                display: flex;
+                                align-items: center;
+                            }
+    
+                            .color3 {
+                                grid-column: 2;
+                                
+                                display: flex;
+                                align-items: center;   
+    
+                                input[type="color"] {
+                                    -webkit-appearance: none;
+                                    border: 1px solid rgba(255, 255, 255, 0.5);
+                                    padding: 0;
+                                    width: 30px;
+                                    height: 20px;
+                                }
+                                input[type="color"]::-webkit-color-swatch-wrapper {
+                                    padding: 0;
+                                }
+                                input[type="color"]::-webkit-color-swatch {
+                                    border: none;
+                                }
+                                
+                                input {
+                                    margin-right: 5px;
+                                }
+                            }
+    
+                            .copy {
+                                grid-column: 3;
+                                display: grid;
+                                align-items: center;
+                                justify-items: center;
+                                cursor: pointer;
+                            }
+    
+                            .expand {
+                                grid-column: 4;
+                                display: grid;
+                                align-items: center;
+                                justify-items: center;
+                                cursor: pointer;
+                            }
+                        }   
+    
+                        .secondLine {
+                            display: grid;
+                            padding-right: 5px;  
+                            border-left: 1px solid rgb(51, 122, 183);
+    
+                            .numeric {
+                                display: grid;
+                                grid-template-columns: 1fr auto;
+                            }
+    
+                            .numeric-label {
+                                text-align: right;
+                                grid-column: 1;
+                                display: flex;
+                                align-items: center;                            
+                                justify-self: right;
+                                margin-right: 10px;                          
+                            }
+    
+                            .numeric-value {
+                                width: 120px;
+                                grid-column: 2;
+                                display: flex;
+                                align-items: center;  
+                                border: 1px solid  rgb(51, 122, 183);
+                            }                        
+                        }                  
+                    }     
+                    
+                    .listLine {
+                        padding-left: $line-padding-left;
+                        height: 30px;
+                        display: grid;
+                        grid-template-columns: 1fr auto;
+    
+    
+                        .label {
+                            grid-column: 1;
+                            display: flex;
+                            align-items: center;
+                        }
+    
+                        .options {
+                            grid-column: 2;
+                            
+                            display: flex;
+                            align-items: center;   
+                            margin-right: 5px;
+    
+                            select {
+                                width: 115px;
+                            }
+                        }                    
+                    }                   
+    
+                    .paneContainer {
+                        margin-top: 3px;
+                        display:grid;
+                        grid-template-rows: 100%;
+                        grid-template-columns: 100%;
+                        
+                        .paneList {
+                            border-left: 3px solid transparent;
+                        }
+    
+                        &:hover {  
+                            .paneList {                      
+                                border-left: 3px solid rgba(51, 122, 183, 0.8);
+                            }
+    
+                            .paneContainer-content {
+                                .header {
+                                    .title {   
+                                        border-left: 3px solid rgb(51, 122, 183);
+                                    }
+                                }
+                            }
+                        }
+                        
+                        .paneContainer-highlight-border {
+                            grid-row: 1;
+                            grid-column: 1;
+                            opacity: 1;
+                            border: 3px solid red;
+                            transition: opacity 250ms;
+                            pointer-events: none;
+                            
+                            &.transparent {
+                                opacity: 0;
+                            }
+                        }
+    
+                        .paneContainer-content {
+                            grid-row: 1;
+                            grid-column: 1;
+    
+                            .header {
+                                display: grid;
+                                grid-template-columns: 1fr auto;
+                                background: #555555;    
+                                height: 30px;   
+                                padding-right: 5px;                        
+                                cursor: pointer;
+                                
+                                .title {                                
+                                    border-left: 3px solid transparent;
+                                    padding-left: 5px;
+                                    grid-column: 1;
+                                    display: flex;
+                                    align-items: center;
+                                }
+    
+                                .collapse {
+                                    grid-column: 2;
+                                    display: flex;
+                                    align-items: center;  
+                                    justify-items: center;
+                                    transform-origin: center;
+    
+                                    &.closed {
+                                        transform: rotate(180deg);
+                                    }
+                                }                        
+                            }
+    
+                            .paneList > div:not(:last-child) {
+                                border-bottom: 0.5px solid rgba(255, 255, 255, 0.1);
+                            }
+    
+                            .fragment > div:not(:last-child)  {
+                                border-bottom: 0.5px solid rgba(255, 255, 255, 0.1);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+

+ 6 - 0
nodeEditor/src/globalState.ts

@@ -0,0 +1,6 @@
+import {NodeMaterial} from "babylonjs/Materials/Node/nodeMaterial"
+import {Nullable} from "babylonjs/types"
+export class GlobalState {
+    nodeMaterial?:NodeMaterial;
+    hostDocument?:Nullable<Document>;
+}

+ 1 - 0
nodeEditor/src/index.ts

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

+ 9 - 0
nodeEditor/src/legacy/legacy.ts

@@ -0,0 +1,9 @@
+import { NodeEditor } from "../index";
+
+var globalObject = (typeof global !== 'undefined') ? global : ((typeof window !== 'undefined') ? window : undefined);
+if (typeof globalObject !== "undefined") {
+    (<any>globalObject).BABYLON = (<any>globalObject).BABYLON || {};
+    (<any>globalObject).BABYLON.NodeEditor = NodeEditor;
+}
+
+export * from "../index";

+ 57 - 0
nodeEditor/src/nodeEditor.ts

@@ -0,0 +1,57 @@
+import * as React from "react";
+import * as ReactDOM from "react-dom";
+import { GlobalState } from './globalState';
+import { GraphEditor } from './components/graphEditor';
+import {NodeMaterial} from "babylonjs/Materials/Node/nodeMaterial"
+import {Popup} from "../src/sharedComponents/popup"
+/**
+ * Interface used to specify creation options for the node editor
+ */
+export interface INodeEditorOptions {
+    /**
+     * Defines the DOM element that will host the node editor
+     */
+    hostElement?: HTMLDivElement
+    nodeMaterial?: NodeMaterial
+}
+
+/**
+ * Class used to create a node editor
+ */
+export class NodeEditor {
+    /**
+     * Show the node editor
+     * @param options defines the options to use to configure the node editor
+     */
+    public static Show(options: INodeEditorOptions) {
+        if(!options.hostElement){
+            options.hostElement = Popup.CreatePopup("SCENE EXPLORER", "node-editor", 1000, 800)!;
+        }
+        let globalState = new GlobalState();
+        globalState.nodeMaterial = options.nodeMaterial
+        globalState.hostDocument = options.hostElement.ownerDocument
+
+        const graphEditor = React.createElement(GraphEditor, {
+            globalState: globalState
+        });
+
+        ReactDOM.render(graphEditor, options.hostElement);
+
+        // Close the popup window when the page is refreshed or scene is disposed
+        var popupWindow = (Popup as any)["node-editor"];
+        if(globalState.nodeMaterial && popupWindow){
+            globalState.nodeMaterial.getScene().onDisposeObservable.addOnce(()=>{
+                if(popupWindow){
+                    popupWindow.close();
+                }
+            })
+            window.onbeforeunload = function(event) {
+                var popupWindow = (Popup as any)["node-editor"];
+                if(popupWindow){
+                    popupWindow.close();
+                }
+            };
+        }
+    }
+}
+

+ 21 - 0
nodeEditor/src/sharedComponents/buttonLineComponent.tsx

@@ -0,0 +1,21 @@
+import * as React from "react";
+
+export interface IButtonLineComponentProps {
+    label: string;
+    onClick: () => void;
+}
+
+export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+    constructor(props: IButtonLineComponentProps) {
+        super(props);
+    }
+
+    render() {
+
+        return (
+            <div className="buttonLine">
+                <button onClick={() => this.props.onClick()}>{this.props.label}</button>
+            </div>
+        );
+    }
+}

+ 33 - 0
nodeEditor/src/sharedComponents/fileButtonLineComponent.tsx

@@ -0,0 +1,33 @@
+import * as React from "react";
+
+interface IFileButtonLineComponentProps {
+    label: string;
+    onClick: (file: File) => void;
+    accept: string;
+}
+
+export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+    constructor(props: IFileButtonLineComponentProps) {
+        super(props);
+    }
+
+    onChange(evt: any) {
+        var files: File[] = evt.target.files;
+        if (files && files.length) {
+            this.props.onClick(files[0]);
+        }
+
+        evt.target.value = "";
+    }
+
+    render() {
+        return (
+            <div className="buttonLine">
+                <label htmlFor="file-upload" className="file-upload">
+                    {this.props.label}
+                </label>
+                <input ref="upload" id="file-upload" type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} />
+            </div>
+        );
+    }
+}

+ 120 - 0
nodeEditor/src/sharedComponents/lineContainerComponent.tsx

@@ -0,0 +1,120 @@
+import * as React from "react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
+
+
+interface ILineContainerComponentProps {
+    globalState?: any; //
+    title: string;
+    children: any[] | any;
+    closed?: boolean;
+}
+
+export class LineContainerComponent extends React.Component<ILineContainerComponentProps, { isExpanded: boolean, isHighlighted: boolean }> {
+    private static _InMemoryStorage: { [key: string]: boolean };
+
+    constructor(props: ILineContainerComponentProps) {
+        super(props);
+
+        let initialState: boolean;
+
+        try {
+            if (LineContainerComponent._InMemoryStorage && LineContainerComponent._InMemoryStorage[this.props.title] !== undefined) {
+                initialState = LineContainerComponent._InMemoryStorage[this.props.title];
+            } else if (typeof (Storage) !== "undefined" && localStorage.getItem(this.props.title) !== null) {
+                initialState = localStorage.getItem(this.props.title) === "true";
+            } else {
+                initialState = !this.props.closed;
+            }
+        }
+        catch (e) {
+            LineContainerComponent._InMemoryStorage = {};
+            LineContainerComponent._InMemoryStorage[this.props.title] = !this.props.closed
+            initialState = !this.props.closed;
+        }
+
+        this.state = { isExpanded: initialState, isHighlighted: false };
+    }
+
+    switchExpandedState(): void {
+        const newState = !this.state.isExpanded;
+
+        try {
+            if (LineContainerComponent._InMemoryStorage) {
+                LineContainerComponent._InMemoryStorage[this.props.title] = newState;
+            } else if (typeof (Storage) !== "undefined") {
+                localStorage.setItem(this.props.title, newState ? "true" : "false");
+            }
+        }
+        catch (e) {
+            LineContainerComponent._InMemoryStorage = {};
+            LineContainerComponent._InMemoryStorage[this.props.title] = newState;
+        }
+
+        this.setState({ isExpanded: newState });
+    }
+
+    componentDidMount() {
+        if (this.props.globalState && !this.props.globalState.selectedLineContainerTitle) {
+            return;
+        }
+
+        if (this.props.globalState && this.props.globalState.selectedLineContainerTitle === this.props.title) {
+            setTimeout(() => {
+                this.props.globalState!.selectedLineContainerTitle = "";
+            });
+
+            this.setState({ isExpanded: true, isHighlighted: true });
+
+            window.setTimeout(() => {
+                this.setState({ isHighlighted: false });
+            }, 5000);
+        } else {
+            this.setState({isExpanded: false});
+        }        
+    }
+
+    renderHeader() {
+        const className = this.state.isExpanded ? "collapse" : "collapse closed";
+
+        return (
+            <div className="header" onClick={() => this.switchExpandedState()}>
+                <div className="title">
+                    {this.props.title}
+                </div>
+                <div className={className}>
+                    <FontAwesomeIcon icon={faChevronDown} />
+                </div>
+            </div>
+        );
+    }
+
+    render() {
+        if (!this.state.isExpanded) {
+            return (
+                <div className="paneContainer">
+                    <div className="paneContainer-content">
+                        {
+                            this.renderHeader()
+                        }
+                    </div>
+                </div>
+            );
+        }
+
+        return (
+            <div className="paneContainer">
+                <div className="paneContainer-content">
+                    {
+                        this.renderHeader()
+                    }
+                    <div className="paneList">
+                        {this.props.children}
+                    </div >
+                </div>
+                <div className={"paneContainer-highlight-border" + (!this.state.isHighlighted ? " transparent" : "")}>
+                </div>
+            </div>
+        );
+    }
+}

+ 69 - 0
nodeEditor/src/sharedComponents/numericInputComponent.tsx

@@ -0,0 +1,69 @@
+import * as React from "react";
+
+interface INumericInputComponentProps {
+    label: string;
+    value: number;
+    step?: number;
+    onChange: (value: number) => void;
+}
+
+export class NumericInputComponent extends React.Component<INumericInputComponentProps, { value: string }> {
+
+    static defaultProps = {
+        step: 1,
+    };
+
+    private _localChange = false;
+    constructor(props: INumericInputComponentProps) {
+        super(props);
+
+        this.state = { value: this.props.value.toFixed(3) }
+    }
+
+    shouldComponentUpdate(nextProps: INumericInputComponentProps, nextState: { value: string }) {
+        if (this._localChange) {
+            this._localChange = false;
+            return true;
+        }
+
+        if (nextProps.value.toString() !== nextState.value) {
+            nextState.value = nextProps.value.toFixed(3);
+            return true;
+        }
+        return false;
+    }
+
+    updateValue(evt: any) {
+        let value = evt.target.value;
+
+        if (/[^0-9\.\-]/g.test(value)) {
+            return;
+        }
+
+        let valueAsNumber = parseFloat(value);
+
+        this._localChange = true;
+        this.setState({ value: value });
+
+        if (isNaN(valueAsNumber)) {
+            return;
+        }
+
+        this.props.onChange(valueAsNumber);
+    }
+
+
+    render() {
+        return (
+            <div className="numeric">
+                {
+                    this.props.label &&
+                    <div className="numeric-label">
+                        {`${this.props.label}: `}
+                    </div>
+                }
+                <input type="number" step={this.props.step} className="numeric-input" value={this.state.value} onChange={evt => this.updateValue(evt)} />
+            </div>
+        )
+    }
+}

+ 70 - 0
nodeEditor/src/sharedComponents/popup.ts

@@ -0,0 +1,70 @@
+export class Popup {
+    public static CreatePopup(title: string, windowVariableName: string, width = 300, height = 800) {
+        const windowCreationOptionsList = {
+            width: width,
+            height: height,
+            top: (window.innerHeight - width) / 2 + window.screenY,
+            left: (window.innerWidth - height) / 2 + window.screenX
+        };
+    
+        var windowCreationOptions = Object.keys(windowCreationOptionsList)
+            .map(
+                (key) => key + '=' + (windowCreationOptionsList as any)[key]
+            )
+            .join(',');
+    
+        const popupWindow = window.open("", title, windowCreationOptions);
+        if (!popupWindow) {
+            return null;
+        }
+    
+        const parentDocument = popupWindow.document;
+    
+        parentDocument.title = title;
+        parentDocument.body.style.width = "100%";
+        parentDocument.body.style.height = "100%";
+        parentDocument.body.style.margin = "0";
+        parentDocument.body.style.padding = "0";
+    
+        let parentControl = parentDocument.createElement("div");
+        parentControl.style.width = "100%";
+        parentControl.style.height = "100%";
+        parentControl.style.margin = "0";
+        parentControl.style.padding = "0";
+    
+        popupWindow.document.body.appendChild(parentControl);
+    
+        this._CopyStyles(window.document, parentDocument);
+    
+        (this as any)[windowVariableName] = popupWindow;
+    
+        return parentControl;
+    }
+
+    private static _CopyStyles(sourceDoc: HTMLDocument, targetDoc: HTMLDocument) {
+        for (var index = 0; index < sourceDoc.styleSheets.length; index++) {
+            var styleSheet: any = sourceDoc.styleSheets[index];
+            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){
+                console.log(e)
+            }
+            
+        }
+    }
+}

+ 6 - 0
nodeEditor/src/sharedComponents/propertyChangedEvent.ts

@@ -0,0 +1,6 @@
+export class PropertyChangedEvent {
+    public object: any;
+    public property: string;
+    public value: any;
+    public initialValue: any;
+}

+ 202 - 0
nodeEditor/src/sharedComponents/textureLineComponent.tsx

@@ -0,0 +1,202 @@
+import * as React from "react";
+
+import { Constants } from "babylonjs/Engines/constants";
+import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
+import { Texture } from "babylonjs/Materials/Textures/texture";
+import { RenderTargetTexture } from "babylonjs/Materials/Textures/renderTargetTexture";
+import { PostProcess } from "babylonjs/PostProcesses/postProcess";
+import { PassPostProcess, PassCubePostProcess } from "babylonjs/PostProcesses/passPostProcess";
+
+interface ITextureLineComponentProps {
+    texture: BaseTexture;
+    width: number;
+    height: number;
+    globalState?: any;
+    hideChannelSelect?:boolean;
+}
+
+export class TextureLineComponent extends React.Component<ITextureLineComponentProps, { displayRed: boolean, displayGreen: boolean, displayBlue: boolean, displayAlpha: boolean, face: number }> {
+    constructor(props: ITextureLineComponentProps) {
+        super(props);
+
+        this.state = {
+            displayRed: true,
+            displayGreen: true,
+            displayBlue: true,
+            displayAlpha: true,
+            face: 0
+        };
+    }
+
+    shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: { displayRed: boolean, displayGreen: boolean, displayBlue: boolean, displayAlpha: boolean, face: number }): boolean {
+        return (nextProps.texture !== this.props.texture || nextState.displayRed !== this.state.displayRed || nextState.displayGreen !== this.state.displayGreen || nextState.displayBlue !== this.state.displayBlue || nextState.displayAlpha !== this.state.displayAlpha || nextState.face !== this.state.face);
+    }
+
+    componentDidMount() {
+        this.updatePreview();
+    }
+
+    componentDidUpdate() {
+        this.updatePreview();
+    }
+
+    updatePreview() {
+        var texture = this.props.texture;
+        if(!texture.isReady() && texture._texture){
+            texture._texture.onLoadedObservable.addOnce(()=>{
+                this.updatePreview();
+            })
+        }
+        var scene = texture.getScene()!;
+        var engine = scene.getEngine();
+        var size = texture.getSize();
+        var ratio = size.width / size.height;
+        var width = this.props.width;
+        var height = (width / ratio) | 1;
+
+        let passPostProcess: PostProcess;
+
+        if (!texture.isCube) {
+            passPostProcess = new PassPostProcess("pass", 1, null, Texture.NEAREST_SAMPLINGMODE, engine, false, Constants.TEXTURETYPE_UNSIGNED_INT);
+        } else {
+            var passCubePostProcess = new PassCubePostProcess("pass", 1, null, Texture.NEAREST_SAMPLINGMODE, engine, false, Constants.TEXTURETYPE_UNSIGNED_INT);
+            passCubePostProcess.face = this.state.face;
+
+            passPostProcess = passCubePostProcess;
+        }
+
+        if (!passPostProcess.getEffect().isReady()) {
+            // Try again later
+            passPostProcess.dispose();
+
+            setTimeout(() => this.updatePreview(), 250);
+
+            return;
+        }
+
+        const previewCanvas = this.refs.canvas as HTMLCanvasElement;
+
+        if(this.props.globalState){
+            this.props.globalState.blockMutationUpdates = true;
+        }
+        
+        let rtt = new RenderTargetTexture(
+            "temp",
+            { width: width, height: height },
+            scene, false);
+
+        passPostProcess.onApply = function(effect) {
+            effect.setTexture("textureSampler", texture);
+        };
+
+        let internalTexture = rtt.getInternalTexture();
+
+        if (internalTexture) {
+            scene.postProcessManager.directRender([passPostProcess], internalTexture);
+
+            // Read the contents of the framebuffer
+            var numberOfChannelsByLine = width * 4;
+            var halfHeight = height / 2;
+
+            //Reading datas from WebGL
+            var data = engine.readPixels(0, 0, width, height);
+
+            if (!texture.isCube) {
+                if (!this.state.displayRed || !this.state.displayGreen || !this.state.displayBlue) {
+                    for (var i = 0; i < width * height * 4; i += 4) {
+
+                        if (!this.state.displayRed) {
+                            data[i] = 0;
+                        }
+
+                        if (!this.state.displayGreen) {
+                            data[i + 1] = 0;
+                        }
+
+                        if (!this.state.displayBlue) {
+                            data[i + 2] = 0;
+                        }
+
+                        if (this.state.displayAlpha) {
+                            var alpha = data[i + 2];
+                            data[i] = alpha;
+                            data[i + 1] = alpha;
+                            data[i + 2] = alpha;
+                            data[i + 2] = 0;
+                        }
+                    }
+                }
+            }
+
+            //To flip image on Y axis.
+            if ((texture as Texture).invertY || texture.isCube) {
+                for (var i = 0; i < halfHeight; i++) {
+                    for (var j = 0; j < numberOfChannelsByLine; j++) {
+                        var currentCell = j + i * numberOfChannelsByLine;
+                        var targetLine = height - i - 1;
+                        var targetCell = j + targetLine * numberOfChannelsByLine;
+
+                        var temp = data[currentCell];
+                        data[currentCell] = data[targetCell];
+                        data[targetCell] = temp;
+                    }
+                }
+            }
+
+            previewCanvas.width = width;
+            previewCanvas.height = height;
+            var context = previewCanvas.getContext('2d');
+
+            if (context) {
+                // Copy the pixels to the preview canvas
+                var imageData = context.createImageData(width, height);
+                var castData = imageData.data;
+                castData.set(data);
+                context.putImageData(imageData, 0, 0);
+            }
+
+            // Unbind
+            engine.unBindFramebuffer(internalTexture);
+        }
+
+        rtt.dispose();
+        passPostProcess.dispose();
+
+        previewCanvas.style.height = height + "px";
+        if(this.props.globalState){
+            this.props.globalState.blockMutationUpdates = false;
+        }
+        
+    }
+
+    render() {
+        var texture = this.props.texture;
+
+        return (
+            <div className="textureLine">
+                {
+                    !this.props.hideChannelSelect && texture.isCube &&
+                    <div className="control3D">
+                        <button className={this.state.face === 0 ? "px command selected" : "px command"} onClick={() => this.setState({ face: 0 })}>PX</button>
+                        <button className={this.state.face === 1 ? "nx command selected" : "nx command"} onClick={() => this.setState({ face: 1 })}>NX</button>
+                        <button className={this.state.face === 2 ? "py command selected" : "py command"} onClick={() => this.setState({ face: 2 })}>PY</button>
+                        <button className={this.state.face === 3 ? "ny command selected" : "ny command"} onClick={() => this.setState({ face: 3 })}>NY</button>
+                        <button className={this.state.face === 4 ? "pz command selected" : "pz command"} onClick={() => this.setState({ face: 4 })}>PZ</button>
+                        <button className={this.state.face === 5 ? "nz command selected" : "nz command"} onClick={() => this.setState({ face: 5 })}>NZ</button>
+                    </div>
+                }
+                {
+                    !this.props.hideChannelSelect && !texture.isCube &&
+                    <div className="control">
+                        <button className={this.state.displayRed && !this.state.displayGreen ? "red command selected" : "red command"} onClick={() => this.setState({ displayRed: true, displayGreen: false, displayBlue: false, displayAlpha: false })}>R</button>
+                        <button className={this.state.displayGreen && !this.state.displayBlue ? "green command selected" : "green command"} onClick={() => this.setState({ displayRed: false, displayGreen: true, displayBlue: false, displayAlpha: false })}>G</button>
+                        <button className={this.state.displayBlue && !this.state.displayAlpha ? "blue command selected" : "blue command"} onClick={() => this.setState({ displayRed: false, displayGreen: false, displayBlue: true, displayAlpha: false })}>B</button>
+                        <button className={this.state.displayAlpha && !this.state.displayRed ? "alpha command selected" : "alpha command"} onClick={() => this.setState({ displayRed: false, displayGreen: false, displayBlue: false, displayAlpha: true })}>A</button>
+                        <button className={this.state.displayRed && this.state.displayGreen ? "all command selected" : "all command"} onClick={() => this.setState({ displayRed: true, displayGreen: true, displayBlue: true, displayAlpha: true })}>ALL</button>
+                    </div>
+                }
+                <canvas ref="canvas" className="preview" />
+            </div>
+        );
+    }
+}

+ 114 - 0
nodeEditor/src/sharedComponents/vector2LineComponent.tsx

@@ -0,0 +1,114 @@
+import * as React from "react";
+import { Vector2 } from "babylonjs/Maths/math";
+import { Observable } from "babylonjs/Misc/observable";
+
+import { NumericInputComponent } from "./numericInputComponent";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
+import { PropertyChangedEvent } from "./propertyChangedEvent";
+
+interface IVector2LineComponentProps {
+    label: string;
+    target: any;
+    propertyName: string;
+    step?: number;
+    onChange?: (newvalue: Vector2) => void;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+}
+
+export class Vector2LineComponent extends React.Component<IVector2LineComponentProps, { isExpanded: boolean, value: Vector2 }> {
+
+    static defaultProps = {
+        step: 0.001, // cm
+    };
+
+    private _localChange = false;
+
+    constructor(props: IVector2LineComponentProps) {
+        super(props);
+
+        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName].clone() }
+    }
+
+    shouldComponentUpdate(nextProps: IVector2LineComponentProps, nextState: { isExpanded: boolean, value: Vector2 }) {
+        const nextPropsValue = nextProps.target[nextProps.propertyName];
+
+        if (!nextPropsValue.equals(nextState.value) || this._localChange) {
+            nextState.value = nextPropsValue.clone();
+            this._localChange = false;
+            return true;
+        }
+        return false;
+    }
+
+    switchExpandState() {
+        this._localChange = true;
+        this.setState({ isExpanded: !this.state.isExpanded });
+    }
+
+    raiseOnPropertyChanged(previousValue: Vector2) {
+        if (this.props.onChange) {
+            this.props.onChange(this.state.value);
+        }
+
+        if (!this.props.onPropertyChangedObservable) {
+            return;
+        }
+        this.props.onPropertyChangedObservable.notifyObservers({
+            object: this.props.target,
+            property: this.props.propertyName,
+            value: this.state.value,
+            initialValue: previousValue
+        });
+    }
+
+    updateStateX(value: number) {
+        this._localChange = true;
+
+        const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].x = value;
+        this.state.value.x = value;
+        this.setState({ value: this.state.value });
+
+        this.raiseOnPropertyChanged(store);
+    }
+
+    updateStateY(value: number) {
+        this._localChange = true;
+
+        const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].y = value;
+        this.state.value.y = value;
+        this.setState({ value: this.state.value });
+
+        this.raiseOnPropertyChanged(store);
+    }
+
+    render() {
+        const chevron = this.state.isExpanded ? <FontAwesomeIcon icon={faMinus} /> : <FontAwesomeIcon icon={faPlus} />
+
+        return (
+            <div className="vector3Line">
+                <div className="firstLine">
+                    <div className="label">
+                        {this.props.label}
+                    </div>
+                    <div className="vector">
+                        {`X: ${this.state.value.x.toFixed(2)}, Y: ${this.state.value.y.toFixed(2)}`}
+
+                    </div>
+                    <div className="expand hoverIcon" onClick={() => this.switchExpandState()} title="Expand">
+                        {chevron}
+                    </div>
+                </div>
+                {
+                    this.state.isExpanded &&
+                    <div className="secondLine">
+                        <NumericInputComponent label="x" step={this.props.step} value={this.state.value.x} onChange={value => this.updateStateX(value)} />
+                        <NumericInputComponent label="y" step={this.props.step} value={this.state.value.y} onChange={value => this.updateStateY(value)} />
+                    </div>
+                }
+            </div>
+        );
+    }
+}

+ 124 - 0
nodeEditor/src/sharedComponents/vector3LineComponent.tsx

@@ -0,0 +1,124 @@
+import * as React from "react";
+import { Vector3 } from "babylonjs/Maths/math";
+import { Observable } from "babylonjs/Misc/observable";
+
+import { NumericInputComponent } from "./numericInputComponent";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
+import { PropertyChangedEvent } from "./propertyChangedEvent";
+
+interface IVector3LineComponentProps {
+    label: string;
+    target: any;
+    propertyName: string;
+    step?: number;
+    onChange?: (newvalue: Vector3) => void;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+}
+
+export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, { isExpanded: boolean, value: Vector3 }> {
+
+    static defaultProps = {
+        step: 0.001, // cm
+    };
+
+    private _localChange = false;
+
+    constructor(props: IVector3LineComponentProps) {
+        super(props);
+
+        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName].clone() }
+    }
+
+    shouldComponentUpdate(nextProps: IVector3LineComponentProps, nextState: { isExpanded: boolean, value: Vector3 }) {
+        const nextPropsValue = nextProps.target[nextProps.propertyName];
+
+        if (!nextPropsValue.equals(nextState.value) || this._localChange) {
+            nextState.value = nextPropsValue.clone();
+            this._localChange = false;
+            return true;
+        }
+        return false;
+    }
+
+    switchExpandState() {
+        this._localChange = true;
+        this.setState({ isExpanded: !this.state.isExpanded });
+    }
+
+    raiseOnPropertyChanged(previousValue: Vector3) {
+        if (this.props.onChange) {
+            this.props.onChange(this.state.value);
+        }
+
+        if (!this.props.onPropertyChangedObservable) {
+            return;
+        }
+        this.props.onPropertyChangedObservable.notifyObservers({
+            object: this.props.target,
+            property: this.props.propertyName,
+            value: this.state.value,
+            initialValue: previousValue
+        });
+    }
+
+    updateVector3() {
+        const store = this.props.target[this.props.propertyName].clone();
+        this.props.target[this.props.propertyName] = this.state.value;
+
+        this.setState({ value: store });
+
+        this.raiseOnPropertyChanged(store);
+    }
+
+
+    updateStateX(value: number) {
+        this._localChange = true;
+
+        this.state.value.x = value;
+        this.updateVector3();
+    }
+
+    updateStateY(value: number) {
+        this._localChange = true;
+
+        this.state.value.y = value;
+        this.updateVector3();
+    }
+
+    updateStateZ(value: number) {
+        this._localChange = true;
+
+        this.state.value.z = value;
+        this.updateVector3();
+    }
+
+    render() {
+        const chevron = this.state.isExpanded ? <FontAwesomeIcon icon={faMinus} /> : <FontAwesomeIcon icon={faPlus} />
+
+        return (
+            <div className="vector3Line">
+                <div className="firstLine">
+                    <div className="label">
+                        {this.props.label}
+                    </div>
+                    <div className="vector">
+                        {`X: ${this.state.value.x.toFixed(2)}, Y: ${this.state.value.y.toFixed(2)}, Z: ${this.state.value.z.toFixed(2)}`}
+
+                    </div>
+                    <div className="expand hoverIcon" onClick={() => this.switchExpandState()} title="Expand">
+                        {chevron}
+                    </div>
+                </div>
+                {
+                    this.state.isExpanded &&
+                    <div className="secondLine">
+                        <NumericInputComponent label="x" step={this.props.step} value={this.state.value.x} onChange={value => this.updateStateX(value)} />
+                        <NumericInputComponent label="y" step={this.props.step} value={this.state.value.y} onChange={value => this.updateStateY(value)} />
+                        <NumericInputComponent label="z" step={this.props.step} value={this.state.value.z} onChange={value => this.updateStateZ(value)} />
+                    </div>
+                }
+            </div>
+        );
+    }
+}

+ 28 - 0
nodeEditor/tsconfig.json

@@ -0,0 +1,28 @@
+{
+    "extends": "../tsconfigRules",
+    "compilerOptions": {
+        "jsx": "react",
+        "baseUrl": "./src/",
+        "rootDir": "./src/",
+        "paths": {
+            "babylonjs-gui/*": [
+                "../../dist/preview release/gui/babylon.gui.module.d.ts"
+            ],
+            "babylonjs-gltf2interface/*": [
+                "../../dist/preview release/glTF2Interface/babylon.glTF2Interface.d.ts"
+            ],
+            "babylonjs-loaders/*": [
+                "../../dist/preview release/loaders/babylonjs.loaders.module.d.ts"
+            ],
+            "babylonjs-serializers/*": [
+                "../../dist/preview release/serializers/babylonjs.serializers.module.d.ts"
+            ],
+            "babylonjs/*": [
+                "../../dist/preview release/babylon.module.d.ts"
+            ]
+        }
+    },
+    "exclude": [
+        "../../inspector"
+    ]
+}

+ 34 - 0
nodeEditor/webpack.config.js

@@ -0,0 +1,34 @@
+const path = require("path");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const babylonWebpackConfig = require('../Tools/WebpackPlugins/babylonWebpackConfig');
+
+var config = babylonWebpackConfig({
+    module: "nodeEditor",
+    resolve: {
+        extensions: [".js", '.ts', ".tsx"],
+    },
+    moduleRules: [
+        {
+            test: /\.scss$/,
+            use: [
+                // fallback to style-loader in development
+                process.env.NODE_ENV !== 'production' ? 'style-loader' : MiniCssExtractPlugin.loader,
+                "css-loader",
+                "sass-loader"
+            ]
+        },
+        {
+            test: /\.css$/,
+            use: ['style-loader', 'css-loader']
+        }],
+    plugins: [
+        new MiniCssExtractPlugin({
+            // Options similar to the same options in webpackOptions.output
+            // both options are optional
+            filename: "[name].css",
+            chunkFilename: "[id].css"
+        })
+    ]
+});
+
+module.exports = config;

+ 3 - 2
package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.1.0-alpha.0",
+    "version": "4.1.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -37,6 +37,7 @@
     "readmeFilename": "README.md",
     "dependencies": {},
     "devDependencies": {
+        "storm-react-diagrams": "^5.2.1",
         "@fortawesome/fontawesome-svg-core": "~1.2.8",
         "@fortawesome/free-regular-svg-icons": "~5.4.1",
         "@fortawesome/free-solid-svg-icons": "~5.4.1",
@@ -105,4 +106,4 @@
         "xhr2": "^0.1.4",
         "xmlbuilder": "8.2.2"
     }
-}
+}

+ 2 - 2
src/Engines/engine.ts

@@ -491,14 +491,14 @@ export class Engine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.1.0-alpha.0";
+        return "babylonjs@4.1.0-alpha.1";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.1.0-alpha.0";
+        return "4.1.0-alpha.1";
     }
 
     /**

+ 2 - 2
src/Loading/sceneLoader.ts

@@ -584,7 +584,7 @@ export class SceneLoader {
                 onProgress(event);
             }
             catch (e) {
-                errorHandler("Error in onProgress callback", e);
+                errorHandler("Error in onProgress callback: " + e, e);
             }
         } : undefined;
 
@@ -596,7 +596,7 @@ export class SceneLoader {
                     onSuccess(meshes, particleSystems, skeletons, animationGroups);
                 }
                 catch (e) {
-                    errorHandler("Error in onSuccess callback", e);
+                    errorHandler("Error in onSuccess callback: " + e, e);
                 }
             }
 

+ 143 - 0
src/Materials/Node/Blocks/Dual/fogBlock.ts

@@ -0,0 +1,143 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { Mesh } from '../../../../Meshes/mesh';
+import { Effect } from '../../../effect';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { MaterialHelper } from '../../../materialHelper';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+
+/**
+ * Block used to add support for scene fog
+ */
+export class FogBlock extends NodeMaterialBlock {
+    /**
+     * Create a new FogBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.VertexAndFragment, true);
+
+        // Vertex
+        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);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "FogBlock";
+    }
+
+    /**
+     * Gets the world position input component
+     */
+    public get worldPosition(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the view input component
+     */
+    public get view(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the color input component
+     */
+    public get color(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the fog color input component
+     */
+    public get fogColor(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+     * Gets the for parameter input component
+     */
+    public get fogParameters(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    public autoConfigure() {
+        if (!this.view.connectedPoint) {
+            this.view.setAsWellKnownValue(NodeMaterialWellKnownValues.View);
+        }
+        if (!this.fogColor.connectedPoint) {
+            this.fogColor.setAsWellKnownValue(NodeMaterialWellKnownValues.BlockBased);
+        }
+        if (!this.fogParameters.connectedPoint) {
+            this.fogParameters.setAsWellKnownValue(NodeMaterialWellKnownValues.BlockBased);
+        }
+        this._outputs[0].isVarying = true;
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        let scene = mesh.getScene();
+        defines.setValue("FOG", nodeMaterial.fogEnabled && MaterialHelper.GetFogState(mesh, scene));
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
+        if (!mesh) {
+            return;
+        }
+
+        const scene = mesh.getScene();
+        effect.setColor3("fogColor", scene.fogColor);
+        effect.setFloat4("fogParameters", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        state.sharedData.blocksWithDefines.push(this);
+
+        if (state.target === NodeMaterialBlockTargets.Fragment) {
+            state._emitFunctionFromInclude("fogFragmentDeclaration", `//${this.name}`, {
+                removeUniforms: true,
+                removeVaryings: true,
+                removeIfDef: false,
+                replaceStrings: [{ search: /float CalcFogFactor\(\)/, replace: "float CalcFogFactor(vec3 vFogDistance, vec4 vFogInfos)" }]
+            });
+
+            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];
+
+            state.compilationString += `#ifdef FOG\r\n`;
+            state.compilationString += `float ${tempFogVariablename} = CalcFogFactor(${vFogDistance.associatedVariableName}, ${fogParameters.associatedVariableName});\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`;
+        }
+
+        return this;
+    }
+}

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

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

+ 51 - 0
src/Materials/Node/Blocks/Fragment/alphaTestBlock.ts

@@ -0,0 +1,51 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+
+/**
+ * Block used to add an alpha test in the fragment shader
+ */
+export class AlphaTestBlock extends NodeMaterialBlock {
+
+    /**
+     * Gets or sets the alpha value where alpha testing happens
+     */
+    public alphaCutOff = 0.4;
+
+    /**
+     * Create a new AlphaTestBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color4);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "AlphaTestBlock";
+    }
+
+    /**
+     * Gets the color input component
+     */
+    public get color(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        state.sharedData.hints.needAlphaTesting = true;
+
+        state.compilationString += `if (${this.color.associatedVariableName}.a < ${this.alphaCutOff}) discard;\r\n`;
+
+        return this;
+    }
+}

+ 54 - 0
src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts

@@ -0,0 +1,54 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+
+/**
+ * Block used to output the final color
+ */
+export class FragmentOutputBlock extends NodeMaterialBlock {
+    /**
+     * Gets or sets a boolean indicating if this block will output an alpha value
+     */
+    public alphaBlendingEnabled = false;
+    /**
+     * Create a new FragmentOutputBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment, true);
+
+        this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color3OrColor4);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "FragmentOutputBlock";
+    }
+
+    /**
+     * Gets the color input component
+     */
+    public get color(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        let input = this.color;
+        state.sharedData.hints.needAlphaBlending = this.alphaBlendingEnabled;
+
+        if (input.connectedPoint!.type === NodeMaterialBlockConnectionPointTypes.Color3) {
+            state.compilationString += `gl_FragColor = vec4(${input.associatedVariableName}, 1.0);\r\n`;
+        } else {
+            state.compilationString += `gl_FragColor = ${input.associatedVariableName};\r\n`;
+        }
+
+        return this;
+    }
+}

+ 134 - 0
src/Materials/Node/Blocks/Fragment/imageProcessingBlock.ts

@@ -0,0 +1,134 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { Effect } from '../../../effect';
+import { Mesh } from '../../../../Meshes/mesh';
+
+/**
+ * Block used to add image processing support to fragment shader
+ */
+export class ImageProcessingBlock extends NodeMaterialBlock {
+    /**
+     * Create a new ImageProcessingBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color3OrColor4);
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color4);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "ImageProcessingBlock";
+    }
+
+    /**
+     * Gets the color input component
+     */
+    public get color(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Initialize the block and prepare the context for build
+     * @param state defines the state that will be used for the build
+     */
+    public initialize(state: NodeMaterialBuildState) {
+        state._excludeVariableName("exposureLinear");
+        state._excludeVariableName("contrast");
+        state._excludeVariableName("vInverseScreenSize");
+        state._excludeVariableName("vignetteSettings1");
+        state._excludeVariableName("vignetteSettings2");
+        state._excludeVariableName("vCameraColorCurveNegative");
+        state._excludeVariableName("vCameraColorCurveNeutral");
+        state._excludeVariableName("vCameraColorCurvePositive");
+        state._excludeVariableName("txColorTransform");
+        state._excludeVariableName("colorTransformSettings");
+    }
+
+    public isReady(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (defines._areImageProcessingDirty && nodeMaterial.imageProcessingConfiguration) {
+            if (!nodeMaterial.imageProcessingConfiguration.isReady()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (defines._areImageProcessingDirty && nodeMaterial.imageProcessingConfiguration) {
+            nodeMaterial.imageProcessingConfiguration.prepareDefines(defines);
+        }
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
+        if (!mesh) {
+            return;
+        }
+
+        if (!nodeMaterial.imageProcessingConfiguration) {
+            return;
+        }
+
+        nodeMaterial.imageProcessingConfiguration.bind(effect);
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        // Register for defines
+        state.sharedData.blocksWithDefines.push(this);
+
+        // Register for blocking
+        state.sharedData.blockingBlocks.push(this);
+
+        // Register for binding
+        state.sharedData.bindableBlocks.push(this);
+
+        // Uniforms
+        state.uniforms.push("exposureLinear");
+        state.uniforms.push("contrast");
+        state.uniforms.push("vInverseScreenSize");
+        state.uniforms.push("vignetteSettings1");
+        state.uniforms.push("vignetteSettings2");
+        state.uniforms.push("vCameraColorCurveNegative");
+        state.uniforms.push("vCameraColorCurveNeutral");
+        state.uniforms.push("vCameraColorCurvePositive");
+        state.uniforms.push("txColorTransform");
+        state.uniforms.push("colorTransformSettings");
+
+        // Emit code
+        let color = this.color;
+        let output = this._outputs[0];
+        let comments = `//${this.name}`;
+
+        state._emitFunctionFromInclude("helperFunctions", comments);
+        state._emitFunctionFromInclude("imageProcessingDeclaration", comments);
+        state._emitFunctionFromInclude("imageProcessingFunctions", comments);
+
+        if (color.connectedPoint!.type === NodeMaterialBlockConnectionPointTypes.Color4) {
+            state.compilationString += `${this._declareOutput(output, state)} = ${color.associatedVariableName};\r\n`;
+        } else {
+            state.compilationString += `${this._declareOutput(output, state)} = vec4(${color.associatedVariableName}, 1.0);\r\n`;
+        }
+        state.compilationString += `#ifdef IMAGEPROCESSINGPOSTPROCESS\r\n`;
+        state.compilationString += `${output.associatedVariableName}.rgb = toLinearSpace(${color.associatedVariableName}.rgb);\r\n`;
+        state.compilationString += `#else\r\n`;
+        state.compilationString += `#ifdef IMAGEPROCESSING\r\n`;
+        state.compilationString += `${output.associatedVariableName}.rgb = toLinearSpace(${color.associatedVariableName}.rgb);\r\n`;
+        state.compilationString += `${output.associatedVariableName} = applyImageProcessing(${output.associatedVariableName});\r\n`;
+        state.compilationString += `#endif\r\n`;
+        state.compilationString += `#endif\r\n`;
+
+        return this;
+    }
+}

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

@@ -0,0 +1,9 @@
+
+export * from "./fragmentOutputBlock";
+export * from "./alphaTestBlock";
+export * from "./rgbaMergerBlock";
+export * from "./rgbMergerBlock";
+export * from "./rgbaSplitterBlock";
+export * from "./rgbSplitterBlock";
+export * from "./textureBlock";
+export * from "./imageProcessingBlock";

+ 67 - 0
src/Materials/Node/Blocks/Fragment/rgbMergerBlock.ts

@@ -0,0 +1,67 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+
+/**
+ * Block used to create a Color3 out of 3 inputs (one for each component)
+ */
+export class RGBMergerBlock extends NodeMaterialBlock {
+    /**
+     * Create a new RGBMergerBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("r", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerInput("g", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerInput("b", NodeMaterialBlockConnectionPointTypes.Float);
+
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color3);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBMergerBlock";
+    }
+
+    /**
+     * Gets the R component input
+     */
+    public get r(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the G component input
+     */
+    public get g(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the B component input
+     */
+    public get b(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        let rInput = this.r;
+        let gInput = this.g;
+        let bInput = this.b;
+
+        let output = this._outputs[0];
+
+        state.compilationString += this._declareOutput(output, state) + ` = vec3(${this._writeVariable(rInput)}, ${this._writeVariable(gInput)}, ${this._writeVariable(bInput)});\r\n`;
+
+        return this;
+    }
+}

+ 59 - 0
src/Materials/Node/Blocks/Fragment/rgbSplitterBlock.ts

@@ -0,0 +1,59 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+
+/**
+ * Block used to expand a Color3 or a Vector3 into 3 outputs (one for each component)
+ */
+export class RGBSplitterBlock extends NodeMaterialBlock {
+
+    /**
+     * Create a new RGBSplitterBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("input", NodeMaterialBlockConnectionPointTypes.Vector3OrColor3);
+        this.registerOutput("r", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerOutput("g", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerOutput("b", NodeMaterialBlockConnectionPointTypes.Float);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBSplitterBlock";
+    }
+
+    /**
+     * Gets the input component
+     */
+    public get input(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        let input = this.input;
+        let rOutput = this._outputs[0];
+        let gOutput = this._outputs[1];
+        let bOutput = this._outputs[2];
+
+        if (rOutput.connectedBlocks.length > 0) {
+            state.compilationString += this._declareOutput(rOutput, state) + ` = ${input.associatedVariableName}.r;\r\n`;
+        }
+        if (gOutput.connectedBlocks.length > 0) {
+            state.compilationString += this._declareOutput(gOutput, state) + ` = ${input.associatedVariableName}.g;\r\n`;
+        }
+        if (bOutput.connectedBlocks.length > 0) {
+            state.compilationString += this._declareOutput(bOutput, state) + ` = ${input.associatedVariableName}.b;\r\n`;
+        }
+        return this;
+    }
+}

+ 88 - 0
src/Materials/Node/Blocks/Fragment/rgbaMergerBlock.ts

@@ -0,0 +1,88 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+
+/**
+ * Block used to create a Color4 out of 4 inputs (one for each component)
+ */
+export class RGBAMergerBlock extends NodeMaterialBlock {
+    /**
+     * Create a new RGBAMergerBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("r", NodeMaterialBlockConnectionPointTypes.Float, true);
+        this.registerInput("g", NodeMaterialBlockConnectionPointTypes.Float, true);
+        this.registerInput("b", NodeMaterialBlockConnectionPointTypes.Float, true);
+        this.registerInput("rgb", NodeMaterialBlockConnectionPointTypes.Vector3OrColor3OrVector4OrColor4, true);
+        this.registerInput("a", NodeMaterialBlockConnectionPointTypes.Float, true);
+
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color4);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBAMergerBlock";
+    }
+
+    /**
+     * Gets the R input component
+     */
+    public get r(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the G input component
+     */
+    public get g(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the B input component
+     */
+    public get b(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the RGB input component
+     */
+    public get rgb(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+     * Gets the R input component
+     */
+    public get a(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        let rgbInput = this.rgb;
+        let aInput = this.a;
+        let output = this._outputs[0];
+
+        if (rgbInput.connectedPoint) {
+            state.compilationString += this._declareOutput(output, state) + ` = vec4(${rgbInput.associatedVariableName}.rgb, ${this._writeVariable(aInput)});\r\n`;
+        } else {
+            let rInput = this._inputs[0];
+            let gInput = this._inputs[1];
+            let bInput = this._inputs[2];
+            state.compilationString += this._declareOutput(output, state) + ` = vec4(${this._writeVariable(rInput)}, ${this._writeVariable(gInput)}, ${this._writeVariable(bInput)}, ${this._writeVariable(aInput)});\r\n`;
+        }
+
+        return this;
+    }
+}

+ 65 - 0
src/Materials/Node/Blocks/Fragment/rgbaSplitterBlock.ts

@@ -0,0 +1,65 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+
+/**
+ * Block used to expand a Color4 or a Vector4 into 4 outputs (one for each component)
+ */
+export class RGBASplitterBlock extends NodeMaterialBlock {
+
+    /**
+     * Create a new RGBASplitterBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("input", NodeMaterialBlockConnectionPointTypes.Vector4OrColor4);
+        this.registerOutput("r", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerOutput("g", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerOutput("b", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerOutput("a", NodeMaterialBlockConnectionPointTypes.Float);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBASplitterBlock";
+    }
+
+    /**
+     * Gets the input component
+     */
+    public get input(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        let input = this.input;
+        let rOutput = this._outputs[0];
+        let gOutput = this._outputs[1];
+        let bOutput = this._outputs[2];
+        let aOutput = this._outputs[3];
+
+        if (rOutput.connectedBlocks.length > 0) {
+            state.compilationString += this._declareOutput(rOutput, state) + ` = ${input.associatedVariableName}.r;\r\n`;
+        }
+        if (gOutput.connectedBlocks.length > 0) {
+            state.compilationString += this._declareOutput(gOutput, state) + ` = ${input.associatedVariableName}.g;\r\n`;
+        }
+        if (bOutput.connectedBlocks.length > 0) {
+            state.compilationString += this._declareOutput(bOutput, state) + ` = ${input.associatedVariableName}.b;\r\n`;
+        }
+        if (aOutput.connectedBlocks.length > 0) {
+            state.compilationString += this._declareOutput(aOutput, state) + ` = ${input.associatedVariableName}.a;\r\n`;
+        }
+
+        return this;
+    }
+}

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

@@ -0,0 +1,194 @@
+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);
+    }
+
+    /**
+     * 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];
+    }
+
+    public autoConfigure() {
+        if (!this.uv.connectedPoint) {
+            this.uv.setAsAttribute();
+            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;
+    }
+}

+ 170 - 0
src/Materials/Node/Blocks/Vertex/bonesBlock.ts

@@ -0,0 +1,170 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { Mesh } from '../../../../Meshes/mesh';
+import { Effect, EffectFallbacks } from '../../../effect';
+import { MaterialHelper } from '../../../materialHelper';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+
+/**
+ * Block used to add support for vertex skinning (bones)
+ */
+export class BonesBlock extends NodeMaterialBlock {
+    /**
+     * Creates a new BonesBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Vertex);
+
+        this.registerInput("matricesIndices", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("matricesWeights", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("matricesIndicesExtra", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("matricesWeightsExtra", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("world", NodeMaterialBlockConnectionPointTypes.Matrix);
+
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Matrix);
+    }
+
+    /**
+     * Initialize the block and prepare the context for build
+     * @param state defines the state that will be used for the build
+     */
+    public initialize(state: NodeMaterialBuildState) {
+        state._excludeVariableName("boneSampler");
+        state._excludeVariableName("boneTextureWidth");
+        state._excludeVariableName("mBones");
+        state._excludeVariableName("BonesPerMesh");
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "BonesBlock";
+    }
+
+    /**
+     * Gets the matrix indices input component
+     */
+    public get matricesIndices(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the matrix weights input component
+     */
+    public get matricesWeights(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the extra matrix indices input component
+     */
+    public get matricesIndicesExtra(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the extra matrix weights input component
+     */
+    public get matricesWeightsExtra(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+     * Gets the world input component
+     */
+    public get world(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    public autoConfigure() {
+        if (!this.matricesIndices.connectedPoint) {
+            this.matricesIndices.setAsAttribute();
+        }
+        if (!this.matricesWeights.connectedPoint) {
+            this.matricesWeights.setAsAttribute();
+        }
+        if (!this.matricesIndicesExtra.connectedPoint) {
+            this.matricesIndicesExtra.setAsAttribute();
+        }
+        if (!this.matricesWeightsExtra.connectedPoint) {
+            this.matricesWeightsExtra.setAsAttribute();
+        }
+        if (!this.world.connectedPoint) {
+            this.world.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+        }
+    }
+
+    public provideFallbacks(mesh: AbstractMesh, fallbacks: EffectFallbacks) {
+        if (mesh && mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
+            fallbacks.addCPUSkinningFallback(0, mesh);
+        }
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
+        MaterialHelper.BindBonesParameters(mesh, effect);
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (!defines._areAttributesDirty) {
+            return;
+        }
+        MaterialHelper.PrepareDefinesForBones(mesh, defines);
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        // Register for compilation fallbacks
+        state.sharedData.blocksWithFallbacks.push(this);
+
+        // Register for binding
+        state.sharedData.bindableBlocks.push(this);
+
+        // Register for defines
+        state.sharedData.blocksWithDefines.push(this);
+
+        // Register internal uniforms and samplers
+        state.uniforms.push("boneTextureWidth");
+        state.uniforms.push("mBones");
+
+        state.samplers.push("boneSampler");
+
+        // Emit code
+        let comments = `//${this.name}`;
+        state._emitFunctionFromInclude("bonesDeclaration", comments, {
+            removeAttributes: true,
+            removeUniforms: false,
+            removeVaryings: true,
+            removeIfDef: false
+        });
+
+        let influenceVariablename = state._getFreeVariableName("influence");
+
+        state.compilationString += state._emitCodeFromInclude("bonesVertex", comments, {
+            replaceStrings: [
+                {
+                    search: /finalWorld=finalWorld\*influence;/,
+                    replace: ""
+                },
+                {
+                    search: /influence/gm,
+                    replace: influenceVariablename
+                }
+            ]
+        });
+
+        let output = this._outputs[0];
+        let worldInput = this.world;
+
+        state.compilationString += this._declareOutput(output, state) + ` = ${worldInput.associatedVariableName} * ${influenceVariablename};`;
+        return this;
+    }
+}

+ 4 - 0
src/Materials/Node/Blocks/Vertex/index.ts

@@ -0,0 +1,4 @@
+export * from "./vertexOutputBlock";
+export * from "./bonesBlock";
+export * from "./instancesBlock";
+export * from "./morphTargetsBlock";

+ 133 - 0
src/Materials/Node/Blocks/Vertex/instancesBlock.ts

@@ -0,0 +1,133 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+
+/**
+ * Block used to add support for instances
+ * @see https://doc.babylonjs.com/how_to/how_to_use_instances
+ */
+export class InstancesBlock extends NodeMaterialBlock {
+    /**
+     * Creates a new InstancesBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Vertex);
+
+        this.registerInput("world0", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("world1", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("world2", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("world3", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("world", NodeMaterialBlockConnectionPointTypes.Matrix, true);
+
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Matrix);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "InstancesBlock";
+    }
+
+    /**
+     * Gets the first world row input component
+     */
+    public get world0(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the second world row input component
+     */
+    public get world1(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the third world row input component
+     */
+    public get world2(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the forth world row input component
+     */
+    public get world3(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+     * Gets the world input component
+     */
+    public get world(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    /**
+     * Gets the output component
+     */
+    public get output(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    public autoConfigure() {
+        if (!this.world0.connectedPoint) {
+            this.world0.setAsAttribute();
+        }
+        if (!this.world1.connectedPoint) {
+            this.world1.setAsAttribute();
+        }
+        if (!this.world2.connectedPoint) {
+            this.world2.setAsAttribute();
+        }
+        if (!this.world3.connectedPoint) {
+            this.world3.setAsAttribute();
+        }
+        if (!this.world.connectedPoint) {
+            this.world.setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+        }
+
+        this.world.define = "!INSTANCES";
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines, useInstances: boolean = false) {
+        let changed = false;
+        if (defines["INSTANCES"] !== useInstances) {
+            defines.setValue("INSTANCES", useInstances);
+            changed = true;
+        }
+
+        if (changed) {
+            defines.markAsUnprocessed();
+        }
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        // Register for defines
+        state.sharedData.blocksWithDefines.push(this);
+
+        // Emit code
+        let output = this._outputs[0];
+        let world0 = this.world0;
+        let world1 = this.world1;
+        let world2 = this.world2;
+        let world3 = this.world3;
+
+        state.compilationString += `#ifdef INSTANCES\r\n`;
+        state.compilationString += this._declareOutput(output, state) + ` = mat4(${world0.associatedVariableName}, ${world1.associatedVariableName}, ${world2.associatedVariableName}, ${world3.associatedVariableName});\r\n`;
+        state.compilationString += `#else\r\n`;
+        state.compilationString += this._declareOutput(output, state) + ` = ${this.world.associatedVariableName};\r\n`;
+        state.compilationString += `#endif\r\n`;
+        return this;
+    }
+}

+ 211 - 0
src/Materials/Node/Blocks/Vertex/morphTargetsBlock.ts

@@ -0,0 +1,211 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { Effect } from '../../../effect';
+import { Mesh } from '../../../../Meshes/mesh';
+import { MaterialHelper } from '../../../materialHelper';
+import { VertexBuffer } from '../../../../Meshes/buffer';
+
+/**
+ * Block used to add morph targets support to vertex shader
+ */
+export class MorphTargetsBlock extends NodeMaterialBlock {
+    private _repeatableContentAnchor: string;
+    private _repeatebleContentGenerated = 0;
+
+    /**
+     * Create a new MorphTargetsBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Vertex);
+
+        this.registerInput("position", NodeMaterialBlockConnectionPointTypes.Vector3);
+        this.registerInput("normal", NodeMaterialBlockConnectionPointTypes.Vector3);
+        this.registerInput("tangent", NodeMaterialBlockConnectionPointTypes.Vector3);
+        this.registerOutput("positionOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
+        this.registerOutput("normalOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
+        this.registerOutput("tangentOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "MorphTargetsBlock";
+    }
+
+    /**
+     * Gets the position input component
+     */
+    public get position(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the normal input component
+     */
+    public get normal(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the tangent input component
+     */
+    public get tangent(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the position output component
+     */
+    public get positionOutput(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    /**
+     * Gets the normal output component
+     */
+    public get normalOutput(): NodeMaterialConnectionPoint {
+        return this._outputs[1];
+    }
+
+    /**
+     * Gets the tangent output component
+     */
+    public get tangentOutput(): NodeMaterialConnectionPoint {
+        return this._outputs[2];
+    }
+
+    public initialize(state: NodeMaterialBuildState) {
+        state._excludeVariableName("morphTargetInfluences");
+    }
+
+    public autoConfigure() {
+        if (!this.position.connectedPoint) {
+            this.position.setAsAttribute();
+        }
+        if (!this.normal.connectedPoint) {
+            this.normal.setAsAttribute();
+            this.normal.define = "NORMAL";
+        }
+        if (!this.tangent.connectedPoint) {
+            this.tangent.setAsAttribute();
+            this.tangent.define = "TANGENT";
+        }
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (!defines._areAttributesDirty) {
+            return;
+        }
+        MaterialHelper.PrepareDefinesForMorphTargets(mesh, defines);
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
+        if (mesh && this._repeatebleContentGenerated) {
+            MaterialHelper.BindMorphTargetParameters(mesh, effect);
+        }
+    }
+
+    public replaceRepeatableContent(vertexShaderState: NodeMaterialBuildState, fragmentShaderState: NodeMaterialBuildState, mesh: AbstractMesh, defines: NodeMaterialDefines) {
+        let position = this.position;
+        let normal = this.normal;
+        let tangent = this.tangent;
+        let positionOutput = this.positionOutput;
+        let normalOutput = this.normalOutput;
+        let tangentOutput = this.tangentOutput;
+        let state = vertexShaderState;
+        let repeatCount = defines.NUM_MORPH_INFLUENCERS as number;
+        this._repeatebleContentGenerated = repeatCount;
+
+        var manager = (<Mesh>mesh).morphTargetManager;
+        var hasNormals = manager && manager.supportsNormals && defines["NORMAL"];
+        var hasTangents = manager && manager.supportsTangents && defines["TANGENT"];
+
+        let injectionCode = "";
+
+        for (var index = 0; index < repeatCount; index++) {
+            injectionCode += `#ifdef MORPHTARGETS\r\n`;
+            injectionCode += `${positionOutput.associatedVariableName} += (position${index} - ${position.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+
+            if (hasNormals) {
+                injectionCode += `#ifdef MORPHTARGETS_NORMAL\r\n`;
+                injectionCode += `${normalOutput.associatedVariableName} += (normal${index} - ${normal.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+                injectionCode += `#endif\r\n`;
+            }
+
+            if (hasTangents) {
+                injectionCode += `#ifdef MORPHTARGETS_TANGENT\r\n`;
+                injectionCode += `${tangentOutput.associatedVariableName}.xyz += (tangent${index} - ${tangent.associatedVariableName}.xyz) * morphTargetInfluences[${index}];\r\n`;
+                injectionCode += `#endif\r\n`;
+            }
+
+            injectionCode += `#endif\r\n`;
+        }
+
+        state.compilationString = state.compilationString.replace(this._repeatableContentAnchor, injectionCode);
+
+        if (repeatCount > 0) {
+            for (var index = 0; index < repeatCount; index++) {
+                state.attributes.push(VertexBuffer.PositionKind + index);
+
+                if (hasNormals) {
+                    state.attributes.push(VertexBuffer.NormalKind + index);
+                }
+
+                if (hasTangents) {
+                    state.attributes.push(VertexBuffer.TangentKind + index);
+                }
+            }
+        }
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        // Register for defines
+        state.sharedData.blocksWithDefines.push(this);
+
+        // Register for binding
+        state.sharedData.bindableBlocks.push(this);
+
+        // Register for repeatable content generation
+        state.sharedData.repeatableContentBlocks.push(this);
+
+        // Emit code
+        let position = this.position;
+        let normal = this.normal;
+        let tangent = this.tangent;
+        let positionOutput = this.positionOutput;
+        let normalOutput = this.normalOutput;
+        let tangentOutput = this.tangentOutput;
+        let comments = `//${this.name}`;
+
+        state.uniforms.push("morphTargetInfluences");
+
+        state._emitFunctionFromInclude("morphTargetsVertexGlobalDeclaration", comments);
+        state._emitFunctionFromInclude("morphTargetsVertexDeclaration", comments, {
+            repeatKey: "maxSimultaneousMorphTargets"
+        });
+
+        state.compilationString += `${this._declareOutput(positionOutput, state)} = ${position.associatedVariableName};\r\n`;
+        state.compilationString += `#ifdef NORMAL\r\n`;
+        state.compilationString += `${this._declareOutput(normalOutput, state)} = ${normal.associatedVariableName};\r\n`;
+        state.compilationString += `#endif\r\n`;
+        state.compilationString += `#ifdef TANGENT\r\n`;
+        state.compilationString += `${this._declareOutput(tangentOutput, state)} = ${tangent.associatedVariableName};\r\n`;
+        state.compilationString += `#endif\r\n`;
+
+        // Repeatable content
+        this._repeatableContentAnchor = state._repeatableContentAnchor;
+        state.compilationString += this._repeatableContentAnchor;
+
+        return this;
+    }
+}

+ 0 - 0
src/Materials/Node/Blocks/Vertex/vertexOutputBlock.ts


Some files were not shown because too many files changed in this diff