瀏覽代碼

Merge remote-tracking branch 'remotes/origin/master' into pgMonaco

David `Deltakosh` Catuhe 5 年之前
父節點
當前提交
89f1bb7c73
共有 100 個文件被更改,包括 22025 次插入5020 次删除
  1. 2 2
      .gitpod.yml
  2. 14 1
      .vscode/launch.json
  3. 0 8
      Playground/js.html
  4. 52 0
      Playground/scenes/Box/Box.obj
  5. 13637 0
      Playground/scenes/StanfordBunny.obj
  6. 1 1
      Tools/Publisher/tasks/prepareEs6DevPackages.js
  7. 4 0
      Tools/Publisher/tasks/prepareUMDDevPackages.js
  8. 5 0
      Tools/Publisher/tasks/processEs6Packages.js
  9. 5 0
      Tools/Publisher/tasks/processUMDPackages.js
  10. 1 30
      azure-pipelines-cd.yml
  11. 433 244
      dist/preview release/babylon.d.ts
  12. 2 2
      dist/preview release/babylon.js
  13. 867 172
      dist/preview release/babylon.max.js
  14. 1 1
      dist/preview release/babylon.max.js.map
  15. 898 511
      dist/preview release/babylon.module.d.ts
  16. 455 246
      dist/preview release/documentation.d.ts
  17. 1 1
      dist/preview release/glTF2Interface/package.json
  18. 51 51
      dist/preview release/gui/babylon.gui.js
  19. 1 1
      dist/preview release/gui/babylon.gui.js.map
  20. 2 2
      dist/preview release/gui/package.json
  21. 7 7
      dist/preview release/inspector/babylon.inspector.bundle.js
  22. 1325 1076
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  23. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  24. 69 14
      dist/preview release/inspector/babylon.inspector.d.ts
  25. 143 28
      dist/preview release/inspector/babylon.inspector.module.d.ts
  26. 7 7
      dist/preview release/inspector/package.json
  27. 3 3
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  28. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.js
  29. 3 3
      dist/preview release/loaders/babylonjs.loaders.js
  30. 3 3
      dist/preview release/loaders/package.json
  31. 3 3
      dist/preview release/materialsLibrary/babylon.cellMaterial.js
  32. 3 3
      dist/preview release/materialsLibrary/babylon.customMaterial.js
  33. 3 3
      dist/preview release/materialsLibrary/babylon.fireMaterial.js
  34. 3 3
      dist/preview release/materialsLibrary/babylon.furMaterial.js
  35. 3 3
      dist/preview release/materialsLibrary/babylon.gradientMaterial.js
  36. 3 3
      dist/preview release/materialsLibrary/babylon.gridMaterial.js
  37. 3 3
      dist/preview release/materialsLibrary/babylon.lavaMaterial.js
  38. 3 3
      dist/preview release/materialsLibrary/babylon.mixMaterial.js
  39. 3 3
      dist/preview release/materialsLibrary/babylon.normalMaterial.js
  40. 3 3
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js
  41. 3 3
      dist/preview release/materialsLibrary/babylon.simpleMaterial.js
  42. 3 3
      dist/preview release/materialsLibrary/babylon.skyMaterial.js
  43. 3 3
      dist/preview release/materialsLibrary/babylon.terrainMaterial.js
  44. 3 3
      dist/preview release/materialsLibrary/babylon.triPlanarMaterial.js
  45. 3 3
      dist/preview release/materialsLibrary/babylon.waterMaterial.js
  46. 3 3
      dist/preview release/materialsLibrary/babylonjs.materials.js
  47. 2 2
      dist/preview release/materialsLibrary/package.json
  48. 1 0
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  49. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  50. 1162 1179
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  51. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  52. 2 0
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  53. 2 2
      dist/preview release/nodeEditor/package.json
  54. 1 1
      dist/preview release/package.json
  55. 1 1
      dist/preview release/packagesSizeBaseLine.json
  56. 3 3
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.js
  57. 3 3
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.js
  58. 3 3
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.js
  59. 2 2
      dist/preview release/postProcessesLibrary/package.json
  60. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.brickProceduralTexture.js
  61. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.cloudProceduralTexture.js
  62. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.fireProceduralTexture.js
  63. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.grassProceduralTexture.js
  64. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.marbleProceduralTexture.js
  65. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.normalMapProceduralTexture.js
  66. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.perlinNoiseProceduralTexture.js
  67. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.roadProceduralTexture.js
  68. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.starfieldProceduralTexture.js
  69. 3 3
      dist/preview release/proceduralTexturesLibrary/babylon.woodProceduralTexture.js
  70. 3 3
      dist/preview release/proceduralTexturesLibrary/babylonjs.proceduralTextures.js
  71. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  72. 229 70
      dist/preview release/serializers/babylon.glTF2Serializer.js
  73. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  74. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  75. 22 2
      dist/preview release/serializers/babylonjs.serializers.d.ts
  76. 229 70
      dist/preview release/serializers/babylonjs.serializers.js
  77. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  78. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  79. 47 6
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  80. 3 3
      dist/preview release/serializers/package.json
  81. 898 511
      dist/preview release/viewer/babylon.module.d.ts
  82. 12 1
      dist/preview release/what's new.md
  83. 2 1
      inspector/src/components/actionTabs/lines/quaternionLineComponent.tsx
  84. 2 3
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/addAnimation.tsx
  85. 278 109
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx
  86. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationListTree.tsx
  87. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss
  88. 54 6
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar.tsx
  89. 7 2
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx
  90. 274 274
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  91. 265 156
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts
  92. 109 3
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss
  93. 74 29
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx
  94. 8 1
      nodeEditor/src/diagram/properties/genericNodePropertyComponent.tsx
  95. 8 0
      nodeEditor/src/sharedComponents/textInputLineComponent.tsx
  96. 2 2
      package.json
  97. 1 0
      readme-es6.md
  98. 3 3
      serializers/src/glTF/2.0/glTFAnimation.ts
  99. 229 63
      serializers/src/glTF/2.0/glTFExporter.ts
  100. 0 0
      serializers/src/glTF/2.0/glTFUtilities.ts

+ 2 - 2
.gitpod.yml

@@ -1,7 +1,7 @@
 image:
   file: .gitpod.Dockerfile
 tasks:
-  - command: gp await-port 1338 && sleep 3 && gp preview $(gp url 1338)/sandbox/index-local.html
+  - command: gp await-port 1338 && sleep 3 && gp preview $(gp url 1338)/sandbox/public/index-local.html
   - before: >
       cd Tools/Gulp &&
       npm i typescript gulp@4.0.0 -g &&
@@ -11,4 +11,4 @@ ports:
   - port: 1338
     onOpen: ignore
   - port: 3000
-    onOpen: ignore
+    onOpen: ignore

+ 14 - 1
.vscode/launch.json

@@ -81,7 +81,7 @@
                 "--enable-unsafe-es3-apis"
             ]
         },
-        {
+        { 
             "name": "Launch playground (Edge)",
             "type": "edge",
             "version": "dev",
@@ -212,6 +212,19 @@
             "runtimeArgs": [
                 "--enable-unsafe-es3-apis"
             ]
+        },   
+        {
+            "name": "Launch Local Dev (Edge) - Direct",
+            "type": "edge",
+            "version": "dev",
+            "request": "launch",
+            "url": "http://localhost:1338/localDev/index.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis"
+            ]
         },
         {
             "name": "Launch Local Dev - Worker mode (Chrome)",

+ 0 - 8
Playground/js.html

@@ -1,8 +0,0 @@
-<script>
-    localStorage.setItem("language", "JS");
-    
-    var parseURL = window.location.href.split("js.html");
-    var hash = parseURL[parseURL.length - 1];
-
-    window.location = "./" + hash;
-</script>

+ 52 - 0
Playground/scenes/Box/Box.obj

@@ -0,0 +1,52 @@
+# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
+# File Created: 29.06.2018 00:36:27
+
+mtllib Box.mtl
+
+#
+# object Box001
+#
+
+v  -13.8390 0.0000 16.0985
+v  -13.8390 0.0000 -16.1733
+v  13.2327 0.0000 -16.1733
+v  13.2327 0.0000 16.0985
+v  -13.8390 23.5656 16.0985
+v  13.2327 23.5656 16.0985
+v  13.2327 23.5656 -16.1733
+v  -13.8390 23.5656 -16.1733
+# 8 vertices
+
+vn -0.5774 -0.5774 0.5774
+vn -0.5774 -0.5774 -0.5774
+vn 0.5774 -0.5774 -0.5774
+vn 0.5774 -0.5774 0.5774
+vn -0.5774 0.5774 0.5774
+vn 0.5774 0.5774 0.5774
+vn 0.5774 0.5774 -0.5774
+vn -0.5774 0.5774 -0.5774
+# 8 vertex normals
+
+vt 1.0000 0.0000 0.0000
+vt 1.0000 1.0000 0.0000
+vt 0.0000 1.0000 0.0000
+vt 0.0000 0.0000 0.0000
+# 4 texture coords
+
+o Box001
+g Box001
+usemtl 01___Default
+s 2
+f 1/1/1 2/2/2 3/3/3 4/4/4 
+s 4
+f 5/4/5 6/1/6 7/2/7 8/3/8 
+s 8
+f 1/4/1 4/1/4 6/2/6 5/3/5 
+s 16
+f 4/4/4 3/1/3 7/2/7 6/3/6 
+s 32
+f 3/4/3 2/1/2 8/2/8 7/3/7 
+s 64
+f 2/4/2 1/1/1 5/2/5 8/3/8 
+# 6 polygons
+

文件差異過大導致無法顯示
+ 13637 - 0
Playground/scenes/StanfordBunny.obj


+ 1 - 1
Tools/Publisher/tasks/prepareEs6DevPackages.js

@@ -13,7 +13,7 @@ const config = require("../../Config/config.js");
  */
 function prepareEs6DevPackages() {
     config.es6modules.forEach(moduleName => {
-        if (moduleName === "viewer") {
+        if (moduleName === "viewer" || moduleName === "sandbox") {
             // Do not publish locally as an es6 npm linked module
             return;
         }

+ 4 - 0
Tools/Publisher/tasks/prepareUMDDevPackages.js

@@ -13,6 +13,10 @@ const config = require("../../Config/config.js");
  */
 function prepareUMDDevPackages() {
     config.modules.forEach(moduleName => {
+        if (moduleName === "sandbox") {
+            // Do not publish apps
+            return;
+        }
         let module = config[moduleName];
 
         colorConsole.log("Prepare " + "UMDDev".magenta + " Package: " + moduleName.blue.bold);

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

@@ -17,6 +17,11 @@ const modules = config.modules.concat(config.viewerModules);
  */
 function processEs6Packages(version) {
     config.es6modules.forEach(moduleName => {
+        if (moduleName === "sandbox") {
+            // Do not publish apps
+            return;
+        }
+
         let module = config[moduleName];
         let es6Config = module.build.es6;
 

+ 5 - 0
Tools/Publisher/tasks/processUMDPackages.js

@@ -17,6 +17,11 @@ const modules = config.modules.concat(config.viewerModules);
  */
 function processUMDPackages(version) {
     modules.forEach(moduleName => {
+        if (moduleName === "sandbox") {
+            // Do not publish apps
+            return;
+        }
+
         let module = config[moduleName];
         colorConsole.log("Process " + "UMD".magenta + " Package: " + moduleName.blue.bold);
 

+ 1 - 30
azure-pipelines-cd.yml

@@ -10,21 +10,8 @@ jobs:
   steps:
   - script: 'npm install netlify-cli'
     displayName: 'npm install netlify'
-  - script: 'npm install --prefix Playground/'
-    displayName: 'npm install playground'
   - script: 'npm install typescript'
     displayName: 'npm install typescript'
-# Controllers
-  - script: 'npx netlify link --id=220a52f0-ea85-4244-8342-8ec149cd78d6'
-    displayName: 'netlify link babylonjscontrollers'
-    env:
-      AZURE_PULLREQUESTID: $(System.PullRequest.PullRequestId)
-      NETLIFY_AUTH_TOKEN: $(babylon.netlify.authToken)
-  - script: 'npx netlify deploy --prod --dir=assets/meshes/controllers'
-    displayName: 'netlify deploy babylonjscontrollers'
-    env:
-      AZURE_PULLREQUESTID: $(System.PullRequest.PullRequestId)
-      NETLIFY_AUTH_TOKEN: $(babylon.netlify.authToken)
 # Sandbox
   - script: 'npx netlify unlink'
     displayName: 'netlify unlink'
@@ -67,20 +54,4 @@ jobs:
     displayName: 'netlify deploy viewer-babylonjs'
     env:
       AZURE_PULLREQUESTID: $(System.PullRequest.PullRequestId)
-      NETLIFY_AUTH_TOKEN: $(babylon.netlify.authToken)
-#  - task: AzureFileCopy@2
-#    displayName: 'Update Preview Storage'
-#    inputs:
-#      SourcePath: 'dist/preview release'
-#      azureSubscription: 'Visual Studio Enterprise (2bb1afe6-4492-4d3d-9cf8-e658f447d211)'
-#      Destination: AzureBlob
-#      storage: previewcdn
-#      ContainerName: '$web'
-
-#  - task: AzureCLI@2
-#    displayName: 'Purge preview CDN'
-#    inputs:
-#      azureSubscription: 'Visual Studio Enterprise(2bb1afe6-4492-4d3d-9cf8-e658f447d211)'
-#      scriptType: 'ps'
-#      scriptLocation: 'inlineScript'
-#      inlineScript: 'az cdn endpoint purge --resource-group Playground-Resources --profile-name Preview-CDN --name babylonjspreview --content-paths /*'
+      NETLIFY_AUTH_TOKEN: $(babylon.netlify.authToken)

文件差異過大導致無法顯示
+ 433 - 244
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/babylon.js


文件差異過大導致無法顯示
+ 867 - 172
dist/preview release/babylon.max.js


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/babylon.max.js.map


文件差異過大導致無法顯示
+ 898 - 511
dist/preview release/babylon.module.d.ts


文件差異過大導致無法顯示
+ 455 - 246
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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -7,7 +7,7 @@
 		exports["babylonjs-gui"] = factory(require("babylonjs"));
 	else
 		root["BABYLON"] = root["BABYLON"] || {}, root["BABYLON"]["GUI"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
@@ -400,7 +400,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_perfCounter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/perfCounter */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/perfCounter */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -543,7 +543,7 @@ var AdvancedDynamicTextureInstrumentation = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTexture", function() { return AdvancedDynamicTexture; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -1515,7 +1515,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _textBlock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./textBlock */ "./2D/controls/textBlock.ts");
 /* harmony import */ var _image__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./image */ "./2D/controls/image.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__);
 
 
@@ -1747,7 +1747,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Checkbox", function() { return Checkbox; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -1930,7 +1930,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ColorPicker", function() { return ColorPicker; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -3319,7 +3319,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container", function() { return Container; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -3734,7 +3734,7 @@ babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredTypes
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control", function() { return Control; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -5660,7 +5660,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DisplayGrid", function() { return DisplayGrid; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -5893,7 +5893,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -5990,7 +5990,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__);
 
 
@@ -6448,7 +6448,7 @@ babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__["_TypeStore"].RegisteredTypes[
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Image", function() { return Image; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -7375,7 +7375,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputPassword", function() { return InputPassword; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -7414,7 +7414,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputText", function() { return InputText; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -8427,7 +8427,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __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_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__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");
@@ -8698,7 +8698,7 @@ babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].Registere
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLine", function() { return MultiLine; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -8968,7 +8968,7 @@ babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].Registe
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RadioButton", function() { return RadioButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -9175,7 +9175,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Rectangle", function() { return Rectangle; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -9325,7 +9325,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _scrollViewerWindow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./scrollViewerWindow */ "./2D/controls/scrollViewers/scrollViewerWindow.ts");
 /* harmony import */ var _sliders_scrollBar__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../sliders/scrollBar */ "./2D/controls/sliders/scrollBar.ts");
 /* harmony import */ var _sliders_imageScrollBar__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../sliders/imageScrollBar */ "./2D/controls/sliders/imageScrollBar.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__);
 
 
@@ -10948,7 +10948,7 @@ var SelectionPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSlider", function() { return BaseSlider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -11278,7 +11278,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _baseSlider__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./baseSlider */ "./2D/controls/sliders/baseSlider.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../measure */ "./2D/measure.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -11871,7 +11871,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Slider", function() { return Slider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _baseSlider__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./baseSlider */ "./2D/controls/sliders/baseSlider.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -12126,7 +12126,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel", function() { return StackPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -12394,7 +12394,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextWrapping", function() { return TextWrapping; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextBlock", function() { return TextBlock; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -12857,7 +12857,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KeyPropertySet", function() { return KeyPropertySet; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VirtualKeyboard", function() { return VirtualKeyboard; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -13246,7 +13246,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_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -13471,7 +13471,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_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 var tmpRect = [
@@ -13620,7 +13620,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_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -13763,7 +13763,7 @@ var MultiLinePoint = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Style", function() { return Style; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -14069,7 +14069,7 @@ var ValueAndUnit = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "XmlLoader", function() { return XmlLoader; });
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -14388,7 +14388,7 @@ var XmlLoader = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AbstractButton3D", function() { return AbstractButton3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -14431,7 +14431,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_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__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");
@@ -14612,7 +14612,7 @@ var Button3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container3D", function() { return Container3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -14769,7 +14769,7 @@ var Container3D = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control3D", function() { return Control3D; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -15175,7 +15175,7 @@ var Control3D = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CylinderPanel", function() { return CylinderPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -15261,7 +15261,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_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Materials/standardMaterial */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Materials/standardMaterial */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_standardMaterial__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");
@@ -15755,7 +15755,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_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__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");
@@ -15810,7 +15810,7 @@ var PlanePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScatterPanel", function() { return ScatterPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -15937,7 +15937,7 @@ var ScatterPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SpherePanel", function() { return SpherePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -16023,7 +16023,7 @@ var SpherePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel3D", function() { return StackPanel3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -16148,7 +16148,7 @@ var StackPanel3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VolumeBasedPanel", function() { return VolumeBasedPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -16339,7 +16339,7 @@ var VolumeBasedPanel = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GUI3DManager", function() { return GUI3DManager; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* 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");
 
@@ -16606,7 +16606,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterialDefines", function() { return FluentMaterialDefines; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterial", function() { return FluentMaterial; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/perfCounter");
 /* 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");
@@ -16929,7 +16929,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentPixelShader", function() { return fluentPixelShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/perfCounter");
 /* 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';
@@ -16951,7 +16951,7 @@ var fluentPixelShader = { name: name, shader: shader };
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentVertexShader", function() { return fluentVertexShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/perfCounter");
 /* 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';
@@ -16974,7 +16974,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_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -17276,14 +17276,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Misc/observable":
+/***/ "babylonjs/Misc/perfCounter":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__;
 
 /***/ })
 

文件差異過大導致無法顯示
+ 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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.23"
+        "babylonjs": "4.2.0-alpha.25"
     },
     "engines": {
         "node": "*"

文件差異過大導致無法顯示
+ 7 - 7
dist/preview release/inspector/babylon.inspector.bundle.js


文件差異過大導致無法顯示
+ 1325 - 1076
dist/preview release/inspector/babylon.inspector.bundle.max.js


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


+ 69 - 14
dist/preview release/inspector/babylon.inspector.d.ts

@@ -418,6 +418,7 @@ declare module INSPECTOR {
         shouldComponentUpdate(nextProps: IQuaternionLineComponentProps, nextState: {
             isExpanded: boolean;
             value: BABYLON.Quaternion;
+            eulerValue: BABYLON.Vector3;
         }): boolean;
         switchExpandState(): void;
         raiseOnPropertyChanged(currentValue: BABYLON.Quaternion, previousValue: BABYLON.Quaternion): void;
@@ -580,6 +581,7 @@ declare module INSPECTOR {
         positionCanvas?: number;
         repositionCanvas?: boolean;
         canvasPositionEnded: () => void;
+        resetActionableKeyframe: () => void;
     }
     export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         panX: number;
@@ -609,7 +611,7 @@ declare module INSPECTOR {
         keyDown(e: KeyboardEvent): void;
         keyUp(e: KeyboardEvent): void;
         focus(e: React.MouseEvent<SVGSVGElement>): void;
-        isControlPointActive(): boolean;
+        isNotControlPointActive(): boolean;
         render(): JSX.Element;
     }
 }
@@ -737,14 +739,20 @@ declare module INSPECTOR {
         setLerpMode: () => void;
         brokenMode: boolean;
         lerpMode: boolean;
-        currentValue: number;
-        currentFrame: number;
+        actionableKeyframe: IActionableKeyFrame;
         title: string;
         close: (event: any) => void;
         enabled: boolean;
+        setKeyframeValue: () => void;
     }
     export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
+        private _frameInput;
+        private _valueInput;
         constructor(props: IGraphActionsBarProps);
+        componentDidMount(): void;
+        componentWillUnmount(): void;
+        isEnterKeyUp(event: KeyboardEvent): void;
+        onBlur(event: React.FocusEvent<HTMLInputElement>): void;
         render(): JSX.Element;
     }
 }
@@ -964,6 +972,10 @@ declare module INSPECTOR {
         value: number;
         label: number;
     }
+    export interface IActionableKeyFrame {
+        frame?: number | string;
+        value?: any;
+    }
     interface ICurveData {
         pathData: string;
         pathLength: number;
@@ -997,6 +1009,7 @@ declare module INSPECTOR {
         panningY: number;
         panningX: number;
         repositionCanvas: boolean;
+        actionableKeyframe: IActionableKeyFrame;
     }> {
         private _snippetUrl;
         private _heightScale;
@@ -1034,21 +1047,30 @@ declare module INSPECTOR {
         setAxesLength(): void;
         getValueLabel(i: number): number;
         resetPlayheadOffset(): void;
+        encodeCurveId(animationName: string, coordinate: number): string;
+        decodeCurveId(id: string): {
+            order: number;
+            coordinate: number;
+        };
+        getKeyframeValueFromAnimation(id: string): {
+            frame: number;
+            value: number;
+        } | undefined;
         /**
          * Keyframe Manipulation
          * This section handles events from SvgDraggableArea.
          */
         selectKeyframe(id: string, multiselect: boolean): void;
+        resetActionableKeyframe(): void;
         selectedControlPoint(type: string, id: string): void;
         deselectKeyframes(): void;
         updateValuePerCoordinate(dataType: number, value: number | BABYLON.Vector2 | BABYLON.Vector3 | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Size | BABYLON.Quaternion, newValue: number, coordinate?: number): number | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, id: string): void;
-        /**
-         * Actions
-         * This section handles events from GraphActionsBar.
-         */
+        updateLeftControlPoint(updatedSvgKeyFrame: IKeyframeSvgPoint, key: BABYLON.IAnimationKey, dataType: number, coordinate: number): void;
+        updateRightControlPoint(updatedSvgKeyFrame: IKeyframeSvgPoint, key: BABYLON.IAnimationKey, dataType: number, coordinate: number): void;
         handleFrameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleValueChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        setKeyframeValue(): void;
         setFlatTangent(): void;
         setTangentMode(): void;
         setBrokenMode(): void;
@@ -1064,8 +1086,9 @@ declare module INSPECTOR {
         linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
         setKeyframePointLinear(point: BABYLON.Vector2, index: number): void;
         flatTangents(keyframes: BABYLON.IAnimationKey[], dataType: number): BABYLON.IAnimationKey[];
-        returnZero(dataType: number): number | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size | undefined;
+        returnZero(dataType: number): 0 | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size;
         getValueAsArray(valueType: number, value: number | BABYLON.Vector2 | BABYLON.Vector3 | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Size | BABYLON.Quaternion): number[];
+        setValueAsType(valueType: number, arrayValue: number[]): number | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size;
         getPathData(animation: BABYLON.Animation | null): ICurveData[] | undefined;
         getAnimationData(animation: BABYLON.Animation): {
             loopMode: number | undefined;
@@ -1092,6 +1115,8 @@ declare module INSPECTOR {
         selectAnimation(animation: BABYLON.Animation, coordinate?: SelectedCoordinate): void;
         isAnimationPlaying(): boolean;
         stopAnimation(): void;
+        setIsLooping(): void;
+        setFramesPerSecond(fps: number): void;
         analizeAnimationForLerp(animation: BABYLON.Animation | null): boolean;
         /**
          * Timeline
@@ -1270,22 +1295,44 @@ declare module INSPECTOR {
     export class TextureCanvasManager {
         private _engine;
         private _scene;
-        private _texture;
         private _camera;
-        private _canvas;
         private _scale;
         private _isPanning;
         private _mouseX;
         private _mouseY;
+        private _UICanvas;
+        private _size;
+        private _2DCanvas;
+        private _texture;
+        private _displayCanvas;
+        private _displayChannel;
+        private _displayTexture;
+        private _originalTexture;
+        private _targetTexture;
+        private _originalInternalTexture;
         private _plane;
         private _planeMaterial;
+        private keyMap;
         private static ZOOM_MOUSE_SPEED;
         private static ZOOM_KEYBOARD_SPEED;
+        private static ZOOM_IN_KEY;
+        private static ZOOM_OUT_KEY;
         private static PAN_SPEED;
-        private static PAN_BUTTON;
+        private static PAN_MOUSE_BUTTON;
+        private static PAN_KEY;
         private static MIN_SCALE;
         private static MAX_SCALE;
-        constructor(targetCanvas: HTMLCanvasElement, texture: BABYLON.BaseTexture);
+        metadata: any;
+        constructor(texture: BABYLON.BaseTexture, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvasDisplay: HTMLCanvasElement);
+        updateTexture(): void;
+        private copyTextureToDisplayTexture;
+        set displayChannel(channel: TextureChannelToDisplay);
+        get displayChannel(): TextureChannelToDisplay;
+        static paintPixelsOnCanvas(pixelData: Uint8Array, canvas: HTMLCanvasElement): void;
+        static flipCanvas(canvas: HTMLCanvasElement): void;
+        get scene(): BABYLON.Scene;
+        get canvas2D(): HTMLCanvasElement;
+        get size(): BABYLON.ISize;
         dispose(): void;
     }
 }
@@ -1294,10 +1341,18 @@ declare module INSPECTOR {
         globalState: GlobalState;
         texture: BABYLON.BaseTexture;
     }
-    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps> {
+    interface TextureEditorComponentState {
+        channel: TextureChannelToDisplay;
+    }
+    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
         private _textureCanvasManager;
-        private reactCanvas;
+        private canvasUI;
+        private canvas2D;
+        private canvasDisplay;
+        private channels;
+        constructor(props: TextureEditorComponentProps);
         componentDidMount(): void;
+        componentDidUpdate(): void;
         componentWillUnmount(): void;
         render(): JSX.Element;
     }

+ 143 - 28
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -472,6 +472,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/quaternionLineCo
         shouldComponentUpdate(nextProps: IQuaternionLineComponentProps, nextState: {
             isExpanded: boolean;
             value: Quaternion;
+            eulerValue: Vector3;
         }): boolean;
         switchExpandState(): void;
         raiseOnPropertyChanged(currentValue: Quaternion, previousValue: Quaternion): void;
@@ -656,6 +657,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         positionCanvas?: number;
         repositionCanvas?: boolean;
         canvasPositionEnded: () => void;
+        resetActionableKeyframe: () => void;
     }
     export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         panX: number;
@@ -685,7 +687,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         keyDown(e: KeyboardEvent): void;
         keyUp(e: KeyboardEvent): void;
         focus(e: React.MouseEvent<SVGSVGElement>): void;
-        isControlPointActive(): boolean;
+        isNotControlPointActive(): boolean;
         render(): JSX.Element;
     }
 }
@@ -810,6 +812,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar" {
     import * as React from 'react';
+    import { IActionableKeyFrame } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent";
     interface IGraphActionsBarProps {
         addKeyframe: () => void;
         removeKeyframe: () => void;
@@ -820,14 +823,20 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         setLerpMode: () => void;
         brokenMode: boolean;
         lerpMode: boolean;
-        currentValue: number;
-        currentFrame: number;
+        actionableKeyframe: IActionableKeyFrame;
         title: string;
         close: (event: any) => void;
         enabled: boolean;
+        setKeyframeValue: () => void;
     }
     export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
+        private _frameInput;
+        private _valueInput;
         constructor(props: IGraphActionsBarProps);
+        componentDidMount(): void;
+        componentWillUnmount(): void;
+        isEnterKeyUp(event: KeyboardEvent): void;
+        onBlur(event: React.FocusEvent<HTMLInputElement>): void;
         render(): JSX.Element;
     }
 }
@@ -1097,6 +1106,10 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         value: number;
         label: number;
     }
+    export interface IActionableKeyFrame {
+        frame?: number | string;
+        value?: any;
+    }
     interface ICurveData {
         pathData: string;
         pathLength: number;
@@ -1130,6 +1143,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         panningY: number;
         panningX: number;
         repositionCanvas: boolean;
+        actionableKeyframe: IActionableKeyFrame;
     }> {
         private _snippetUrl;
         private _heightScale;
@@ -1167,21 +1181,30 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         setAxesLength(): void;
         getValueLabel(i: number): number;
         resetPlayheadOffset(): void;
+        encodeCurveId(animationName: string, coordinate: number): string;
+        decodeCurveId(id: string): {
+            order: number;
+            coordinate: number;
+        };
+        getKeyframeValueFromAnimation(id: string): {
+            frame: number;
+            value: number;
+        } | undefined;
         /**
          * Keyframe Manipulation
          * This section handles events from SvgDraggableArea.
          */
         selectKeyframe(id: string, multiselect: boolean): void;
+        resetActionableKeyframe(): void;
         selectedControlPoint(type: string, id: string): void;
         deselectKeyframes(): void;
         updateValuePerCoordinate(dataType: number, value: number | Vector2 | Vector3 | Color3 | Color4 | Size | Quaternion, newValue: number, coordinate?: number): number | Vector3 | Quaternion | Color3 | Color4 | Vector2 | Size;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, id: string): void;
-        /**
-         * Actions
-         * This section handles events from GraphActionsBar.
-         */
+        updateLeftControlPoint(updatedSvgKeyFrame: IKeyframeSvgPoint, key: IAnimationKey, dataType: number, coordinate: number): void;
+        updateRightControlPoint(updatedSvgKeyFrame: IKeyframeSvgPoint, key: IAnimationKey, dataType: number, coordinate: number): void;
         handleFrameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleValueChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        setKeyframeValue(): void;
         setFlatTangent(): void;
         setTangentMode(): void;
         setBrokenMode(): void;
@@ -1197,8 +1220,9 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         linearInterpolation(keyframes: IAnimationKey[], data: string, middle: number): string;
         setKeyframePointLinear(point: Vector2, index: number): void;
         flatTangents(keyframes: IAnimationKey[], dataType: number): IAnimationKey[];
-        returnZero(dataType: number): number | Vector3 | Quaternion | Color3 | Color4 | Vector2 | Size | undefined;
+        returnZero(dataType: number): 0 | Vector3 | Quaternion | Color3 | Color4 | Vector2 | Size;
         getValueAsArray(valueType: number, value: number | Vector2 | Vector3 | Color3 | Color4 | Size | Quaternion): number[];
+        setValueAsType(valueType: number, arrayValue: number[]): number | Vector3 | Quaternion | Color3 | Color4 | Vector2 | Size;
         getPathData(animation: Animation | null): ICurveData[] | undefined;
         getAnimationData(animation: Animation): {
             loopMode: number | undefined;
@@ -1225,6 +1249,8 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         selectAnimation(animation: Animation, coordinate?: SelectedCoordinate): void;
         isAnimationPlaying(): boolean;
         stopAnimation(): void;
+        setIsLooping(): void;
+        setFramesPerSecond(fps: number): void;
         analizeAnimationForLerp(animation: Animation | null): boolean;
         /**
          * Timeline
@@ -1437,26 +1463,51 @@ declare module "babylonjs-inspector/components/actionTabs/lines/textureLineCompo
     }
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager" {
+    import { Scene } from 'babylonjs/scene';
     import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
+    import { TextureChannelToDisplay } from "babylonjs-inspector/textureHelper";
+    import { ISize } from 'babylonjs/Maths/math.size';
     export class TextureCanvasManager {
         private _engine;
         private _scene;
-        private _texture;
         private _camera;
-        private _canvas;
         private _scale;
         private _isPanning;
         private _mouseX;
         private _mouseY;
+        private _UICanvas;
+        private _size;
+        private _2DCanvas;
+        private _texture;
+        private _displayCanvas;
+        private _displayChannel;
+        private _displayTexture;
+        private _originalTexture;
+        private _targetTexture;
+        private _originalInternalTexture;
         private _plane;
         private _planeMaterial;
+        private keyMap;
         private static ZOOM_MOUSE_SPEED;
         private static ZOOM_KEYBOARD_SPEED;
+        private static ZOOM_IN_KEY;
+        private static ZOOM_OUT_KEY;
         private static PAN_SPEED;
-        private static PAN_BUTTON;
+        private static PAN_MOUSE_BUTTON;
+        private static PAN_KEY;
         private static MIN_SCALE;
         private static MAX_SCALE;
-        constructor(targetCanvas: HTMLCanvasElement, texture: BaseTexture);
+        metadata: any;
+        constructor(texture: BaseTexture, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvasDisplay: HTMLCanvasElement);
+        updateTexture(): void;
+        private copyTextureToDisplayTexture;
+        set displayChannel(channel: TextureChannelToDisplay);
+        get displayChannel(): TextureChannelToDisplay;
+        static paintPixelsOnCanvas(pixelData: Uint8Array, canvas: HTMLCanvasElement): void;
+        static flipCanvas(canvas: HTMLCanvasElement): void;
+        get scene(): Scene;
+        get canvas2D(): HTMLCanvasElement;
+        get size(): ISize;
         dispose(): void;
     }
 }
@@ -1464,14 +1515,23 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     import * as React from 'react';
     import { GlobalState } from "babylonjs-inspector/components/globalState";
     import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
+    import { TextureChannelToDisplay } from "babylonjs-inspector/textureHelper";
     interface TextureEditorComponentProps {
         globalState: GlobalState;
         texture: BaseTexture;
     }
-    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps> {
+    interface TextureEditorComponentState {
+        channel: TextureChannelToDisplay;
+    }
+    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
         private _textureCanvasManager;
-        private reactCanvas;
+        private canvasUI;
+        private canvas2D;
+        private canvasDisplay;
+        private channels;
+        constructor(props: TextureEditorComponentProps);
         componentDidMount(): void;
+        componentDidUpdate(): void;
         componentWillUnmount(): void;
         render(): JSX.Element;
     }
@@ -4129,6 +4189,7 @@ declare module INSPECTOR {
         shouldComponentUpdate(nextProps: IQuaternionLineComponentProps, nextState: {
             isExpanded: boolean;
             value: BABYLON.Quaternion;
+            eulerValue: BABYLON.Vector3;
         }): boolean;
         switchExpandState(): void;
         raiseOnPropertyChanged(currentValue: BABYLON.Quaternion, previousValue: BABYLON.Quaternion): void;
@@ -4291,6 +4352,7 @@ declare module INSPECTOR {
         positionCanvas?: number;
         repositionCanvas?: boolean;
         canvasPositionEnded: () => void;
+        resetActionableKeyframe: () => void;
     }
     export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         panX: number;
@@ -4320,7 +4382,7 @@ declare module INSPECTOR {
         keyDown(e: KeyboardEvent): void;
         keyUp(e: KeyboardEvent): void;
         focus(e: React.MouseEvent<SVGSVGElement>): void;
-        isControlPointActive(): boolean;
+        isNotControlPointActive(): boolean;
         render(): JSX.Element;
     }
 }
@@ -4448,14 +4510,20 @@ declare module INSPECTOR {
         setLerpMode: () => void;
         brokenMode: boolean;
         lerpMode: boolean;
-        currentValue: number;
-        currentFrame: number;
+        actionableKeyframe: IActionableKeyFrame;
         title: string;
         close: (event: any) => void;
         enabled: boolean;
+        setKeyframeValue: () => void;
     }
     export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
+        private _frameInput;
+        private _valueInput;
         constructor(props: IGraphActionsBarProps);
+        componentDidMount(): void;
+        componentWillUnmount(): void;
+        isEnterKeyUp(event: KeyboardEvent): void;
+        onBlur(event: React.FocusEvent<HTMLInputElement>): void;
         render(): JSX.Element;
     }
 }
@@ -4675,6 +4743,10 @@ declare module INSPECTOR {
         value: number;
         label: number;
     }
+    export interface IActionableKeyFrame {
+        frame?: number | string;
+        value?: any;
+    }
     interface ICurveData {
         pathData: string;
         pathLength: number;
@@ -4708,6 +4780,7 @@ declare module INSPECTOR {
         panningY: number;
         panningX: number;
         repositionCanvas: boolean;
+        actionableKeyframe: IActionableKeyFrame;
     }> {
         private _snippetUrl;
         private _heightScale;
@@ -4745,21 +4818,30 @@ declare module INSPECTOR {
         setAxesLength(): void;
         getValueLabel(i: number): number;
         resetPlayheadOffset(): void;
+        encodeCurveId(animationName: string, coordinate: number): string;
+        decodeCurveId(id: string): {
+            order: number;
+            coordinate: number;
+        };
+        getKeyframeValueFromAnimation(id: string): {
+            frame: number;
+            value: number;
+        } | undefined;
         /**
          * Keyframe Manipulation
          * This section handles events from SvgDraggableArea.
          */
         selectKeyframe(id: string, multiselect: boolean): void;
+        resetActionableKeyframe(): void;
         selectedControlPoint(type: string, id: string): void;
         deselectKeyframes(): void;
         updateValuePerCoordinate(dataType: number, value: number | BABYLON.Vector2 | BABYLON.Vector3 | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Size | BABYLON.Quaternion, newValue: number, coordinate?: number): number | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, id: string): void;
-        /**
-         * Actions
-         * This section handles events from GraphActionsBar.
-         */
+        updateLeftControlPoint(updatedSvgKeyFrame: IKeyframeSvgPoint, key: BABYLON.IAnimationKey, dataType: number, coordinate: number): void;
+        updateRightControlPoint(updatedSvgKeyFrame: IKeyframeSvgPoint, key: BABYLON.IAnimationKey, dataType: number, coordinate: number): void;
         handleFrameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleValueChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        setKeyframeValue(): void;
         setFlatTangent(): void;
         setTangentMode(): void;
         setBrokenMode(): void;
@@ -4775,8 +4857,9 @@ declare module INSPECTOR {
         linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
         setKeyframePointLinear(point: BABYLON.Vector2, index: number): void;
         flatTangents(keyframes: BABYLON.IAnimationKey[], dataType: number): BABYLON.IAnimationKey[];
-        returnZero(dataType: number): number | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size | undefined;
+        returnZero(dataType: number): 0 | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size;
         getValueAsArray(valueType: number, value: number | BABYLON.Vector2 | BABYLON.Vector3 | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Size | BABYLON.Quaternion): number[];
+        setValueAsType(valueType: number, arrayValue: number[]): number | BABYLON.Vector3 | BABYLON.Quaternion | BABYLON.Color3 | BABYLON.Color4 | BABYLON.Vector2 | BABYLON.Size;
         getPathData(animation: BABYLON.Animation | null): ICurveData[] | undefined;
         getAnimationData(animation: BABYLON.Animation): {
             loopMode: number | undefined;
@@ -4803,6 +4886,8 @@ declare module INSPECTOR {
         selectAnimation(animation: BABYLON.Animation, coordinate?: SelectedCoordinate): void;
         isAnimationPlaying(): boolean;
         stopAnimation(): void;
+        setIsLooping(): void;
+        setFramesPerSecond(fps: number): void;
         analizeAnimationForLerp(animation: BABYLON.Animation | null): boolean;
         /**
          * Timeline
@@ -4981,22 +5066,44 @@ declare module INSPECTOR {
     export class TextureCanvasManager {
         private _engine;
         private _scene;
-        private _texture;
         private _camera;
-        private _canvas;
         private _scale;
         private _isPanning;
         private _mouseX;
         private _mouseY;
+        private _UICanvas;
+        private _size;
+        private _2DCanvas;
+        private _texture;
+        private _displayCanvas;
+        private _displayChannel;
+        private _displayTexture;
+        private _originalTexture;
+        private _targetTexture;
+        private _originalInternalTexture;
         private _plane;
         private _planeMaterial;
+        private keyMap;
         private static ZOOM_MOUSE_SPEED;
         private static ZOOM_KEYBOARD_SPEED;
+        private static ZOOM_IN_KEY;
+        private static ZOOM_OUT_KEY;
         private static PAN_SPEED;
-        private static PAN_BUTTON;
+        private static PAN_MOUSE_BUTTON;
+        private static PAN_KEY;
         private static MIN_SCALE;
         private static MAX_SCALE;
-        constructor(targetCanvas: HTMLCanvasElement, texture: BABYLON.BaseTexture);
+        metadata: any;
+        constructor(texture: BABYLON.BaseTexture, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvasDisplay: HTMLCanvasElement);
+        updateTexture(): void;
+        private copyTextureToDisplayTexture;
+        set displayChannel(channel: TextureChannelToDisplay);
+        get displayChannel(): TextureChannelToDisplay;
+        static paintPixelsOnCanvas(pixelData: Uint8Array, canvas: HTMLCanvasElement): void;
+        static flipCanvas(canvas: HTMLCanvasElement): void;
+        get scene(): BABYLON.Scene;
+        get canvas2D(): HTMLCanvasElement;
+        get size(): BABYLON.ISize;
         dispose(): void;
     }
 }
@@ -5005,10 +5112,18 @@ declare module INSPECTOR {
         globalState: GlobalState;
         texture: BABYLON.BaseTexture;
     }
-    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps> {
+    interface TextureEditorComponentState {
+        channel: TextureChannelToDisplay;
+    }
+    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
         private _textureCanvasManager;
-        private reactCanvas;
+        private canvasUI;
+        private canvas2D;
+        private canvasDisplay;
+        private channels;
+        constructor(props: TextureEditorComponentProps);
         componentDidMount(): void;
+        componentDidUpdate(): void;
         componentWillUnmount(): void;
         render(): JSX.Element;
     }

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,12 +29,12 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.23",
-        "babylonjs-gui": "4.2.0-alpha.23",
-        "babylonjs-loaders": "4.2.0-alpha.23",
-        "babylonjs-materials": "4.2.0-alpha.23",
-        "babylonjs-serializers": "4.2.0-alpha.23",
-        "babylonjs-gltf2interface": "4.2.0-alpha.23"
+        "babylonjs": "4.2.0-alpha.25",
+        "babylonjs-gui": "4.2.0-alpha.25",
+        "babylonjs-loaders": "4.2.0-alpha.25",
+        "babylonjs-materials": "4.2.0-alpha.25",
+        "babylonjs-serializers": "4.2.0-alpha.25",
+        "babylonjs-gltf2interface": "4.2.0-alpha.25"
     },
     "peerDependencies": {
         "@types/react": ">=16.7.3",

+ 3 - 3
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.2.0-alpha.23",
-        "babylonjs": "4.2.0-alpha.23"
+        "babylonjs-gltf2interface": "4.2.0-alpha.25",
+        "babylonjs": "4.2.0-alpha.25"
     },
     "engines": {
         "node": "*"

+ 3 - 3
dist/preview release/materialsLibrary/babylon.cellMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.customMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.fireMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.furMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.gradientMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.gridMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.lavaMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.mixMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.normalMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.simpleMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.skyMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.terrainMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.triPlanarMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylon.waterMaterial.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/materialsLibrary/babylonjs.materials.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.23"
+        "babylonjs": "4.2.0-alpha.25"
     },
     "engines": {
         "node": "*"

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

@@ -788,6 +788,7 @@ declare module NODEEDITOR {
         propertyName?: string;
         value?: string;
         onChange?: (value: string) => void;
+        validator?: (value: string) => boolean;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/nodeEditor/babylon.nodeEditor.js


文件差異過大導致無法顯示
+ 1162 - 1179
dist/preview release/nodeEditor/babylon.nodeEditor.max.js


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map


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

@@ -981,6 +981,7 @@ declare module "babylonjs-node-editor/sharedComponents/textInputLineComponent" {
         propertyName?: string;
         value?: string;
         onChange?: (value: string) => void;
+        validator?: (value: string) => boolean;
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
     }
     export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {
@@ -2651,6 +2652,7 @@ declare module NODEEDITOR {
         propertyName?: string;
         value?: string;
         onChange?: (value: string) => void;
+        validator?: (value: string) => boolean;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     export class TextInputLineComponent extends React.Component<ITextInputLineComponentProps, {

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

@@ -4,14 +4,14 @@
     },
     "name": "babylonjs-node-editor",
     "description": "The Babylon.js node material editor.",
-    "version": "4.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.23"
+        "babylonjs": "4.2.0-alpha.25"
     },
     "files": [
         "babylon.nodeEditor.max.js.map",

+ 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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -1 +1 @@
-{"thinEngineOnly":116308,"engineOnly":152744,"sceneOnly":515374,"minGridMaterial":652684,"minStandardMaterial":801332}
+{"thinEngineOnly":116955,"engineOnly":153391,"sceneOnly":516268,"minGridMaterial":653578,"minStandardMaterial":803077}

+ 3 - 3
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/postProcessesLibrary/babylonjs.postProcess.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.23"
+        "babylonjs": "4.2.0-alpha.25"
     },
     "engines": {
         "node": "*"

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.brickProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.cloudProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.fireProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.grassProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.marbleProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.normalMapProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.perlinNoiseProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.roadProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.starfieldProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylon.woodProceduralTexture.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 3 - 3
dist/preview release/proceduralTexturesLibrary/babylonjs.proceduralTextures.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 

+ 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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.23"
+        "babylonjs": "4.2.0-alpha.25"
     },
     "engines": {
         "node": "*"

+ 229 - 70
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
@@ -1550,10 +1550,10 @@ var _GLTFAnimation = /** @class */ (function () {
             if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                 if (tangentValue) {
                     if (useQuaternion) {
-                        tangent = tangentValue.scale(frameDelta).asArray();
+                        tangent = tangentValue.asArray();
                     }
                     else {
-                        var array = tangentValue.scale(frameDelta);
+                        var array = tangentValue;
                         tangent = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                     }
                     if (convertToRightHandedSystem) {
@@ -1569,7 +1569,7 @@ var _GLTFAnimation = /** @class */ (function () {
             }
             else {
                 if (tangentValue) {
-                    tangent = tangentValue.scale(frameDelta).asArray();
+                    tangent = tangentValue.asArray();
                     if (convertToRightHandedSystem) {
                         if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             _glTFUtilities__WEBPACK_IMPORTED_MODULE_1__["_GLTFUtilities"]._GetRightHandedPositionArray3FromRef(tangent);
@@ -1740,6 +1740,7 @@ var _Exporter = /** @class */ (function () {
         this._materialMap = [];
         this._textures = [];
         this._samplers = [];
+        this._skins = [];
         this._animations = [];
         this._imageData = {};
         this._options = options || {};
@@ -2199,12 +2200,11 @@ var _Exporter = /** @class */ (function () {
      * Returns the bytelength of the data
      * @param vertexBufferKind Indicates what kind of vertex data is being passed in
      * @param meshAttributeArray Array containing the attribute data
+     * @param byteStride Specifies the space between data
      * @param binaryWriter The buffer to write the binary data to
-     * @param indices Used to specify the order of the vertex data
      * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, byteStride, binaryWriter, convertToRightHandedSystem) {
-        var stride = byteStride / 4;
+    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, attributeComponentKind, meshAttributeArray, stride, binaryWriter, convertToRightHandedSystem, babylonTransformNode) {
         var vertexAttributes = [];
         var index;
         switch (vertexBufferKind) {
@@ -2259,16 +2259,56 @@ var _Exporter = /** @class */ (function () {
                 }
                 break;
             }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesKind:
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesExtraKind: {
+                for (var k = 0, length_7 = meshAttributeArray.length / stride; k < length_7; ++k) {
+                    index = k * stride;
+                    var vertexData = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
+                    vertexAttributes.push(vertexData.asArray());
+                }
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsKind:
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsExtraKind: {
+                for (var k = 0, length_8 = meshAttributeArray.length / stride; k < length_8; ++k) {
+                    index = k * stride;
+                    var vertexData = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
+                    vertexAttributes.push(vertexData.asArray());
+                }
+                break;
+            }
             default: {
                 babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Unsupported Vertex Buffer Type: " + vertexBufferKind);
                 vertexAttributes = [];
             }
         }
+        var writeBinaryFunc;
+        switch (attributeComponentKind) {
+            case 5121 /* UNSIGNED_BYTE */: {
+                writeBinaryFunc = binaryWriter.setUInt8.bind(binaryWriter);
+                break;
+            }
+            case 5123 /* UNSIGNED_SHORT */: {
+                writeBinaryFunc = binaryWriter.setUInt16.bind(binaryWriter);
+                break;
+            }
+            case 5125 /* UNSIGNED_INT */: {
+                writeBinaryFunc = binaryWriter.setUInt32.bind(binaryWriter);
+            }
+            case 5126 /* FLOAT */: {
+                writeBinaryFunc = binaryWriter.setFloat32.bind(binaryWriter);
+                break;
+            }
+            default: {
+                babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Unsupported Attribute Component kind: " + attributeComponentKind);
+                return;
+            }
+        }
         for (var _i = 0, vertexAttributes_1 = vertexAttributes; _i < vertexAttributes_1.length; _i++) {
             var vertexAttribute = vertexAttributes_1[_i];
             for (var _a = 0, vertexAttribute_1 = vertexAttribute; _a < vertexAttribute_1.length; _a++) {
                 var component = vertexAttribute_1[_a];
-                binaryWriter.setFloat32(component);
+                writeBinaryFunc(component);
             }
         }
     };
@@ -2317,6 +2357,9 @@ var _Exporter = /** @class */ (function () {
         if (this._samplers && this._samplers.length) {
             this._glTF.samplers = this._samplers;
         }
+        if (this._skins && this._skins.length) {
+            this._glTF.skins = this._skins;
+        }
         if (this._images && this._images.length) {
             if (!shouldUseGlb) {
                 this._glTF.images = this._images;
@@ -2519,21 +2562,24 @@ var _Exporter = /** @class */ (function () {
     /**
      * Creates a bufferview based on the vertices type for the Babylon mesh
      * @param kind Indicates the type of vertices data
+     * @param componentType Indicates the numerical type used to store the data
      * @param babylonTransformNode The Babylon mesh to get the vertices data from
      * @param binaryWriter The buffer to write the bufferview data to
      * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.createBufferViewKind = function (kind, babylonTransformNode, binaryWriter, byteStride, convertToRightHandedSystem) {
+    _Exporter.prototype.createBufferViewKind = function (kind, attributeComponentKind, babylonTransformNode, binaryWriter, byteStride, convertToRightHandedSystem) {
         var bufferMesh = babylonTransformNode instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Mesh"] ?
             babylonTransformNode : babylonTransformNode instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["InstancedMesh"] ?
             babylonTransformNode.sourceMesh : null;
         if (bufferMesh) {
+            var vertexBuffer = bufferMesh.getVertexBuffer(kind);
             var vertexData = bufferMesh.getVerticesData(kind);
-            if (vertexData) {
-                var byteLength = vertexData.length * 4;
+            if (vertexBuffer && vertexData) {
+                var typeByteLength = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(attributeComponentKind);
+                var byteLength = vertexData.length * typeByteLength;
                 var bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                 this._bufferViews.push(bufferView);
-                this.writeAttributeData(kind, vertexData, byteStride, binaryWriter, convertToRightHandedSystem);
+                this.writeAttributeData(kind, attributeComponentKind, vertexData, byteStride / typeByteLength, binaryWriter, convertToRightHandedSystem, babylonTransformNode);
             }
         }
     };
@@ -2619,6 +2665,22 @@ var _Exporter = /** @class */ (function () {
                 meshPrimitive.attributes.TEXCOORD_1 = this._accessors.length - 1;
                 break;
             }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesKind: {
+                meshPrimitive.attributes.JOINTS_0 = this._accessors.length - 1;
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesExtraKind: {
+                meshPrimitive.attributes.JOINTS_1 = this._accessors.length - 1;
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsKind: {
+                meshPrimitive.attributes.WEIGHTS_0 = this._accessors.length - 1;
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsExtraKind: {
+                meshPrimitive.attributes.WEIGHTS_1 = this._accessors.length - 1;
+                break;
+            }
             default: {
                 babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Unsupported Vertex Buffer Type: " + attributeKind);
             }
@@ -2644,12 +2706,16 @@ var _Exporter = /** @class */ (function () {
             bufferMesh = babylonTransformNode.sourceMesh;
         }
         var attributeData = [
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind, accessorType: "VEC3" /* VEC3 */, byteStride: 12 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].NormalKind, accessorType: "VEC3" /* VEC3 */, byteStride: 12 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind, accessorType: "VEC4" /* VEC4 */, byteStride: 16 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].TangentKind, accessorType: "VEC4" /* VEC4 */, byteStride: 16 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UVKind, accessorType: "VEC2" /* VEC2 */, byteStride: 8 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind, accessorType: "VEC2" /* VEC2 */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind, accessorType: "VEC3" /* VEC3 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 12 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].NormalKind, accessorType: "VEC3" /* VEC3 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 12 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].TangentKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UVKind, accessorType: "VEC2" /* VEC2 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind, accessorType: "VEC2" /* VEC2 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5123 /* UNSIGNED_SHORT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesExtraKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5123 /* UNSIGNED_SHORT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsExtraKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
         ];
         if (bufferMesh) {
             var indexBufferViewIndex = null;
@@ -2659,13 +2725,14 @@ var _Exporter = /** @class */ (function () {
             for (var _i = 0, attributeData_1 = attributeData; _i < attributeData_1.length; _i++) {
                 var attribute = attributeData_1[_i];
                 var attributeKind = attribute.kind;
+                var attributeComponentKind = attribute.accessorComponentType;
                 if (bufferMesh.isVerticesDataPresent(attributeKind)) {
                     var vertexBuffer = this.getVertexBufferFromMesh(attributeKind, bufferMesh);
-                    attribute.byteStride = vertexBuffer ? vertexBuffer.getSize() * 4 : babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].DeduceStride(attributeKind) * 4;
+                    attribute.byteStride = vertexBuffer ? vertexBuffer.getSize() * babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(attribute.accessorComponentType) : babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].DeduceStride(attributeKind) * 4;
                     if (attribute.byteStride === 12) {
                         attribute.accessorType = "VEC3" /* VEC3 */;
                     }
-                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
+                    this.createBufferViewKind(attributeKind, attributeComponentKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
                     attribute.bufferViewIndex = this._bufferViews.length - 1;
                     vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                 }
@@ -2677,7 +2744,7 @@ var _Exporter = /** @class */ (function () {
                     bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                     this._bufferViews.push(bufferView);
                     indexBufferViewIndex = this._bufferViews.length - 1;
-                    for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
+                    for (var k = 0, length_9 = indices.length; k < length_9; ++k) {
                         binaryWriter.setUInt32(indices[k]);
                     }
                 }
@@ -2735,7 +2802,7 @@ var _Exporter = /** @class */ (function () {
                                     if (attributeKind == babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind) {
                                         minMax = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, convertToRightHandedSystem);
                                     }
-                                    var accessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                    var accessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, attribute.accessorComponentType, vertexData.length / stride, 0, minMax.min, minMax.max);
                                     this._accessors.push(accessor);
                                     this.setAttributeKind(meshPrimitive, attributeKind);
                                 }
@@ -2863,59 +2930,67 @@ var _Exporter = /** @class */ (function () {
         });
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, "image/png" /* PNG */, true).then(function () {
             return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then(function (nodeMap) {
-                _this._nodeMap = nodeMap;
-                _this._totalByteLength = binaryWriter.getByteOffset();
-                if (_this._totalByteLength == undefined) {
-                    throw new Error("undefined byte length!");
-                }
-                // Build Hierarchy with the node map.
-                for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
-                    var babylonNode = nodes_1[_i];
-                    glTFNodeIndex = _this._nodeMap[babylonNode.uniqueId];
-                    if (glTFNodeIndex !== undefined) {
-                        glTFNode = _this._nodes[glTFNodeIndex];
-                        if (babylonNode.metadata) {
-                            if (_this._options.metadataSelector) {
-                                glTFNode.extras = _this._options.metadataSelector(babylonNode.metadata);
-                            }
-                            else if (babylonNode.metadata.gltf) {
-                                glTFNode.extras = babylonNode.metadata.gltf.extras;
-                            }
-                        }
-                        if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
-                            if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
-                                babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
+                return _this.createSkinsAsync(babylonScene, nodeMap, binaryWriter).then(function (skinMap) {
+                    _this._nodeMap = nodeMap;
+                    _this._totalByteLength = binaryWriter.getByteOffset();
+                    if (_this._totalByteLength == undefined) {
+                        throw new Error("undefined byte length!");
+                    }
+                    // Build Hierarchy with the node map.
+                    for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
+                        var babylonNode = nodes_1[_i];
+                        glTFNodeIndex = _this._nodeMap[babylonNode.uniqueId];
+                        if (glTFNodeIndex !== undefined) {
+                            glTFNode = _this._nodes[glTFNodeIndex];
+                            if (babylonNode.metadata) {
+                                if (_this._options.metadataSelector) {
+                                    glTFNode.extras = _this._options.metadataSelector(babylonNode.metadata);
+                                }
+                                else if (babylonNode.metadata.gltf) {
+                                    glTFNode.extras = babylonNode.metadata.gltf.extras;
+                                }
                             }
-                            else {
-                                var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
-                                if (convertToRightHandedSystem) {
-                                    if (glTFNode.translation) {
-                                        glTFNode.translation[2] *= -1;
-                                        glTFNode.translation[0] *= -1;
+                            if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
+                                if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
+                                    babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
+                                }
+                                else {
+                                    var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                                    if (convertToRightHandedSystem) {
+                                        if (glTFNode.translation) {
+                                            glTFNode.translation[2] *= -1;
+                                            glTFNode.translation[0] *= -1;
+                                        }
+                                        glTFNode.rotation = glTFNode.rotation ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0]).multiply(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray(glTFNode.rotation)).asArray() : (babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0])).asArray();
                                     }
-                                    glTFNode.rotation = glTFNode.rotation ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0]).multiply(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray(glTFNode.rotation)).asArray() : (babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0])).asArray();
+                                    scene.nodes.push(glTFNodeIndex);
                                 }
-                                scene.nodes.push(glTFNodeIndex);
                             }
-                        }
-                        directDescendents = babylonNode.getDescendants(true);
-                        if (!glTFNode.children && directDescendents && directDescendents.length) {
-                            var children = [];
-                            for (var _a = 0, directDescendents_1 = directDescendents; _a < directDescendents_1.length; _a++) {
-                                var descendent = directDescendents_1[_a];
-                                if (_this._nodeMap[descendent.uniqueId] != null) {
-                                    children.push(_this._nodeMap[descendent.uniqueId]);
+                            if (babylonNode instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Mesh"]) {
+                                var babylonMesh = babylonNode;
+                                if (babylonMesh.skeleton) {
+                                    glTFNode.skin = skinMap[babylonMesh.skeleton.uniqueId];
                                 }
                             }
-                            if (children.length) {
-                                glTFNode.children = children;
+                            directDescendents = babylonNode.getDescendants(true);
+                            if (!glTFNode.children && directDescendents && directDescendents.length) {
+                                var children = [];
+                                for (var _a = 0, directDescendents_1 = directDescendents; _a < directDescendents_1.length; _a++) {
+                                    var descendent = directDescendents_1[_a];
+                                    if (_this._nodeMap[descendent.uniqueId] != null) {
+                                        children.push(_this._nodeMap[descendent.uniqueId]);
+                                    }
+                                }
+                                if (children.length) {
+                                    glTFNode.children = children;
+                                }
                             }
                         }
                     }
-                }
-                if (scene.nodes.length) {
-                    _this._scenes.push(scene);
-                }
+                    if (scene.nodes.length) {
+                        _this._scenes.push(scene);
+                    }
+                });
             });
         });
     };
@@ -3021,6 +3096,63 @@ var _Exporter = /** @class */ (function () {
             }
         });
     };
+    /**
+     * Creates a glTF skin from a Babylon skeleton
+     * @param babylonScene Babylon Scene
+     * @param nodes Babylon transform nodes
+     * @param binaryWriter Buffer to write binary data to
+     * @returns Node mapping of unique id to index
+     */
+    _Exporter.prototype.createSkinsAsync = function (babylonScene, nodeMap, binaryWriter) {
+        var promiseChain = Promise.resolve();
+        var skinMap = {};
+        var _loop_2 = function (skeleton) {
+            // create skin
+            var skin = { joints: [] };
+            var inverseBindMatrices = [];
+            var skeletonMesh = babylonScene.meshes.find(function (mesh) { mesh.skeleton === skeleton; });
+            skin.skeleton = skeleton.overrideMesh === null ? (skeletonMesh ? nodeMap[skeletonMesh.uniqueId] : undefined) : nodeMap[skeleton.overrideMesh.uniqueId];
+            for (var _i = 0, _a = skeleton.bones; _i < _a.length; _i++) {
+                var bone = _a[_i];
+                if (bone._index != -1) {
+                    var transformNode = bone.getTransformNode();
+                    if (transformNode) {
+                        var boneMatrix = bone.getInvertedAbsoluteTransform();
+                        if (this_2._convertToRightHandedSystem) {
+                            _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedMatrixFromRef(boneMatrix);
+                        }
+                        inverseBindMatrices.push(boneMatrix);
+                        skin.joints.push(nodeMap[transformNode.uniqueId]);
+                    }
+                }
+            }
+            // create buffer view for inverse bind matrices
+            var byteStride = 64; // 4 x 4 matrix of 32 bit float
+            var byteLength = inverseBindMatrices.length * byteStride;
+            var bufferViewOffset = binaryWriter.getByteOffset();
+            var bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, bufferViewOffset, byteLength, byteStride, "InverseBindMatrices" + " - " + skeleton.name);
+            this_2._bufferViews.push(bufferView);
+            var bufferViewIndex = this_2._bufferViews.length - 1;
+            var bindMatrixAccessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, "InverseBindMatrices" + " - " + skeleton.name, "MAT4" /* MAT4 */, 5126 /* FLOAT */, inverseBindMatrices.length, null, null, null);
+            var inverseBindAccessorIndex = this_2._accessors.push(bindMatrixAccessor) - 1;
+            skin.inverseBindMatrices = inverseBindAccessorIndex;
+            this_2._skins.push(skin);
+            skinMap[skeleton.uniqueId] = this_2._skins.length - 1;
+            inverseBindMatrices.forEach(function (mat) {
+                mat.m.forEach(function (cell) {
+                    binaryWriter.setFloat32(cell);
+                });
+            });
+        };
+        var this_2 = this;
+        for (var _i = 0, _a = babylonScene.skeletons; _i < _a.length; _i++) {
+            var skeleton = _a[_i];
+            _loop_2(skeleton);
+        }
+        return promiseChain.then(function () {
+            return skinMap;
+        });
+    };
     _Exporter._ExtensionNames = new Array();
     _Exporter._ExtensionFactories = {};
     return _Exporter;
@@ -3049,7 +3181,7 @@ var _BinaryWriter = /** @class */ (function () {
         var newBuffer = new ArrayBuffer(byteLength);
         var oldUint8Array = new Uint8Array(this._arrayBuffer);
         var newUint8Array = new Uint8Array(newBuffer);
-        for (var i = 0, length_8 = newUint8Array.byteLength; i < length_8; ++i) {
+        for (var i = 0, length_10 = newUint8Array.byteLength; i < length_10; ++i) {
             newUint8Array[i] = oldUint8Array[i];
         }
         this._arrayBuffer = newBuffer;
@@ -3091,7 +3223,30 @@ var _BinaryWriter = /** @class */ (function () {
             if (this._byteOffset + 1 > this._arrayBuffer.byteLength) {
                 this.resizeBuffer(this._arrayBuffer.byteLength * 2);
             }
-            this._dataView.setUint8(this._byteOffset++, entry);
+            this._dataView.setUint8(this._byteOffset, entry);
+            this._byteOffset += 1;
+        }
+    };
+    /**
+     * Stores an UInt16 in the array buffer
+     * @param entry
+     * @param byteOffset If defined, specifies where to set the value as an offset.
+     */
+    _BinaryWriter.prototype.setUInt16 = function (entry, byteOffset) {
+        if (byteOffset != null) {
+            if (byteOffset < this._byteOffset) {
+                this._dataView.setUint16(byteOffset, entry, true);
+            }
+            else {
+                babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error('BinaryWriter: byteoffset is greater than the current binary buffer length!');
+            }
+        }
+        else {
+            if (this._byteOffset + 2 > this._arrayBuffer.byteLength) {
+                this.resizeBuffer(this._arrayBuffer.byteLength * 2);
+            }
+            this._dataView.setUint16(this._byteOffset, entry, true);
+            this._byteOffset += 2;
         }
     };
     /**
@@ -4605,6 +4760,10 @@ var _GLTFUtilities = /** @class */ (function () {
             tangent.z /= length;
         }
     };
+    _GLTFUtilities._GetRightHandedMatrixFromRef = function (matrix) {
+        var m = matrix.m;
+        babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Matrix"].FromValuesToRef(m[0], m[1], -m[2], m[3], m[4], m[5], -m[6], m[7], -m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15], matrix);
+    };
     return _GLTFUtilities;
 }());
 

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.js.map


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 22 - 2
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -498,6 +498,7 @@ declare module BABYLON.GLTF2.Exporter {
          */
         static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
         static _NormalizeTangentFromRef(tangent: Vector4): void;
+        static _GetRightHandedMatrixFromRef(matrix: Matrix): void;
     }
 }
 declare module BABYLON.GLTF2.Exporter {
@@ -550,6 +551,10 @@ declare module BABYLON.GLTF2.Exporter {
          */
         _samplers: ISampler[];
         /**
+         * Stores all the generated glTF skins
+         */
+        _skins: ISkin[];
+        /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
         /**
@@ -708,11 +713,11 @@ declare module BABYLON.GLTF2.Exporter {
          * Returns the bytelength of the data
          * @param vertexBufferKind Indicates what kind of vertex data is being passed in
          * @param meshAttributeArray Array containing the attribute data
+         * @param byteStride Specifies the space between data
          * @param binaryWriter The buffer to write the binary data to
-         * @param indices Used to specify the order of the vertex data
          * @param convertToRightHandedSystem Converts the values to right-handed
          */
-        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void;
+        writeAttributeData(vertexBufferKind: string, attributeComponentKind: AccessorComponentType, meshAttributeArray: FloatArray, stride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean, babylonTransformNode: TransformNode): void;
         /**
          * Generates glTF json data
          * @param shouldUseGlb Indicates whether the json should be written for a glb file
@@ -754,6 +759,7 @@ declare module BABYLON.GLTF2.Exporter {
         /**
          * Creates a bufferview based on the vertices type for the Babylon mesh
          * @param kind Indicates the type of vertices data
+         * @param componentType Indicates the numerical type used to store the data
          * @param babylonTransformNode The Babylon mesh to get the vertices data from
          * @param binaryWriter The buffer to write the bufferview data to
          * @param convertToRightHandedSystem Converts the values to right-handed
@@ -815,6 +821,14 @@ declare module BABYLON.GLTF2.Exporter {
          * @returns glTF node
          */
         private createNodeAsync;
+        /**
+         * Creates a glTF skin from a Babylon skeleton
+         * @param babylonScene Babylon Scene
+         * @param nodes Babylon transform nodes
+         * @param binaryWriter Buffer to write binary data to
+         * @returns Node mapping of unique id to index
+         */
+        private createSkinsAsync;
     }
     /**
      * @hidden
@@ -861,6 +875,12 @@ declare module BABYLON.GLTF2.Exporter {
          */
         setUInt8(entry: number, byteOffset?: number): void;
         /**
+         * Stores an UInt16 in the array buffer
+         * @param entry
+         * @param byteOffset If defined, specifies where to set the value as an offset.
+         */
+        setUInt16(entry: number, byteOffset?: number): void;
+        /**
          * Gets an UInt32 in the array buffer
          * @param entry
          * @param byteOffset If defined, specifies where to set the value as an offset.

+ 229 - 70
dist/preview release/serializers/babylonjs.serializers.js

@@ -97,9 +97,9 @@ return /******/ (function(modules) { // webpackBootstrap
 /******/ ({
 
 /***/ "../../node_modules/tslib/tslib.es6.js":
-/*!***********************************************************!*\
-  !*** D:/Repos/Babylon.js/node_modules/tslib/tslib.es6.js ***!
-  \***********************************************************/
+/*!*****************************************************************!*\
+  !*** C:/Dev/Babylon/Babylon.js/node_modules/tslib/tslib.es6.js ***!
+  \*****************************************************************/
 /*! exports provided: __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
@@ -1735,10 +1735,10 @@ var _GLTFAnimation = /** @class */ (function () {
             if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                 if (tangentValue) {
                     if (useQuaternion) {
-                        tangent = tangentValue.scale(frameDelta).asArray();
+                        tangent = tangentValue.asArray();
                     }
                     else {
-                        var array = tangentValue.scale(frameDelta);
+                        var array = tangentValue;
                         tangent = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                     }
                     if (convertToRightHandedSystem) {
@@ -1754,7 +1754,7 @@ var _GLTFAnimation = /** @class */ (function () {
             }
             else {
                 if (tangentValue) {
-                    tangent = tangentValue.scale(frameDelta).asArray();
+                    tangent = tangentValue.asArray();
                     if (convertToRightHandedSystem) {
                         if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             _glTFUtilities__WEBPACK_IMPORTED_MODULE_1__["_GLTFUtilities"]._GetRightHandedPositionArray3FromRef(tangent);
@@ -1925,6 +1925,7 @@ var _Exporter = /** @class */ (function () {
         this._materialMap = [];
         this._textures = [];
         this._samplers = [];
+        this._skins = [];
         this._animations = [];
         this._imageData = {};
         this._options = options || {};
@@ -2384,12 +2385,11 @@ var _Exporter = /** @class */ (function () {
      * Returns the bytelength of the data
      * @param vertexBufferKind Indicates what kind of vertex data is being passed in
      * @param meshAttributeArray Array containing the attribute data
+     * @param byteStride Specifies the space between data
      * @param binaryWriter The buffer to write the binary data to
-     * @param indices Used to specify the order of the vertex data
      * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, byteStride, binaryWriter, convertToRightHandedSystem) {
-        var stride = byteStride / 4;
+    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, attributeComponentKind, meshAttributeArray, stride, binaryWriter, convertToRightHandedSystem, babylonTransformNode) {
         var vertexAttributes = [];
         var index;
         switch (vertexBufferKind) {
@@ -2444,16 +2444,56 @@ var _Exporter = /** @class */ (function () {
                 }
                 break;
             }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesKind:
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesExtraKind: {
+                for (var k = 0, length_7 = meshAttributeArray.length / stride; k < length_7; ++k) {
+                    index = k * stride;
+                    var vertexData = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
+                    vertexAttributes.push(vertexData.asArray());
+                }
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsKind:
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsExtraKind: {
+                for (var k = 0, length_8 = meshAttributeArray.length / stride; k < length_8; ++k) {
+                    index = k * stride;
+                    var vertexData = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
+                    vertexAttributes.push(vertexData.asArray());
+                }
+                break;
+            }
             default: {
                 babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Unsupported Vertex Buffer Type: " + vertexBufferKind);
                 vertexAttributes = [];
             }
         }
+        var writeBinaryFunc;
+        switch (attributeComponentKind) {
+            case 5121 /* UNSIGNED_BYTE */: {
+                writeBinaryFunc = binaryWriter.setUInt8.bind(binaryWriter);
+                break;
+            }
+            case 5123 /* UNSIGNED_SHORT */: {
+                writeBinaryFunc = binaryWriter.setUInt16.bind(binaryWriter);
+                break;
+            }
+            case 5125 /* UNSIGNED_INT */: {
+                writeBinaryFunc = binaryWriter.setUInt32.bind(binaryWriter);
+            }
+            case 5126 /* FLOAT */: {
+                writeBinaryFunc = binaryWriter.setFloat32.bind(binaryWriter);
+                break;
+            }
+            default: {
+                babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Unsupported Attribute Component kind: " + attributeComponentKind);
+                return;
+            }
+        }
         for (var _i = 0, vertexAttributes_1 = vertexAttributes; _i < vertexAttributes_1.length; _i++) {
             var vertexAttribute = vertexAttributes_1[_i];
             for (var _a = 0, vertexAttribute_1 = vertexAttribute; _a < vertexAttribute_1.length; _a++) {
                 var component = vertexAttribute_1[_a];
-                binaryWriter.setFloat32(component);
+                writeBinaryFunc(component);
             }
         }
     };
@@ -2502,6 +2542,9 @@ var _Exporter = /** @class */ (function () {
         if (this._samplers && this._samplers.length) {
             this._glTF.samplers = this._samplers;
         }
+        if (this._skins && this._skins.length) {
+            this._glTF.skins = this._skins;
+        }
         if (this._images && this._images.length) {
             if (!shouldUseGlb) {
                 this._glTF.images = this._images;
@@ -2704,21 +2747,24 @@ var _Exporter = /** @class */ (function () {
     /**
      * Creates a bufferview based on the vertices type for the Babylon mesh
      * @param kind Indicates the type of vertices data
+     * @param componentType Indicates the numerical type used to store the data
      * @param babylonTransformNode The Babylon mesh to get the vertices data from
      * @param binaryWriter The buffer to write the bufferview data to
      * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.createBufferViewKind = function (kind, babylonTransformNode, binaryWriter, byteStride, convertToRightHandedSystem) {
+    _Exporter.prototype.createBufferViewKind = function (kind, attributeComponentKind, babylonTransformNode, binaryWriter, byteStride, convertToRightHandedSystem) {
         var bufferMesh = babylonTransformNode instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Mesh"] ?
             babylonTransformNode : babylonTransformNode instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["InstancedMesh"] ?
             babylonTransformNode.sourceMesh : null;
         if (bufferMesh) {
+            var vertexBuffer = bufferMesh.getVertexBuffer(kind);
             var vertexData = bufferMesh.getVerticesData(kind);
-            if (vertexData) {
-                var byteLength = vertexData.length * 4;
+            if (vertexBuffer && vertexData) {
+                var typeByteLength = babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(attributeComponentKind);
+                var byteLength = vertexData.length * typeByteLength;
                 var bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                 this._bufferViews.push(bufferView);
-                this.writeAttributeData(kind, vertexData, byteStride, binaryWriter, convertToRightHandedSystem);
+                this.writeAttributeData(kind, attributeComponentKind, vertexData, byteStride / typeByteLength, binaryWriter, convertToRightHandedSystem, babylonTransformNode);
             }
         }
     };
@@ -2804,6 +2850,22 @@ var _Exporter = /** @class */ (function () {
                 meshPrimitive.attributes.TEXCOORD_1 = this._accessors.length - 1;
                 break;
             }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesKind: {
+                meshPrimitive.attributes.JOINTS_0 = this._accessors.length - 1;
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesExtraKind: {
+                meshPrimitive.attributes.JOINTS_1 = this._accessors.length - 1;
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsKind: {
+                meshPrimitive.attributes.WEIGHTS_0 = this._accessors.length - 1;
+                break;
+            }
+            case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsExtraKind: {
+                meshPrimitive.attributes.WEIGHTS_1 = this._accessors.length - 1;
+                break;
+            }
             default: {
                 babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Unsupported Vertex Buffer Type: " + attributeKind);
             }
@@ -2829,12 +2891,16 @@ var _Exporter = /** @class */ (function () {
             bufferMesh = babylonTransformNode.sourceMesh;
         }
         var attributeData = [
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind, accessorType: "VEC3" /* VEC3 */, byteStride: 12 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].NormalKind, accessorType: "VEC3" /* VEC3 */, byteStride: 12 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind, accessorType: "VEC4" /* VEC4 */, byteStride: 16 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].TangentKind, accessorType: "VEC4" /* VEC4 */, byteStride: 16 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UVKind, accessorType: "VEC2" /* VEC2 */, byteStride: 8 },
-            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind, accessorType: "VEC2" /* VEC2 */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind, accessorType: "VEC3" /* VEC3 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 12 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].NormalKind, accessorType: "VEC3" /* VEC3 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 12 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].TangentKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UVKind, accessorType: "VEC2" /* VEC2 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind, accessorType: "VEC2" /* VEC2 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5123 /* UNSIGNED_SHORT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesIndicesExtraKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5123 /* UNSIGNED_SHORT */, byteStride: 8 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
+            { kind: babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].MatricesWeightsExtraKind, accessorType: "VEC4" /* VEC4 */, accessorComponentType: 5126 /* FLOAT */, byteStride: 16 },
         ];
         if (bufferMesh) {
             var indexBufferViewIndex = null;
@@ -2844,13 +2910,14 @@ var _Exporter = /** @class */ (function () {
             for (var _i = 0, attributeData_1 = attributeData; _i < attributeData_1.length; _i++) {
                 var attribute = attributeData_1[_i];
                 var attributeKind = attribute.kind;
+                var attributeComponentKind = attribute.accessorComponentType;
                 if (bufferMesh.isVerticesDataPresent(attributeKind)) {
                     var vertexBuffer = this.getVertexBufferFromMesh(attributeKind, bufferMesh);
-                    attribute.byteStride = vertexBuffer ? vertexBuffer.getSize() * 4 : babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].DeduceStride(attributeKind) * 4;
+                    attribute.byteStride = vertexBuffer ? vertexBuffer.getSize() * babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(attribute.accessorComponentType) : babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].DeduceStride(attributeKind) * 4;
                     if (attribute.byteStride === 12) {
                         attribute.accessorType = "VEC3" /* VEC3 */;
                     }
-                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
+                    this.createBufferViewKind(attributeKind, attributeComponentKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
                     attribute.bufferViewIndex = this._bufferViews.length - 1;
                     vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                 }
@@ -2862,7 +2929,7 @@ var _Exporter = /** @class */ (function () {
                     bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                     this._bufferViews.push(bufferView);
                     indexBufferViewIndex = this._bufferViews.length - 1;
-                    for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
+                    for (var k = 0, length_9 = indices.length; k < length_9; ++k) {
                         binaryWriter.setUInt32(indices[k]);
                     }
                 }
@@ -2920,7 +2987,7 @@ var _Exporter = /** @class */ (function () {
                                     if (attributeKind == babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind) {
                                         minMax = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, convertToRightHandedSystem);
                                     }
-                                    var accessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                    var accessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, attribute.accessorComponentType, vertexData.length / stride, 0, minMax.min, minMax.max);
                                     this._accessors.push(accessor);
                                     this.setAttributeKind(meshPrimitive, attributeKind);
                                 }
@@ -3048,59 +3115,67 @@ var _Exporter = /** @class */ (function () {
         });
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, "image/png" /* PNG */, true).then(function () {
             return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then(function (nodeMap) {
-                _this._nodeMap = nodeMap;
-                _this._totalByteLength = binaryWriter.getByteOffset();
-                if (_this._totalByteLength == undefined) {
-                    throw new Error("undefined byte length!");
-                }
-                // Build Hierarchy with the node map.
-                for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
-                    var babylonNode = nodes_1[_i];
-                    glTFNodeIndex = _this._nodeMap[babylonNode.uniqueId];
-                    if (glTFNodeIndex !== undefined) {
-                        glTFNode = _this._nodes[glTFNodeIndex];
-                        if (babylonNode.metadata) {
-                            if (_this._options.metadataSelector) {
-                                glTFNode.extras = _this._options.metadataSelector(babylonNode.metadata);
-                            }
-                            else if (babylonNode.metadata.gltf) {
-                                glTFNode.extras = babylonNode.metadata.gltf.extras;
-                            }
-                        }
-                        if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
-                            if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
-                                babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
+                return _this.createSkinsAsync(babylonScene, nodeMap, binaryWriter).then(function (skinMap) {
+                    _this._nodeMap = nodeMap;
+                    _this._totalByteLength = binaryWriter.getByteOffset();
+                    if (_this._totalByteLength == undefined) {
+                        throw new Error("undefined byte length!");
+                    }
+                    // Build Hierarchy with the node map.
+                    for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
+                        var babylonNode = nodes_1[_i];
+                        glTFNodeIndex = _this._nodeMap[babylonNode.uniqueId];
+                        if (glTFNodeIndex !== undefined) {
+                            glTFNode = _this._nodes[glTFNodeIndex];
+                            if (babylonNode.metadata) {
+                                if (_this._options.metadataSelector) {
+                                    glTFNode.extras = _this._options.metadataSelector(babylonNode.metadata);
+                                }
+                                else if (babylonNode.metadata.gltf) {
+                                    glTFNode.extras = babylonNode.metadata.gltf.extras;
+                                }
                             }
-                            else {
-                                var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
-                                if (convertToRightHandedSystem) {
-                                    if (glTFNode.translation) {
-                                        glTFNode.translation[2] *= -1;
-                                        glTFNode.translation[0] *= -1;
+                            if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
+                                if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
+                                    babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
+                                }
+                                else {
+                                    var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                                    if (convertToRightHandedSystem) {
+                                        if (glTFNode.translation) {
+                                            glTFNode.translation[2] *= -1;
+                                            glTFNode.translation[0] *= -1;
+                                        }
+                                        glTFNode.rotation = glTFNode.rotation ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0]).multiply(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray(glTFNode.rotation)).asArray() : (babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0])).asArray();
                                     }
-                                    glTFNode.rotation = glTFNode.rotation ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0]).multiply(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray(glTFNode.rotation)).asArray() : (babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].FromArray([0, 1, 0, 0])).asArray();
+                                    scene.nodes.push(glTFNodeIndex);
                                 }
-                                scene.nodes.push(glTFNodeIndex);
                             }
-                        }
-                        directDescendents = babylonNode.getDescendants(true);
-                        if (!glTFNode.children && directDescendents && directDescendents.length) {
-                            var children = [];
-                            for (var _a = 0, directDescendents_1 = directDescendents; _a < directDescendents_1.length; _a++) {
-                                var descendent = directDescendents_1[_a];
-                                if (_this._nodeMap[descendent.uniqueId] != null) {
-                                    children.push(_this._nodeMap[descendent.uniqueId]);
+                            if (babylonNode instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Mesh"]) {
+                                var babylonMesh = babylonNode;
+                                if (babylonMesh.skeleton) {
+                                    glTFNode.skin = skinMap[babylonMesh.skeleton.uniqueId];
                                 }
                             }
-                            if (children.length) {
-                                glTFNode.children = children;
+                            directDescendents = babylonNode.getDescendants(true);
+                            if (!glTFNode.children && directDescendents && directDescendents.length) {
+                                var children = [];
+                                for (var _a = 0, directDescendents_1 = directDescendents; _a < directDescendents_1.length; _a++) {
+                                    var descendent = directDescendents_1[_a];
+                                    if (_this._nodeMap[descendent.uniqueId] != null) {
+                                        children.push(_this._nodeMap[descendent.uniqueId]);
+                                    }
+                                }
+                                if (children.length) {
+                                    glTFNode.children = children;
+                                }
                             }
                         }
                     }
-                }
-                if (scene.nodes.length) {
-                    _this._scenes.push(scene);
-                }
+                    if (scene.nodes.length) {
+                        _this._scenes.push(scene);
+                    }
+                });
             });
         });
     };
@@ -3206,6 +3281,63 @@ var _Exporter = /** @class */ (function () {
             }
         });
     };
+    /**
+     * Creates a glTF skin from a Babylon skeleton
+     * @param babylonScene Babylon Scene
+     * @param nodes Babylon transform nodes
+     * @param binaryWriter Buffer to write binary data to
+     * @returns Node mapping of unique id to index
+     */
+    _Exporter.prototype.createSkinsAsync = function (babylonScene, nodeMap, binaryWriter) {
+        var promiseChain = Promise.resolve();
+        var skinMap = {};
+        var _loop_2 = function (skeleton) {
+            // create skin
+            var skin = { joints: [] };
+            var inverseBindMatrices = [];
+            var skeletonMesh = babylonScene.meshes.find(function (mesh) { mesh.skeleton === skeleton; });
+            skin.skeleton = skeleton.overrideMesh === null ? (skeletonMesh ? nodeMap[skeletonMesh.uniqueId] : undefined) : nodeMap[skeleton.overrideMesh.uniqueId];
+            for (var _i = 0, _a = skeleton.bones; _i < _a.length; _i++) {
+                var bone = _a[_i];
+                if (bone._index != -1) {
+                    var transformNode = bone.getTransformNode();
+                    if (transformNode) {
+                        var boneMatrix = bone.getInvertedAbsoluteTransform();
+                        if (this_2._convertToRightHandedSystem) {
+                            _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedMatrixFromRef(boneMatrix);
+                        }
+                        inverseBindMatrices.push(boneMatrix);
+                        skin.joints.push(nodeMap[transformNode.uniqueId]);
+                    }
+                }
+            }
+            // create buffer view for inverse bind matrices
+            var byteStride = 64; // 4 x 4 matrix of 32 bit float
+            var byteLength = inverseBindMatrices.length * byteStride;
+            var bufferViewOffset = binaryWriter.getByteOffset();
+            var bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, bufferViewOffset, byteLength, byteStride, "InverseBindMatrices" + " - " + skeleton.name);
+            this_2._bufferViews.push(bufferView);
+            var bufferViewIndex = this_2._bufferViews.length - 1;
+            var bindMatrixAccessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, "InverseBindMatrices" + " - " + skeleton.name, "MAT4" /* MAT4 */, 5126 /* FLOAT */, inverseBindMatrices.length, null, null, null);
+            var inverseBindAccessorIndex = this_2._accessors.push(bindMatrixAccessor) - 1;
+            skin.inverseBindMatrices = inverseBindAccessorIndex;
+            this_2._skins.push(skin);
+            skinMap[skeleton.uniqueId] = this_2._skins.length - 1;
+            inverseBindMatrices.forEach(function (mat) {
+                mat.m.forEach(function (cell) {
+                    binaryWriter.setFloat32(cell);
+                });
+            });
+        };
+        var this_2 = this;
+        for (var _i = 0, _a = babylonScene.skeletons; _i < _a.length; _i++) {
+            var skeleton = _a[_i];
+            _loop_2(skeleton);
+        }
+        return promiseChain.then(function () {
+            return skinMap;
+        });
+    };
     _Exporter._ExtensionNames = new Array();
     _Exporter._ExtensionFactories = {};
     return _Exporter;
@@ -3234,7 +3366,7 @@ var _BinaryWriter = /** @class */ (function () {
         var newBuffer = new ArrayBuffer(byteLength);
         var oldUint8Array = new Uint8Array(this._arrayBuffer);
         var newUint8Array = new Uint8Array(newBuffer);
-        for (var i = 0, length_8 = newUint8Array.byteLength; i < length_8; ++i) {
+        for (var i = 0, length_10 = newUint8Array.byteLength; i < length_10; ++i) {
             newUint8Array[i] = oldUint8Array[i];
         }
         this._arrayBuffer = newBuffer;
@@ -3276,7 +3408,30 @@ var _BinaryWriter = /** @class */ (function () {
             if (this._byteOffset + 1 > this._arrayBuffer.byteLength) {
                 this.resizeBuffer(this._arrayBuffer.byteLength * 2);
             }
-            this._dataView.setUint8(this._byteOffset++, entry);
+            this._dataView.setUint8(this._byteOffset, entry);
+            this._byteOffset += 1;
+        }
+    };
+    /**
+     * Stores an UInt16 in the array buffer
+     * @param entry
+     * @param byteOffset If defined, specifies where to set the value as an offset.
+     */
+    _BinaryWriter.prototype.setUInt16 = function (entry, byteOffset) {
+        if (byteOffset != null) {
+            if (byteOffset < this._byteOffset) {
+                this._dataView.setUint16(byteOffset, entry, true);
+            }
+            else {
+                babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error('BinaryWriter: byteoffset is greater than the current binary buffer length!');
+            }
+        }
+        else {
+            if (this._byteOffset + 2 > this._arrayBuffer.byteLength) {
+                this.resizeBuffer(this._arrayBuffer.byteLength * 2);
+            }
+            this._dataView.setUint16(this._byteOffset, entry, true);
+            this._byteOffset += 2;
         }
     };
     /**
@@ -4790,6 +4945,10 @@ var _GLTFUtilities = /** @class */ (function () {
             tangent.z /= length;
         }
     };
+    _GLTFUtilities._GetRightHandedMatrixFromRef = function (matrix) {
+        var m = matrix.m;
+        babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Matrix"].FromValuesToRef(m[0], m[1], -m[2], m[3], m[4], m[5], -m[6], m[7], -m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15], matrix);
+    };
     return _GLTFUtilities;
 }());
 

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.js.map


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 47 - 6
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -434,7 +434,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFSerializer" {
 declare module "babylonjs-serializers/glTF/2.0/glTFUtilities" {
     import { IBufferView, AccessorType, AccessorComponentType, IAccessor } from "babylonjs-gltf2interface";
     import { FloatArray, Nullable } from "babylonjs/types";
-    import { Vector3, Vector4, Quaternion } from "babylonjs/Maths/math.vector";
+    import { Vector3, Vector4, Quaternion, Matrix } from "babylonjs/Maths/math.vector";
     /**
      * @hidden
      */
@@ -526,13 +526,15 @@ declare module "babylonjs-serializers/glTF/2.0/glTFUtilities" {
          */
         static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
         static _NormalizeTangentFromRef(tangent: Vector4): void;
+        static _GetRightHandedMatrixFromRef(matrix: Matrix): void;
     }
 }
 declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
-    import { IBufferView, IAccessor, INode, IMaterial, ITexture, IImage, ISampler, ImageMimeType, IMeshPrimitive, IGLTF, ITextureInfo } from "babylonjs-gltf2interface";
+    import { IBufferView, IAccessor, INode, IMaterial, ITexture, IImage, ISampler, ImageMimeType, IMeshPrimitive, IGLTF, AccessorComponentType, ITextureInfo, ISkin } from "babylonjs-gltf2interface";
     import { FloatArray, Nullable } from "babylonjs/types";
     import { Vector3, Vector4 } from "babylonjs/Maths/math.vector";
     import { Node } from "babylonjs/node";
+    import { TransformNode } from "babylonjs/Meshes/transformNode";
     import { SubMesh } from "babylonjs/Meshes/subMesh";
     import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
     import { Texture } from "babylonjs/Materials/Textures/texture";
@@ -592,6 +594,10 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          */
         _samplers: ISampler[];
         /**
+         * Stores all the generated glTF skins
+         */
+        _skins: ISkin[];
+        /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
         /**
@@ -750,11 +756,11 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * Returns the bytelength of the data
          * @param vertexBufferKind Indicates what kind of vertex data is being passed in
          * @param meshAttributeArray Array containing the attribute data
+         * @param byteStride Specifies the space between data
          * @param binaryWriter The buffer to write the binary data to
-         * @param indices Used to specify the order of the vertex data
          * @param convertToRightHandedSystem Converts the values to right-handed
          */
-        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void;
+        writeAttributeData(vertexBufferKind: string, attributeComponentKind: AccessorComponentType, meshAttributeArray: FloatArray, stride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean, babylonTransformNode: TransformNode): void;
         /**
          * Generates glTF json data
          * @param shouldUseGlb Indicates whether the json should be written for a glb file
@@ -796,6 +802,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
         /**
          * Creates a bufferview based on the vertices type for the Babylon mesh
          * @param kind Indicates the type of vertices data
+         * @param componentType Indicates the numerical type used to store the data
          * @param babylonTransformNode The Babylon mesh to get the vertices data from
          * @param binaryWriter The buffer to write the bufferview data to
          * @param convertToRightHandedSystem Converts the values to right-handed
@@ -857,6 +864,14 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @returns glTF node
          */
         private createNodeAsync;
+        /**
+         * Creates a glTF skin from a Babylon skeleton
+         * @param babylonScene Babylon Scene
+         * @param nodes Babylon transform nodes
+         * @param binaryWriter Buffer to write binary data to
+         * @returns Node mapping of unique id to index
+         */
+        private createSkinsAsync;
     }
     /**
      * @hidden
@@ -903,6 +918,12 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          */
         setUInt8(entry: number, byteOffset?: number): void;
         /**
+         * Stores an UInt16 in the array buffer
+         * @param entry
+         * @param byteOffset If defined, specifies where to set the value as an offset.
+         */
+        setUInt16(entry: number, byteOffset?: number): void;
+        /**
          * Gets an UInt32 in the array buffer
          * @param entry
          * @param byteOffset If defined, specifies where to set the value as an offset.
@@ -1812,6 +1833,7 @@ declare module BABYLON.GLTF2.Exporter {
          */
         static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
         static _NormalizeTangentFromRef(tangent: Vector4): void;
+        static _GetRightHandedMatrixFromRef(matrix: Matrix): void;
     }
 }
 declare module BABYLON.GLTF2.Exporter {
@@ -1864,6 +1886,10 @@ declare module BABYLON.GLTF2.Exporter {
          */
         _samplers: ISampler[];
         /**
+         * Stores all the generated glTF skins
+         */
+        _skins: ISkin[];
+        /**
          * Stores all the generated animation samplers, which is referenced by glTF animations
          */
         /**
@@ -2022,11 +2048,11 @@ declare module BABYLON.GLTF2.Exporter {
          * Returns the bytelength of the data
          * @param vertexBufferKind Indicates what kind of vertex data is being passed in
          * @param meshAttributeArray Array containing the attribute data
+         * @param byteStride Specifies the space between data
          * @param binaryWriter The buffer to write the binary data to
-         * @param indices Used to specify the order of the vertex data
          * @param convertToRightHandedSystem Converts the values to right-handed
          */
-        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void;
+        writeAttributeData(vertexBufferKind: string, attributeComponentKind: AccessorComponentType, meshAttributeArray: FloatArray, stride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean, babylonTransformNode: TransformNode): void;
         /**
          * Generates glTF json data
          * @param shouldUseGlb Indicates whether the json should be written for a glb file
@@ -2068,6 +2094,7 @@ declare module BABYLON.GLTF2.Exporter {
         /**
          * Creates a bufferview based on the vertices type for the Babylon mesh
          * @param kind Indicates the type of vertices data
+         * @param componentType Indicates the numerical type used to store the data
          * @param babylonTransformNode The Babylon mesh to get the vertices data from
          * @param binaryWriter The buffer to write the bufferview data to
          * @param convertToRightHandedSystem Converts the values to right-handed
@@ -2129,6 +2156,14 @@ declare module BABYLON.GLTF2.Exporter {
          * @returns glTF node
          */
         private createNodeAsync;
+        /**
+         * Creates a glTF skin from a Babylon skeleton
+         * @param babylonScene Babylon Scene
+         * @param nodes Babylon transform nodes
+         * @param binaryWriter Buffer to write binary data to
+         * @returns Node mapping of unique id to index
+         */
+        private createSkinsAsync;
     }
     /**
      * @hidden
@@ -2175,6 +2210,12 @@ declare module BABYLON.GLTF2.Exporter {
          */
         setUInt8(entry: number, byteOffset?: number): void;
         /**
+         * Stores an UInt16 in the array buffer
+         * @param entry
+         * @param byteOffset If defined, specifies where to set the value as an offset.
+         */
+        setUInt16(entry: number, byteOffset?: number): void;
+        /**
          * Gets an UInt32 in the array buffer
          * @param entry
          * @param byteOffset If defined, specifies where to set the value as an offset.

+ 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.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.23",
-        "babylonjs-gltf2interface": "4.2.0-alpha.23"
+        "babylonjs": "4.2.0-alpha.25",
+        "babylonjs-gltf2interface": "4.2.0-alpha.25"
     },
     "engines": {
         "node": "*"

文件差異過大導致無法顯示
+ 898 - 511
dist/preview release/viewer/babylon.module.d.ts


+ 12 - 1
dist/preview release/what's new.md

@@ -19,6 +19,7 @@
 
 - Refactored React refs from old string API to React.createRef() API ([belfortk](https://github.com/belfortk))
 - Scale on one axis for `BoundingBoxGizmo` ([cedricguillemet](https://github.com/cedricguillemet))
+- Node support (Transform, Bone) for gizmos ([cedricguillemet](https://github.com/cedricguillemet))
 - Simplified code contributions by fully automating the dev setup with gitpod ([nisarhassan12](https://github.com/nisarhassan12))
 - Add a `CascadedShadowMap.IsSupported` method and log an error instead of throwing an exception when CSM is not supported ([Popov72](https://github.com/Popov72))
 - Added initial code for DeviceInputSystem ([PolygonalSun](https://github.com/PolygonalSun))
@@ -29,6 +30,9 @@
 - Added initial code for user facing DeviceSourceManager ([PolygonalSun](https://github.com/PolygonalSun))
 - Added a Simple and advanced timer, based on observables ([RaananW](https://github.com/RaananW))
 - Don't log a message in `CustomProceduralTexture` if the `texturePath` is not a JSON path ([Popov72](https://github.com/Popov72))
+- Added an alternate option to the mesh edge renderer to generate edges faster / more accurately for unusual geometries (like the ones generated by CSG) ([Popov72](https://github.com/Popov72))
+- Added an option when creating the engine to switch matrix computation to 64 bits ([Popov72](https://github.com/Popov72))
+- Added support for the alpha component to the SSR post-process ([Popov72](https://github.com/Popov72))
 
 ### Engine
 
@@ -67,7 +71,7 @@
 - Added support for recording GIF ([Deltakosh](https://github.com/deltakosh))
 - Popup Window available (To be used in Curve Editor) ([pixelspace](https://github.com/devpixelspace))
 - Add support to update inspector when switching to a new scene ([belfortk](https://github.com/belfortk))
-- View textures in pop out window with zoom & pan ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
+- View textures in pop out window with zoom & pan and individual channels. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
 
 ### Cameras
 
@@ -104,10 +108,12 @@
 - Use min/max values from position accessors (when available) to set the bounding box of meshes ([Popov72](https://github.com/Popov72))
 - Added missing "pluginExtension" parameter to SceneLoader.ImportAnimations. ([phenry20](https://github.com/phenry20))
 - Added support for .glb file loading through a base64 encoded filename ([Popov72](https://github.com/Popov72))
+- Fixed issue with loading screen hiding too early when loading multiple assets concurrently. ([bghgary](https://github.com/bghgary))
 
 ### Serializers
 
 - Added support for KHR_materials_unlit to glTF serializer ([Popov72](https://github.com/Popov72))
+- Added support for glTF Skins to glTF serializer ([Drigax](https://github.com/Drigax))
 
 ### Navigation
 
@@ -186,6 +192,10 @@
 
 - Added support for custom word splitting function for `TextBlock` ([Popov72](https://github.com/Popov72))
 
+### Post Processes
+
+- SSAO 2 is now using the brand new `PrePassRenderer` to avoid rendering the scene twice ([CraigFeldpsar](https://github.com/craigfeldspar)
+
 ## Bugs
 
 - Fix infinite loop in `GlowLayer.unReferenceMeshFromUsingItsOwnMaterial` ([Popov72](https://github.com/Popov72))
@@ -247,6 +257,7 @@
 - Fix crash when loading a .obj file with vertex colors ([Popov72](https://github.com/Popov72))
 - Fix skeleton viewer still visible when `isEnabled = false` ([Popov72](https://github.com/Popov72))
 - Fix crash with CSG when no uvs defined ([Popov72](https://github.com/Popov72))
+- Fix an issue causing views to render blank when scene rendering is skipped for a given iteration of the render loop ([elInfidel](https://github.com/elInfidel))
 
 ## Breaking changes
 

+ 2 - 1
inspector/src/components/actionTabs/lines/quaternionLineComponent.tsx

@@ -27,11 +27,12 @@ export class QuaternionLineComponent extends React.Component<IQuaternionLineComp
         this.state = { isExpanded: false, value: quat, eulerValue: quat.toEulerAngles() }
     }
 
-    shouldComponentUpdate(nextProps: IQuaternionLineComponentProps, nextState: { isExpanded: boolean, value: Quaternion }) {
+    shouldComponentUpdate(nextProps: IQuaternionLineComponentProps, nextState: { isExpanded: boolean, value: Quaternion, eulerValue: Vector3 }) {
         const nextPropsValue = nextProps.target[nextProps.propertyName];
 
         if (!nextPropsValue.equals(nextState.value) || this._localChange) {
             nextState.value = nextPropsValue.clone();
+            nextState.eulerValue = nextPropsValue.toEulerAngles();
             this._localChange = false;
             return true;
         }

+ 2 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/animations/addAnimation.tsx

@@ -376,15 +376,14 @@ export class AddAnimation extends React.Component<
                 onChange={(e) => this.handleTypeChange(e)}
                 value={this.state.animationType}
               >
+                <option value={Animation.ANIMATIONTYPE_COLOR3}>Color3</option>
+                <option value={Animation.ANIMATIONTYPE_COLOR4}>Color4</option>
                 <option value={Animation.ANIMATIONTYPE_FLOAT}>Float</option>
                 <option value={Animation.ANIMATIONTYPE_VECTOR3}>Vector3</option>
                 <option value={Animation.ANIMATIONTYPE_VECTOR2}>Vector2</option>
                 <option value={Animation.ANIMATIONTYPE_QUATERNION}>
                   Quaternion
                 </option>
-                <option value={Animation.ANIMATIONTYPE_COLOR3}>Color3</option>
-                <option value={Animation.ANIMATIONTYPE_COLOR4}>Color4</option>
-                <option value={Animation.ANIMATIONTYPE_SIZE}>Size</option>
               </select>
             </div>
           )}

+ 278 - 109
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -37,6 +37,11 @@ interface ICanvasAxis {
   label: number;
 }
 
+export interface IActionableKeyFrame {
+  frame?: number | string;
+  value?: any;
+}
+
 interface ICurveData {
   pathData: string;
   pathLength: number;
@@ -73,6 +78,7 @@ export class AnimationCurveEditorComponent extends React.Component<
     panningY: number;
     panningX: number;
     repositionCanvas: boolean;
+    actionableKeyframe: IActionableKeyFrame;
   }
 > {
   private _snippetUrl = 'https://snippet.babylonjs.com';
@@ -84,6 +90,7 @@ export class AnimationCurveEditorComponent extends React.Component<
   private _svgKeyframes: IKeyframeSvgPoint[] = [];
   private _isPlaying: boolean = false;
   private _graphCanvas: React.RefObject<HTMLDivElement>;
+
   //private _selectedCurve: React.RefObject<SVGPathElement>;
   private _svgCanvas: React.RefObject<SvgDraggableArea>;
   private _isTargetedAnimation: boolean;
@@ -95,9 +102,8 @@ export class AnimationCurveEditorComponent extends React.Component<
   constructor(props: IAnimationCurveEditorComponentProps) {
     super(props);
     this._entityName = (this.props.entity as any).id;
-    // Review is we really need this refs
+
     this._graphCanvas = React.createRef();
-    //this._selectedCurve = React.createRef();
     this._svgCanvas = React.createRef();
 
     this._pixelFrameUnit = 10;
@@ -177,6 +183,7 @@ export class AnimationCurveEditorComponent extends React.Component<
       panningY: 0,
       panningX: 0,
       repositionCanvas: false,
+      actionableKeyframe: { frame: undefined, value: undefined },
     };
   }
 
@@ -289,24 +296,59 @@ export class AnimationCurveEditorComponent extends React.Component<
     }
   }
 
+  encodeCurveId(animationName: string, coordinate: number) {
+    return animationName + '_' + coordinate;
+  }
+
+  decodeCurveId(id: string) {
+    const order = parseInt(id.split('_')[3]);
+    const coordinate = parseInt(id.split('_')[2]);
+    return { order, coordinate };
+  }
+
+  getKeyframeValueFromAnimation(id: string) {
+    const animation = this.state.selected as Animation;
+    const { order, coordinate } = this.decodeCurveId(id);
+    const keys = [...animation.getKeys()];
+
+    const key = keys.find((_, i) => i === order);
+
+    if (key) {
+      const valueAsArray = this.getValueAsArray(animation.dataType, key.value);
+      return { frame: key?.frame, value: valueAsArray[coordinate] };
+    } else {
+      return undefined;
+    }
+  }
+
   /**
    * Keyframe Manipulation
    * This section handles events from SvgDraggableArea.
    */
   selectKeyframe(id: string, multiselect: boolean) {
-    let selectedKeyFrame = this.state.svgKeyframes?.find((kf) => kf.id === id)
+    const frameValue = this.getKeyframeValueFromAnimation(id);
+    const selectedKeyFrame = this.state.svgKeyframes?.find((kf) => kf.id === id)
       ?.selected;
     if (!multiselect) {
       this.deselectKeyframes();
     }
 
-    let updatedKeyframes = this.state.svgKeyframes?.map((kf) => {
+    const updatedKeyframes = this.state.svgKeyframes?.map((kf) => {
       if (kf.id === id) {
         kf.selected = !selectedKeyFrame;
       }
       return kf;
     });
-    this.setState({ svgKeyframes: updatedKeyframes });
+    this.setState({
+      svgKeyframes: updatedKeyframes,
+      actionableKeyframe: frameValue ?? this.state.actionableKeyframe,
+    });
+  }
+
+  resetActionableKeyframe() {
+    this.setState({
+      actionableKeyframe: { frame: undefined, value: undefined },
+    });
   }
 
   selectedControlPoint(type: string, id: string) {
@@ -334,7 +376,10 @@ export class AnimationCurveEditorComponent extends React.Component<
       kf.selected = false;
       return kf;
     });
-    this.setState({ svgKeyframes: updatedKeyframes });
+    this.setState({
+      svgKeyframes: updatedKeyframes,
+      actionableKeyframe: { frame: undefined, value: undefined },
+    });
   }
 
   updateValuePerCoordinate(
@@ -436,10 +481,8 @@ export class AnimationCurveEditorComponent extends React.Component<
 
   renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, id: string) {
     let animation = this.state.selected as Animation;
-    // Bug: After play/stop we get an extra keyframe at 0
-    let index = parseInt(id.split('_')[3]);
 
-    let coordinate = parseInt(id.split('_')[2]);
+    const { order: index, coordinate } = this.decodeCurveId(id);
 
     let keys = [...animation.getKeys()];
 
@@ -457,22 +500,71 @@ export class AnimationCurveEditorComponent extends React.Component<
       }
     }
 
-    keys[index].frame = newFrame; // This value comes as percentage/frame/time
+    if (newFrame > keys[index].frame) {
+      const nextKf = keys[index + 1];
 
-    // Calculate value for Vector3...
+      if (nextKf) {
+        if (nextKf.frame <= newFrame) {
+          keys[index].frame = keys[index].frame;
+        } else {
+          keys[index].frame = newFrame;
+        }
+      }
+    }
+
+    if (newFrame < keys[index].frame) {
+      const prevKf = keys[index - 1];
+      if (prevKf) {
+        if (prevKf.frame >= newFrame) {
+          keys[index].frame = keys[index].frame;
+        } else {
+          keys[index].frame = newFrame;
+        }
+      }
+    }
 
     let updatedValue =
       ((this._heightScale - updatedSvgKeyFrame.keyframePoint.y) /
         this._heightScale) *
       2; // this value comes inverted svg from 0 = 100 to 100 = 0
 
-    keys[index].value = this.updateValuePerCoordinate(
+    const updatedValueInCoordinate = this.updateValuePerCoordinate(
       animation.dataType,
       keys[index].value,
       updatedValue,
       coordinate
     );
 
+    keys[index].value = updatedValueInCoordinate;
+
+    this.updateLeftControlPoint(
+      updatedSvgKeyFrame,
+      keys[index],
+      animation.dataType,
+      coordinate
+    );
+    this.updateRightControlPoint(
+      updatedSvgKeyFrame,
+      keys[index],
+      animation.dataType,
+      coordinate
+    );
+
+    animation.setKeys(keys);
+
+    this.setState({
+      actionableKeyframe: { frame: newFrame, value: updatedValueInCoordinate },
+    });
+
+    this.selectAnimation(animation, coordinate);
+  }
+
+  updateLeftControlPoint(
+    updatedSvgKeyFrame: IKeyframeSvgPoint,
+    key: IAnimationKey,
+    dataType: number,
+    coordinate: number
+  ) {
     if (updatedSvgKeyFrame.isLeftActive) {
       if (updatedSvgKeyFrame.leftControlPoint !== null) {
         // Rotate
@@ -488,9 +580,9 @@ export class AnimationCurveEditorComponent extends React.Component<
 
         let updatedValue = keyframeValue - newValue;
 
-        keys[index].inTangent = this.updateValuePerCoordinate(
-          animation.dataType,
-          keys[index].inTangent,
+        key.inTangent = this.updateValuePerCoordinate(
+          dataType,
+          key.inTangent,
           updatedValue,
           coordinate
         );
@@ -499,12 +591,19 @@ export class AnimationCurveEditorComponent extends React.Component<
           // Right control point if exists
           if (updatedSvgKeyFrame.rightControlPoint !== null) {
             // Sets opposite value
-            keys[index].outTangent = keys[index].inTangent * -1;
+            key.outTangent = key.inTangent * -1;
           }
         }
       }
     }
+  }
 
+  updateRightControlPoint(
+    updatedSvgKeyFrame: IKeyframeSvgPoint,
+    key: IAnimationKey,
+    dataType: number,
+    coordinate: number
+  ) {
     if (updatedSvgKeyFrame.isRightActive) {
       if (updatedSvgKeyFrame.rightControlPoint !== null) {
         // Rotate
@@ -520,9 +619,9 @@ export class AnimationCurveEditorComponent extends React.Component<
 
         let updatedValue = keyframeValue - newValue;
 
-        keys[index].outTangent = this.updateValuePerCoordinate(
-          animation.dataType,
-          keys[index].outTangent,
+        key.outTangent = this.updateValuePerCoordinate(
+          dataType,
+          key.outTangent,
           updatedValue,
           coordinate
         );
@@ -530,76 +629,95 @@ export class AnimationCurveEditorComponent extends React.Component<
         if (!this.state.isBrokenMode) {
           if (updatedSvgKeyFrame.leftControlPoint !== null) {
             // Sets opposite value
-            keys[index].inTangent = keys[index].outTangent * -1;
+            key.inTangent = key.outTangent * -1;
           }
         }
       }
     }
-
-    animation.setKeys(keys);
-
-    this.selectAnimation(animation, coordinate);
   }
 
-  /**
-   * Actions
-   * This section handles events from GraphActionsBar.
-   */
   handleFrameChange(event: React.ChangeEvent<HTMLInputElement>) {
     event.preventDefault();
-    let frame = 0;
 
-    if (
-      isNaN(event.target.valueAsNumber) ||
-      event.target.value.indexOf('.') !== -1
-    ) {
-      this.setState({
-        notification: 'Frame input only accepts integer values',
-      });
+    let frame;
+    if (event.target.value === '') {
+      frame = '';
     } else {
       frame = parseInt(event.target.value);
-      this.changeCurrentFrame(frame);
     }
+
+    this.setState({
+      actionableKeyframe: {
+        frame: frame,
+        value: this.state.actionableKeyframe.value,
+      },
+    });
   }
 
   handleValueChange(event: React.ChangeEvent<HTMLInputElement>) {
     event.preventDefault();
 
-    if (isNaN(event.target.valueAsNumber)) {
+    let value;
+
+    if (event.target.value !== undefined) {
+      if (event.target.value !== '') {
+        value = parseFloat(event.target.value);
+      } else {
+        value = '';
+      }
+
       this.setState({
-        notification: 'Value input only numeric values',
+        actionableKeyframe: {
+          frame: this.state.actionableKeyframe.frame,
+          value: value,
+        },
       });
-    } else {
-      this.setState(
-        { currentValue: parseFloat(parseFloat(event.target.value).toFixed(3)) },
-        () => {
-          if (this.state.selected !== null) {
-            let animation = this.state.selected;
-            let keys = animation.getKeys();
-
-            let isKeyframe = keys.find(
-              (k) => k.frame === this.state.currentFrame
-            );
-            if (isKeyframe) {
-              let updatedKeys = keys.map((k) => {
-                if (k.frame === this.state.currentFrame) {
-                  k.value = this.state.currentValue;
-                }
-                return k;
-              });
-              this.state.selected.setKeys(updatedKeys);
-              this.selectAnimation(animation);
-            }
+    }
+  }
+
+  setKeyframeValue() {
+    if (
+      this.state.actionableKeyframe.frame !== '' &&
+      this.state.actionableKeyframe.frame !== undefined &&
+      this.state.actionableKeyframe.value !== '' &&
+      this.state.actionableKeyframe.value !== undefined
+    ) {
+      if (this.state.selected !== null) {
+        let currentSelected = this.state.svgKeyframes?.find(
+          (kf) => kf.selected
+        );
+        if (currentSelected) {
+          let { order, coordinate } = this.decodeCurveId(currentSelected.id);
+          let animation = this.state.selected;
+          let keys = animation.getKeys();
+
+          let isKeyframe = keys.find((_, i) => i === order);
+          if (isKeyframe) {
+            let updatedKeys = keys.map((k, i) => {
+              if (i === order) {
+                k.frame = this.state.actionableKeyframe.frame as number;
+
+                const currentValue = this.getValueAsArray(
+                  animation.dataType,
+                  k.value
+                );
+                currentValue[coordinate] = this.state.actionableKeyframe.value;
+                k.value = currentValue;
+              }
+              return k;
+            });
+            this.state.selected.setKeys(updatedKeys);
+            this.selectAnimation(animation);
           }
         }
-      );
+      }
     }
   }
 
   setFlatTangent() {
     if (this.state.selected !== null) {
       let animation = this.state.selected;
-      this.setState({ isFlatTangentMode: !this.state.isFlatTangentMode }, () =>
+      this.setState({ isFlatTangentMode: true }, () =>
         this.selectAnimation(animation)
       );
     }
@@ -637,18 +755,46 @@ export class AnimationCurveEditorComponent extends React.Component<
     if (this.state.selected !== null) {
       let currentAnimation = this.state.selected;
 
-      if (currentAnimation.dataType === Animation.ANIMATIONTYPE_FLOAT) {
-        let keys = currentAnimation.getKeys();
-        let x = this.state.currentFrame;
-        let y = this.state.currentValue;
+      let keys = currentAnimation.getKeys();
+      let x = this.state.currentFrame;
+      let y = this.state.actionableKeyframe.value ?? 1;
+      // check if value exists...
+      let arrayValue: any = [];
+      let emptyValue = this.returnZero(currentAnimation.dataType);
+      let existValue = keys.find((k) => k.frame === x);
+      if (existValue !== undefined) {
+        arrayValue = this.getValueAsArray(
+          currentAnimation.dataType,
+          existValue.value
+        );
+      } else {
+        // Set empty if doesn't exist
+        if (emptyValue) {
+          arrayValue = this.getValueAsArray(
+            currentAnimation.dataType,
+            emptyValue
+          );
+        }
+      }
 
-        keys.push({ frame: x, value: y, inTangent: 0, outTangent: 0 });
-        keys.sort((a, b) => a.frame - b.frame);
+      arrayValue[this.state.selectedCoordinate] = y;
 
-        currentAnimation.setKeys(keys);
+      let actualValue = this.setValueAsType(
+        currentAnimation.dataType,
+        arrayValue
+      );
 
-        this.selectAnimation(currentAnimation);
-      }
+      keys.push({
+        frame: x,
+        value: actualValue,
+        inTangent: emptyValue,
+        outTangent: emptyValue,
+      });
+      keys.sort((a, b) => a.frame - b.frame);
+
+      currentAnimation.setKeys(keys);
+
+      this.selectAnimation(currentAnimation, this.state.selectedCoordinate);
     }
   }
 
@@ -787,35 +933,29 @@ export class AnimationCurveEditorComponent extends React.Component<
     } else {
       flattened = keyframes;
     }
+    this.setState({ isFlatTangentMode: false });
     return flattened;
   }
 
   returnZero(dataType: number) {
-    let type;
     switch (dataType) {
       case Animation.ANIMATIONTYPE_FLOAT:
-        type = 0;
-        break;
+        return 0;
       case Animation.ANIMATIONTYPE_VECTOR3:
-        type = Vector3.Zero();
-        break;
+        return Vector3.Zero();
       case Animation.ANIMATIONTYPE_VECTOR2:
-        type = Vector2.Zero();
-        break;
+        return Vector2.Zero();
       case Animation.ANIMATIONTYPE_QUATERNION:
-        type = Quaternion.Zero();
-        break;
+        return Quaternion.Zero();
       case Animation.ANIMATIONTYPE_COLOR3:
-        type = new Color3(0, 0, 0);
-        break;
+        return new Color3(0, 0, 0);
       case Animation.ANIMATIONTYPE_COLOR4:
-        type = new Color4(0, 0, 0, 0);
-        break;
+        return new Color4(0, 0, 0, 0);
       case Animation.ANIMATIONTYPE_SIZE:
-        type = new Size(0, 0);
-        break;
+        return new Size(0, 0);
+      default:
+        return 0;
     }
-    return type;
   }
 
   getValueAsArray(
@@ -849,6 +989,37 @@ export class AnimationCurveEditorComponent extends React.Component<
     return valueAsArray;
   }
 
+  setValueAsType(valueType: number, arrayValue: number[]) {
+    switch (valueType) {
+      case Animation.ANIMATIONTYPE_FLOAT:
+        return arrayValue[0];
+      case Animation.ANIMATIONTYPE_VECTOR3:
+        return new Vector3(arrayValue[0], arrayValue[1], arrayValue[2]);
+      case Animation.ANIMATIONTYPE_VECTOR2:
+        return new Vector2(arrayValue[0], arrayValue[1]);
+      case Animation.ANIMATIONTYPE_QUATERNION:
+        return new Quaternion(
+          arrayValue[0],
+          arrayValue[1],
+          arrayValue[2],
+          arrayValue[3]
+        );
+      case Animation.ANIMATIONTYPE_COLOR3:
+        return new Color3(arrayValue[0], arrayValue[1], arrayValue[2]);
+      case Animation.ANIMATIONTYPE_COLOR4:
+        return new Color4(
+          arrayValue[0],
+          arrayValue[1],
+          arrayValue[2],
+          arrayValue[3]
+        );
+      case Animation.ANIMATIONTYPE_SIZE:
+        return new Size(arrayValue[0], arrayValue[1]);
+      default:
+        return arrayValue[0];
+    }
+  }
+
   getPathData(animation: Animation | null) {
     if (animation === null) {
       return undefined;
@@ -1000,7 +1171,7 @@ export class AnimationCurveEditorComponent extends React.Component<
   ) {
     keyframes.forEach((key, i) => {
       // Create a unique id for curve
-      const curveId = animationName + '_' + i;
+      const curveId = this.encodeCurveId(animationName, i);
 
       // identify type of value and split...
       const keyframe_valueAsArray = this.getValueAsArray(type, key.value)[
@@ -1028,18 +1199,6 @@ export class AnimationCurveEditorComponent extends React.Component<
 
       let y = this._heightScale - keyframe_valueAsArray * middle;
 
-      let nextKeyframe = keyframes[i + 1];
-      let prevKeyframe = keyframes[i - 1];
-      if (nextKeyframe !== undefined) {
-        let distance = keyframes[i + 1].frame - key.frame;
-        defaultWeight = distance * 0.33;
-      }
-
-      if (prevKeyframe !== undefined) {
-        let distance = key.frame - keyframes[i - 1].frame;
-        defaultWeight = distance * 0.33;
-      }
-
       if (inT !== null) {
         let valueIn = y * inT + y;
         inTangent = new Vector2(
@@ -1350,6 +1509,16 @@ export class AnimationCurveEditorComponent extends React.Component<
     }
   }
 
+  setIsLooping() {
+    this.setState({ isLooping: !this.state.isLooping, isPlaying: false }, () =>
+      this.stopAnimation()
+    );
+  }
+
+  setFramesPerSecond(fps: number) {
+    this.setState({ fps: fps, isPlaying: false }, () => this.stopAnimation());
+  }
+
   analizeAnimationForLerp(animation: Animation | null) {
     if (animation !== null) {
       const { easingMode, easingType, usesTangents } = this.getAnimationData(
@@ -1375,6 +1544,7 @@ export class AnimationCurveEditorComponent extends React.Component<
    */
   changeCurrentFrame(frame: number) {
     let currentValue;
+    this.stopAnimation();
     if (this.state.selectedPathData) {
       let selectedCurve = this.state.selectedPathData[
         this.state.selectedCoordinate
@@ -1398,6 +1568,7 @@ export class AnimationCurveEditorComponent extends React.Component<
           currentFrame: frame,
           currentValue: currentValue,
           currentPoint: currentP,
+          isPlaying: false,
         });
       }
     }
@@ -1477,6 +1648,7 @@ export class AnimationCurveEditorComponent extends React.Component<
   }
 
   moveFrameTo(e: React.MouseEvent<SVGRectElement, MouseEvent>) {
+    this.stopAnimation();
     var svg = e.currentTarget as SVGRectElement;
     var CTM = svg.getScreenCTM();
     let position;
@@ -1486,7 +1658,7 @@ export class AnimationCurveEditorComponent extends React.Component<
         (e.clientY - CTM.f) / CTM.d
       );
       let selectedFrame = Math.round(position.x / this._pixelFrameUnit);
-      this.setState({ currentFrame: selectedFrame });
+      this.setState({ currentFrame: selectedFrame, isPlaying: false });
     }
   }
 
@@ -1525,6 +1697,7 @@ export class AnimationCurveEditorComponent extends React.Component<
           close={() => this.clearNotification()}
         />
         <GraphActionsBar
+          setKeyframeValue={() => this.setKeyframeValue()}
           enabled={
             this.state.selected === null || this.state.selected === undefined
               ? false
@@ -1532,8 +1705,7 @@ export class AnimationCurveEditorComponent extends React.Component<
           }
           title={this._entityName}
           close={this.props.close}
-          currentValue={this.state.currentValue}
-          currentFrame={this.state.currentFrame}
+          actionableKeyframe={this.state.actionableKeyframe}
           handleFrameChange={(e) => this.handleFrameChange(e)}
           handleValueChange={(e) => this.handleValueChange(e)}
           addKeyframe={() => this.addKeyframeClick()}
@@ -1563,12 +1735,8 @@ export class AnimationCurveEditorComponent extends React.Component<
               globalState={this.props.globalState}
               snippetServer={this._snippetUrl}
               fps={this.state.fps}
-              setFps={(fps: number) => {
-                this.setState({ fps: fps });
-              }}
-              setIsLooping={() => {
-                this.setState({ isLooping: !this.state.isLooping });
-              }}
+              setFps={(fps: number) => this.setFramesPerSecond(fps)}
+              setIsLooping={() => this.setIsLooping()}
             />
 
             <div
@@ -1610,6 +1778,7 @@ export class AnimationCurveEditorComponent extends React.Component<
                   canvasPositionEnded={() =>
                     this.setState({ repositionCanvas: false })
                   }
+                  resetActionableKeyframe={() => this.resetActionableKeyframe()}
                 >
                   {/* Multiple Curves  */}
                   {this.state.selectedPathData?.map((curve, i) => (

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationListTree.tsx

@@ -122,7 +122,7 @@ export class AnimationListTree extends React.Component<
     index: number
   ) {
     this.setState({ selectedCoordinate: coordinate, selectedAnimation: index });
-    this.props.selectAnimation(animation, SelectedCoordinate.x);
+    this.props.selectAnimation(animation, coordinate);
   }
 
   coordinateItem(

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss

@@ -485,7 +485,7 @@
       align-items: center;
       margin-right: 8px;
       input {
-        width: 75px;
+        width: 78px;
         height: 24px;
         color: white;
         font-size: 12px;

+ 54 - 6
inspector/src/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar.tsx

@@ -1,5 +1,6 @@
 import * as React from 'react';
 import { IconButtonLineComponent } from '../../../lines/iconButtonLineComponent';
+import { IActionableKeyFrame } from './animationCurveEditorComponent';
 
 interface IGraphActionsBarProps {
   addKeyframe: () => void;
@@ -11,16 +12,57 @@ interface IGraphActionsBarProps {
   setLerpMode: () => void;
   brokenMode: boolean;
   lerpMode: boolean;
-  currentValue: number;
-  currentFrame: number;
+  actionableKeyframe: IActionableKeyFrame;
   title: string;
   close: (event: any) => void;
   enabled: boolean;
+  setKeyframeValue: () => void;
 }
 
 export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
+  private _frameInput: React.RefObject<HTMLInputElement>;
+  private _valueInput: React.RefObject<HTMLInputElement>;
   constructor(props: IGraphActionsBarProps) {
     super(props);
+    this._frameInput = React.createRef();
+    this._valueInput = React.createRef();
+  }
+
+  componentDidMount() {
+    this._frameInput.current?.addEventListener(
+      'keyup',
+      this.isEnterKeyUp.bind(this)
+    );
+    this._valueInput.current?.addEventListener(
+      'keyup',
+      this.isEnterKeyUp.bind(this)
+    );
+  }
+
+  componentWillUnmount() {
+    this._frameInput.current?.removeEventListener(
+      'keyup',
+      this.isEnterKeyUp.bind(this)
+    );
+    this._valueInput.current?.removeEventListener(
+      'keyup',
+      this.isEnterKeyUp.bind(this)
+    );
+  }
+
+  isEnterKeyUp(event: KeyboardEvent) {
+    event.preventDefault();
+
+    if (event.key === 'Enter') {
+      this.props.setKeyframeValue();
+    }
+  }
+
+  onBlur(event: React.FocusEvent<HTMLInputElement>) {
+    event.preventDefault();
+    if (event.target.value !== '') {
+      this.props.setKeyframeValue();
+    }
   }
 
   render() {
@@ -34,20 +76,26 @@ export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
           className='buttons-container'
           style={{ pointerEvents: this.props.enabled ? 'all' : 'none' }}
         >
-          <div className='action-input'>
+          <div className='action-input frame-input'>
             <input
+              ref={this._frameInput}
               type='number'
-              value={this.props.currentFrame}
               onChange={this.props.handleFrameChange}
+              value={this.props.actionableKeyframe.frame?.toString() || ''}
               step='1'
+              disabled={this.props.actionableKeyframe.frame === undefined}
+              onBlur={(e) => this.onBlur(e)}
             />
           </div>
           <div className='action-input'>
             <input
+              ref={this._valueInput}
               type='number'
-              value={this.props.currentValue}
+              value={this.props.actionableKeyframe.value || ''}
               onChange={this.props.handleValueChange}
-              step='0.1'
+              step='0.01'
+              disabled={this.props.actionableKeyframe.value === undefined}
+              onBlur={(e) => this.onBlur(e)}
             />
           </div>
           <IconButtonLineComponent

+ 7 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx

@@ -17,6 +17,7 @@ interface ISvgDraggableAreaProps {
   positionCanvas?: number;
   repositionCanvas?: boolean;
   canvasPositionEnded: () => void;
+  resetActionableKeyframe: () => void;
 }
 
 export class SvgDraggableArea extends React.Component<
@@ -145,6 +146,8 @@ export class SvgDraggableArea extends React.Component<
               point.isRightActive = true;
             } else {
               point.keyframePoint = coord;
+              point.isRightActive = false;
+              point.isLeftActive = false;
             }
             this.props.updatePosition(point, this._currentPointId);
           }
@@ -249,13 +252,15 @@ export class SvgDraggableArea extends React.Component<
     this._draggableArea.current?.focus();
 
     if ((e.target as SVGSVGElement).className.baseVal == 'linear pannable') {
-      if (this.isControlPointActive()) {
+      if (this.isNotControlPointActive()) {
         this.props.deselectKeyframes();
       }
+
+      this.props.resetActionableKeyframe();
     }
   }
 
-  isControlPointActive() {
+  isNotControlPointActive() {
     const activeControlPoints = this.props.keyframeSvgPoints.filter(
       (x) => x.isLeftActive || x.isRightActive
     );

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

@@ -1,275 +1,275 @@
-import * as React from "react";
-
-import { Nullable } from "babylonjs/types";
-import { Tools } from "babylonjs/Misc/tools";
-import { Observable } from "babylonjs/Misc/observable";
-import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
-import { Texture } from "babylonjs/Materials/Textures/texture";
-import { CubeTexture } from "babylonjs/Materials/Textures/cubeTexture";
-
-import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
-import { LineContainerComponent } from "../../../lineContainerComponent";
-import { SliderLineComponent } from "../../../lines/sliderLineComponent";
-import { TextLineComponent } from "../../../lines/textLineComponent";
-import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
-import { TextureLineComponent } from "../../../lines/textureLineComponent";
-import { FloatLineComponent } from "../../../lines/floatLineComponent";
-import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
-import { FileButtonLineComponent } from "../../../lines/fileButtonLineComponent";
-import { LockObject } from "../lockObject";
-import { ValueLineComponent } from "../../../lines/valueLineComponent";
-import { GlobalState } from "../../../../../components/globalState";
-
-import { AdvancedDynamicTextureInstrumentation } from "babylonjs-gui/2D/adtInstrumentation";
-import { AdvancedDynamicTexture } from "babylonjs-gui/2D/advancedDynamicTexture";
-import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
-import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
-import { TextInputLineComponent } from '../../../lines/textInputLineComponent';
-import { AnimationGridComponent } from '../animations/animationPropertyGridComponent';
-
-import { Engine } from 'babylonjs/Engines/engine';
-import { PopupComponent } from '../../../../popupComponent';
-import { TextureEditorComponent } from './textures/textureEditorComponent';
-
-interface ITexturePropertyGridComponentProps {
-    texture: BaseTexture,
-    lockObject: LockObject,
-    globalState: GlobalState,
-    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
-}
-
-export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps> {
-
-    private _adtInstrumentation: Nullable<AdvancedDynamicTextureInstrumentation>;
-    private textureLineRef: React.RefObject<TextureLineComponent>;
-
-    private _isTextureEditorOpen = false;
-    
-
-    constructor(props: ITexturePropertyGridComponentProps) {
-        super(props);
-
-        const texture = this.props.texture;
-
-        this.textureLineRef = React.createRef();
-
-        if (!texture || !(texture as any).rootContainer) {
-            return;
-        }
-
-        const adt = texture as AdvancedDynamicTexture;
-
-        this._adtInstrumentation = new AdvancedDynamicTextureInstrumentation(adt);
-        this._adtInstrumentation!.captureRenderTime = true;
-        this._adtInstrumentation!.captureLayoutTime = true;
-    }
-
-    componentWillUnmount() {
-        if (this._adtInstrumentation) {
-            this._adtInstrumentation.dispose();
-            this._adtInstrumentation = null;
-        }
-    }
-
-    updateTexture(file: File) {
-        const texture = this.props.texture;
-        Tools.ReadFile(file, (data) => {
-            var blob = new Blob([data], { type: "octet/stream" });
-
-            var reader = new FileReader();
-            reader.readAsDataURL(blob); 
-            reader.onloadend = () => {
-                let base64data = reader.result as string;     
-
-                if (texture.isCube) {
-                    let extension: string | undefined = undefined;
-                    if (file.name.toLowerCase().indexOf(".dds") > 0) {
-                        extension = ".dds";
-                    } else if (file.name.toLowerCase().indexOf(".env") > 0) {
-                        extension = ".env";
-                    }
-
-                    (texture as CubeTexture).updateURL(base64data, extension, () => this.forceRefresh());
-                } else {
-                    (texture as Texture).updateURL(base64data, null, () => this.forceRefresh());
-                }
-            };
-
-        }, undefined, true);
-    }
-
-    onOpenTextureEditor() {
-        this._isTextureEditorOpen = true;
-    }
-    
-    onCloseTextureEditor(window: Window | null) {
-        this._isTextureEditorOpen = false;
-        if (window !== null) {
-            window.close();
-        }
-    }
-
-    forceRefresh() {
-        this.forceUpdate();
-        (this.textureLineRef.current as TextureLineComponent).updatePreview();
-    }
-
-    render() {
-        const texture = this.props.texture;
-
-        var samplingMode = [
-            { label: "Nearest", value: Texture.NEAREST_NEAREST },
-            { label: "Nearest & linear mip", value: Texture.NEAREST_LINEAR },
-            { label: "Linear", value: Texture.LINEAR_LINEAR_MIPLINEAR },
-        ];
-
-        var coordinatesMode = [
-            { label: "Explicit", value: Texture.EXPLICIT_MODE },
-            { label: "Cubic", value: Texture.CUBIC_MODE },
-            { label: "Inverse cubic", value: Texture.INVCUBIC_MODE },
-            { label: "Equirectangular", value: Texture.EQUIRECTANGULAR_MODE },
-            { label: "Fixed equirectangular", value: Texture.FIXED_EQUIRECTANGULAR_MODE },
-            { label: "Fixed equirectangular mirrored", value: Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE },
-            { label: "Planar", value: Texture.PLANAR_MODE },
-            { label: "Projection", value: Texture.PROJECTION_MODE },
-            { label: "Skybox", value: Texture.SKYBOX_MODE },
-            { label: "Spherical", value: Texture.SPHERICAL_MODE },
-        ];
-
-        let extension = "";
-        let url = (texture as Texture).url;
-        let textureUrl = (!url || url.substring(0, 4) === "data" || url.substring(0, 4) === "blob") ? "" : url;
-
-        if (textureUrl) {
-            for (var index = textureUrl.length - 1; index >= 0; index--) {
-                if (textureUrl[index] === ".") {
-                    break;
-                }
-                extension = textureUrl[index] + extension;
-            }
-        }
-
-        const editable = texture.textureType != Engine.TEXTURETYPE_FLOAT && texture.textureType != Engine.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV && texture.textureType !== Engine.TEXTURETYPE_HALF_FLOAT;
-
-        return (
-            <div className="pane">
-                <LineContainerComponent globalState={this.props.globalState} title="PREVIEW">
-                    <TextureLineComponent ref={this.textureLineRef} texture={texture} width={256} height={256} globalState={this.props.globalState} />
-                    <FileButtonLineComponent label="Load texture from file" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
-                    {editable &&
-                        <ButtonLineComponent label="View" onClick={() => this.onOpenTextureEditor()} />
-                    }
-                    <TextInputLineComponent label="URL" value={textureUrl} lockObject={this.props.lockObject} onChange={url => {
-                        (texture as Texture).updateURL(url);
-                        this.forceRefresh();
-                    }} />
-                </LineContainerComponent>
-                {this._isTextureEditorOpen && (
-                <PopupComponent
-                  id='texture-editor'
-                  title='Texture Editor'
-                  size={{ width: 1024, height: 490 }}
-                  onOpen={(window: Window) => {}}
-                  onClose={(window: Window) =>
-                    this.onCloseTextureEditor(window)
-                  }
-                >
-                    <TextureEditorComponent
-                        globalState={this.props.globalState}
-                        texture={this.props.texture}
-                    />
-                </PopupComponent>)}
-                <CustomPropertyGridComponent globalState={this.props.globalState} target={texture}
-                    lockObject={this.props.lockObject}
-                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
-                    <TextLineComponent label="Width" value={texture.getSize().width.toString()} />
-                    <TextLineComponent label="Height" value={texture.getSize().height.toString()} />
-                    {
-                        texture.isRenderTarget &&
-                        <ButtonLineComponent label="Scale up" onClick={() => {
-                            let scene = texture.getScene()!;
-                            texture.scale(2);
-                            setTimeout(() => {
-                                this.props.globalState.onSelectionChangedObservable.notifyObservers(scene.getTextureByUniqueID(texture.uniqueId));
-                            });
-                        }} />
-                    }
-                    {
-                        texture.isRenderTarget &&
-                        <ButtonLineComponent label="Scale down" onClick={() => {                            
-                            let scene = texture.getScene()!;
-                            texture.scale(0.5);
-                            setTimeout(() => {
-                                this.props.globalState.onSelectionChangedObservable.notifyObservers(scene.getTextureByUniqueID(texture.uniqueId));
-                            });
-                        }} />
-                    }
-                    {
-                        extension &&
-                        <TextLineComponent label="File format" value={extension} />
-                    }
-                    <TextLineComponent label="Unique ID" value={texture.uniqueId.toString()} />
-                    <TextLineComponent label="Class" value={texture.getClassName()} />
-                    <TextLineComponent label="Has alpha" value={texture.hasAlpha ? "Yes" : "No"} />
-                    <TextLineComponent label="Is 3D" value={texture.is3D ? "Yes" : "No"} />
-                    <TextLineComponent label="Is 2D array" value={texture.is2DArray ? "Yes" : "No"} />
-                    <TextLineComponent label="Is cube" value={texture.isCube ? "Yes" : "No"} />
-                    <TextLineComponent label="Is render target" value={texture.isRenderTarget ? "Yes" : "No"} />
-                    {
-                        (texture instanceof Texture) && 
-                        <TextLineComponent label="Stored as inverted on Y" value={texture.invertY ? "Yes" : "No"} />
-                    }
-                    <TextLineComponent label="Has mipmaps" value={!texture.noMipmap ? "Yes" : "No"} />
-                    <SliderLineComponent label="UV set" target={texture} propertyName="coordinatesIndex" minimum={0} maximum={3} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} decimalCount={0} />
-                    <OptionsLineComponent label="Mode" options={coordinatesMode} target={texture} propertyName="coordinatesMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={(value) => texture.updateSamplingMode(value)} />
-                    <SliderLineComponent label="Level" target={texture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    {
-                        texture.updateSamplingMode &&
-                        <OptionsLineComponent label="Sampling" options={samplingMode} target={texture} noDirectUpdate={true} propertyName="samplingMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={(value) => texture.updateSamplingMode(value)} />
-                    }
-                </LineContainerComponent>
-                {
-                    texture.getScene() &&
-                    <AnimationGridComponent globalState={this.props.globalState} animatable={texture} scene={texture.getScene()!} lockObject={this.props.lockObject} />
-                }
-                {
-                    (texture as any).rootContainer &&
-                    <LineContainerComponent globalState={this.props.globalState} title="ADVANCED TEXTURE PROPERTIES">
-                        <ValueLineComponent label="Last layout time" value={this._adtInstrumentation!.renderTimeCounter.current} units="ms" />
-                        <ValueLineComponent label="Last render time" value={this._adtInstrumentation!.layoutTimeCounter.current} units="ms" />
-                        <SliderLineComponent label="Render scale" minimum={0.1} maximum={5} step={0.1} target={texture} propertyName="renderScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <CheckBoxLineComponent label="Premultiply alpha" target={texture} propertyName="premulAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal width" target={texture} propertyName="idealWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal height" target={texture} propertyName="idealHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <CheckBoxLineComponent label="Use smallest ideal" target={texture} propertyName="useSmallestIdeal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <CheckBoxLineComponent label="Render at ideal size" target={texture} propertyName="renderAtIdealSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <CheckBoxLineComponent label="Invalidate Rect optimization" target={texture} propertyName="useInvalidateRectOptimization" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    </LineContainerComponent>
-                }
-                <LineContainerComponent globalState={this.props.globalState} title="TRANSFORM">
-                    {
-                        !texture.isCube &&
-                        <div>
-                            <FloatLineComponent lockObject={this.props.lockObject} label="U offset" target={texture} propertyName="uOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <FloatLineComponent lockObject={this.props.lockObject} label="V offset" target={texture} propertyName="vOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <FloatLineComponent lockObject={this.props.lockObject} label="U scale" target={texture} propertyName="uScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <FloatLineComponent lockObject={this.props.lockObject} label="V scale" target={texture} propertyName="vScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <FloatLineComponent lockObject={this.props.lockObject} label="U angle" useEuler={this.props.globalState.onlyUseEulers} target={texture} propertyName="uAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <FloatLineComponent lockObject={this.props.lockObject} label="V angle" useEuler={this.props.globalState.onlyUseEulers} target={texture} propertyName="vAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <FloatLineComponent lockObject={this.props.lockObject} label="W angle" useEuler={this.props.globalState.onlyUseEulers} target={texture} propertyName="wAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <CheckBoxLineComponent label="Clamp U" isSelected={() => texture.wrapU === Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapU = value ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE} />
-                            <CheckBoxLineComponent label="Clamp V" isSelected={() => texture.wrapV === Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapV = value ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE} />
-                        </div>
-                    }
-                    {
-                        texture.isCube &&
-                        <div>
-                            <SliderLineComponent label="Rotation Y" useEuler={this.props.globalState.onlyUseEulers} minimum={0} maximum={2 * Math.PI} step={0.1} target={texture} propertyName="rotationY" />
-                        </div>
-                    }
-                </LineContainerComponent>
-            </div>
-        );
-    }
+import * as React from "react";
+
+import { Nullable } from "babylonjs/types";
+import { Tools } from "babylonjs/Misc/tools";
+import { Observable } from "babylonjs/Misc/observable";
+import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
+import { Texture } from "babylonjs/Materials/Textures/texture";
+import { CubeTexture } from "babylonjs/Materials/Textures/cubeTexture";
+
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { SliderLineComponent } from "../../../lines/sliderLineComponent";
+import { TextLineComponent } from "../../../lines/textLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { TextureLineComponent } from "../../../lines/textureLineComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+import { FileButtonLineComponent } from "../../../lines/fileButtonLineComponent";
+import { LockObject } from "../lockObject";
+import { ValueLineComponent } from "../../../lines/valueLineComponent";
+import { GlobalState } from "../../../../../components/globalState";
+
+import { AdvancedDynamicTextureInstrumentation } from "babylonjs-gui/2D/adtInstrumentation";
+import { AdvancedDynamicTexture } from "babylonjs-gui/2D/advancedDynamicTexture";
+import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
+import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
+import { TextInputLineComponent } from '../../../lines/textInputLineComponent';
+import { AnimationGridComponent } from '../animations/animationPropertyGridComponent';
+
+import { Engine } from 'babylonjs/Engines/engine';
+import { PopupComponent } from '../../../../popupComponent';
+import { TextureEditorComponent } from './textures/textureEditorComponent';
+
+interface ITexturePropertyGridComponentProps {
+    texture: BaseTexture,
+    lockObject: LockObject,
+    globalState: GlobalState,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps> {
+
+    private _adtInstrumentation: Nullable<AdvancedDynamicTextureInstrumentation>;
+    private textureLineRef: React.RefObject<TextureLineComponent>;
+
+    private _isTextureEditorOpen = false;
+    
+
+    constructor(props: ITexturePropertyGridComponentProps) {
+        super(props);
+
+        const texture = this.props.texture;
+
+        this.textureLineRef = React.createRef();
+
+        if (!texture || !(texture as any).rootContainer) {
+            return;
+        }
+
+        const adt = texture as AdvancedDynamicTexture;
+
+        this._adtInstrumentation = new AdvancedDynamicTextureInstrumentation(adt);
+        this._adtInstrumentation!.captureRenderTime = true;
+        this._adtInstrumentation!.captureLayoutTime = true;
+    }
+
+    componentWillUnmount() {
+        if (this._adtInstrumentation) {
+            this._adtInstrumentation.dispose();
+            this._adtInstrumentation = null;
+        }
+    }
+
+    updateTexture(file: File) {
+        const texture = this.props.texture;
+        Tools.ReadFile(file, (data) => {
+            var blob = new Blob([data], { type: "octet/stream" });
+
+            var reader = new FileReader();
+            reader.readAsDataURL(blob); 
+            reader.onloadend = () => {
+                let base64data = reader.result as string;     
+
+                if (texture.isCube) {
+                    let extension: string | undefined = undefined;
+                    if (file.name.toLowerCase().indexOf(".dds") > 0) {
+                        extension = ".dds";
+                    } else if (file.name.toLowerCase().indexOf(".env") > 0) {
+                        extension = ".env";
+                    }
+
+                    (texture as CubeTexture).updateURL(base64data, extension, () => this.forceRefresh());
+                } else {
+                    (texture as Texture).updateURL(base64data, null, () => this.forceRefresh());
+                }
+            };
+
+        }, undefined, true);
+    }
+
+    onOpenTextureEditor() {
+        this._isTextureEditorOpen = true;
+    }
+    
+    onCloseTextureEditor(window: Window | null) {
+        this._isTextureEditorOpen = false;
+        if (window !== null) {
+            window.close();
+        }
+    }
+
+    forceRefresh() {
+        this.forceUpdate();
+        (this.textureLineRef.current as TextureLineComponent).updatePreview();
+    }
+
+    render() {
+        const texture = this.props.texture;
+
+        var samplingMode = [
+            { label: "Nearest", value: Texture.NEAREST_NEAREST },
+            { label: "Nearest & linear mip", value: Texture.NEAREST_LINEAR },
+            { label: "Linear", value: Texture.LINEAR_LINEAR_MIPLINEAR },
+        ];
+
+        var coordinatesMode = [
+            { label: "Explicit", value: Texture.EXPLICIT_MODE },
+            { label: "Cubic", value: Texture.CUBIC_MODE },
+            { label: "Inverse cubic", value: Texture.INVCUBIC_MODE },
+            { label: "Equirectangular", value: Texture.EQUIRECTANGULAR_MODE },
+            { label: "Fixed equirectangular", value: Texture.FIXED_EQUIRECTANGULAR_MODE },
+            { label: "Fixed equirectangular mirrored", value: Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE },
+            { label: "Planar", value: Texture.PLANAR_MODE },
+            { label: "Projection", value: Texture.PROJECTION_MODE },
+            { label: "Skybox", value: Texture.SKYBOX_MODE },
+            { label: "Spherical", value: Texture.SPHERICAL_MODE },
+        ];
+
+        let extension = "";
+        let url = (texture as Texture).url;
+        let textureUrl = (!url || url.substring(0, 4) === "data" || url.substring(0, 4) === "blob") ? "" : url;
+
+        if (textureUrl) {
+            for (var index = textureUrl.length - 1; index >= 0; index--) {
+                if (textureUrl[index] === ".") {
+                    break;
+                }
+                extension = textureUrl[index] + extension;
+            }
+        }
+
+        const editable = texture.textureType != Engine.TEXTURETYPE_FLOAT && texture.textureType != Engine.TEXTURETYPE_FLOAT_32_UNSIGNED_INT_24_8_REV && texture.textureType !== Engine.TEXTURETYPE_HALF_FLOAT;
+
+        return (
+            <div className="pane">
+                <LineContainerComponent globalState={this.props.globalState} title="PREVIEW">
+                    <TextureLineComponent ref={this.textureLineRef} texture={texture} width={256} height={256} globalState={this.props.globalState} />
+                    <FileButtonLineComponent label="Load texture from file" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
+                    {editable &&
+                        <ButtonLineComponent label="View" onClick={() => this.onOpenTextureEditor()} />
+                    }
+                    <TextInputLineComponent label="URL" value={textureUrl} lockObject={this.props.lockObject} onChange={url => {
+                        (texture as Texture).updateURL(url);
+                        this.forceRefresh();
+                    }} />
+                </LineContainerComponent>
+                {this._isTextureEditorOpen && (
+                <PopupComponent
+                  id='texture-editor'
+                  title='Texture Editor'
+                  size={{ width: 1024, height: 490 }}
+                  onOpen={(window: Window) => {}}
+                  onClose={(window: Window) =>
+                    this.onCloseTextureEditor(window)
+                  }
+                >
+                    <TextureEditorComponent
+                        globalState={this.props.globalState}
+                        texture={this.props.texture}
+                    />
+                </PopupComponent>)}
+                <CustomPropertyGridComponent globalState={this.props.globalState} target={texture}
+                    lockObject={this.props.lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
+                    <TextLineComponent label="Width" value={texture.getSize().width.toString()} />
+                    <TextLineComponent label="Height" value={texture.getSize().height.toString()} />
+                    {
+                        texture.isRenderTarget &&
+                        <ButtonLineComponent label="Scale up" onClick={() => {
+                            let scene = texture.getScene()!;
+                            texture.scale(2);
+                            setTimeout(() => {
+                                this.props.globalState.onSelectionChangedObservable.notifyObservers(scene.getTextureByUniqueID(texture.uniqueId));
+                            });
+                        }} />
+                    }
+                    {
+                        texture.isRenderTarget &&
+                        <ButtonLineComponent label="Scale down" onClick={() => {                            
+                            let scene = texture.getScene()!;
+                            texture.scale(0.5);
+                            setTimeout(() => {
+                                this.props.globalState.onSelectionChangedObservable.notifyObservers(scene.getTextureByUniqueID(texture.uniqueId));
+                            });
+                        }} />
+                    }
+                    {
+                        extension &&
+                        <TextLineComponent label="File format" value={extension} />
+                    }
+                    <TextLineComponent label="Unique ID" value={texture.uniqueId.toString()} />
+                    <TextLineComponent label="Class" value={texture.getClassName()} />
+                    <TextLineComponent label="Has alpha" value={texture.hasAlpha ? "Yes" : "No"} />
+                    <TextLineComponent label="Is 3D" value={texture.is3D ? "Yes" : "No"} />
+                    <TextLineComponent label="Is 2D array" value={texture.is2DArray ? "Yes" : "No"} />
+                    <TextLineComponent label="Is cube" value={texture.isCube ? "Yes" : "No"} />
+                    <TextLineComponent label="Is render target" value={texture.isRenderTarget ? "Yes" : "No"} />
+                    {
+                        (texture instanceof Texture) && 
+                        <TextLineComponent label="Stored as inverted on Y" value={texture.invertY ? "Yes" : "No"} />
+                    }
+                    <TextLineComponent label="Has mipmaps" value={!texture.noMipmap ? "Yes" : "No"} />
+                    <SliderLineComponent label="UV set" target={texture} propertyName="coordinatesIndex" minimum={0} maximum={3} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} decimalCount={0} />
+                    <OptionsLineComponent label="Mode" options={coordinatesMode} target={texture} propertyName="coordinatesMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={(value) => texture.updateSamplingMode(value)} />
+                    <SliderLineComponent label="Level" target={texture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        texture.updateSamplingMode &&
+                        <OptionsLineComponent label="Sampling" options={samplingMode} target={texture} noDirectUpdate={true} propertyName="samplingMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={(value) => texture.updateSamplingMode(value)} />
+                    }
+                </LineContainerComponent>
+                {
+                    texture.getScene() &&
+                    <AnimationGridComponent globalState={this.props.globalState} animatable={texture} scene={texture.getScene()!} lockObject={this.props.lockObject} />
+                }
+                {
+                    (texture as any).rootContainer &&
+                    <LineContainerComponent globalState={this.props.globalState} title="ADVANCED TEXTURE PROPERTIES">
+                        <ValueLineComponent label="Last layout time" value={this._adtInstrumentation!.renderTimeCounter.current} units="ms" />
+                        <ValueLineComponent label="Last render time" value={this._adtInstrumentation!.layoutTimeCounter.current} units="ms" />
+                        <SliderLineComponent label="Render scale" minimum={0.1} maximum={5} step={0.1} target={texture} propertyName="renderScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <CheckBoxLineComponent label="Premultiply alpha" target={texture} propertyName="premulAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal width" target={texture} propertyName="idealWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal height" target={texture} propertyName="idealHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <CheckBoxLineComponent label="Use smallest ideal" target={texture} propertyName="useSmallestIdeal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <CheckBoxLineComponent label="Render at ideal size" target={texture} propertyName="renderAtIdealSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <CheckBoxLineComponent label="Invalidate Rect optimization" target={texture} propertyName="useInvalidateRectOptimization" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    </LineContainerComponent>
+                }
+                <LineContainerComponent globalState={this.props.globalState} title="TRANSFORM">
+                    {
+                        !texture.isCube &&
+                        <div>
+                            <FloatLineComponent lockObject={this.props.lockObject} label="U offset" target={texture} propertyName="uOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="V offset" target={texture} propertyName="vOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="U scale" target={texture} propertyName="uScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="V scale" target={texture} propertyName="vScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="U angle" useEuler={this.props.globalState.onlyUseEulers} target={texture} propertyName="uAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="V angle" useEuler={this.props.globalState.onlyUseEulers} target={texture} propertyName="vAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent lockObject={this.props.lockObject} label="W angle" useEuler={this.props.globalState.onlyUseEulers} target={texture} propertyName="wAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Clamp U" isSelected={() => texture.wrapU === Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapU = value ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE} />
+                            <CheckBoxLineComponent label="Clamp V" isSelected={() => texture.wrapV === Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapV = value ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE} />
+                        </div>
+                    }
+                    {
+                        texture.isCube &&
+                        <div>
+                            <SliderLineComponent label="Rotation Y" useEuler={this.props.globalState.onlyUseEulers} minimum={0} maximum={2 * Math.PI} step={0.1} target={texture} propertyName="rotationY" />
+                        </div>
+                    }
+                </LineContainerComponent>
+            </div>
+        );
+    }
 }

+ 265 - 156
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts

@@ -1,156 +1,265 @@
-import { Engine } from 'babylonjs/Engines/engine';
-import { Scene } from 'babylonjs/scene';
-import { Vector3 } from 'babylonjs/Maths/math.vector';
-import { Color4 } from 'babylonjs/Maths/math.color';
-import { FreeCamera } from 'babylonjs/Cameras/freeCamera';
-
-import { PlaneBuilder } from 'babylonjs/Meshes/Builders/planeBuilder';
-import { Mesh } from 'babylonjs/Meshes/mesh';
-import { Camera } from 'babylonjs/Cameras/camera';
-import { DynamicTexture } from 'babylonjs/Materials/Textures/dynamicTexture';
-import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
-import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
-
-import { PointerEventTypes } from 'babylonjs/Events/pointerEvents';
-import { KeyboardEventTypes } from 'babylonjs/Events/keyboardEvents';
-
-export class TextureCanvasManager {
-    private _engine: Engine;
-    private _scene: Scene;
-    private _texture: DynamicTexture;
-    private _camera: FreeCamera;
-    private _canvas : HTMLCanvasElement;
-
-    private _scale : number;
-    private _isPanning : boolean;
-    private _mouseX : number;
-    private _mouseY : number;
-
-    private _plane : Mesh;
-    private _planeMaterial : NodeMaterial;
-
-    private static ZOOM_MOUSE_SPEED : number = 0.0005;
-    private static ZOOM_KEYBOARD_SPEED : number = 0.2;
-    private static PAN_SPEED : number = 0.002;
-    private static PAN_BUTTON : number = 0; // left mouse button
-    private static MIN_SCALE : number = 0.01;
-    private static MAX_SCALE : number = 10;
-
-    public constructor(targetCanvas: HTMLCanvasElement, texture: BaseTexture) {
-        this._canvas = targetCanvas;
-
-        this._engine = new Engine(targetCanvas, true);
-        this._scene = new Scene(this._engine);
-        this._scene.clearColor = new Color4(0.2, 0.2, 0.2, 1.0);
-
-        this._camera = new FreeCamera("Camera", new Vector3(0, 0, -1), this._scene);
-        this._camera.mode = Camera.ORTHOGRAPHIC_CAMERA;
-
-        if (texture) {
-            /* Grab image data from original texture and paint it onto the context of a DynamicTexture */
-            const pixelData = texture.readPixels()!;
-            const arr = new Uint8ClampedArray(pixelData.buffer);
-            let imgData = new ImageData(arr, texture.getSize().width, texture.getSize().height);
-            this._texture = new DynamicTexture("texture", texture.getSize(), this._scene, false);
-            const ctx = this._texture.getContext();
-            ctx.putImageData(imgData, 0, 0);
-            this._texture.update();
-            this._texture.hasAlpha = texture.hasAlpha;
-        } else {
-            /* If we don't have a texture to start with, just generate a white rectangle */
-            this._texture = new DynamicTexture("texture",  256, this._scene, false);
-            const ctx = this._texture.getContext();
-            ctx.fillStyle = 'white';
-            ctx.fillRect(0, 0, 256, 256);
-            this._texture.update();
-        }
-
-        this._texture.updateSamplingMode(Engine.TEXTURE_NEAREST_LINEAR);
-        const textureRatio = this._texture.getSize().width / this._texture.getSize().height;
-
-        this._plane = PlaneBuilder.CreatePlane("plane", {width: textureRatio, height: 1}, this._scene);
-        NodeMaterial.ParseFromSnippetAsync("#TPSEV2#3", this._scene)
-            .then((material) => {
-                this._planeMaterial = material;
-                this._planeMaterial.getTextureBlocks()[0].texture = this._texture;
-                this._plane.material = this._planeMaterial;
-                this._canvas.focus();
-            });
-        this._plane.enableEdgesRendering();
-        this._plane.edgesWidth = 4.0;
-        this._plane.edgesColor = new Color4(1,1,1,1);
-
-        this._engine.runRenderLoop(() => {
-            this._engine.resize();
-            this._scene.render();
-
-        });
-
-        this._scale = 1;
-        this._isPanning = false;
-
-        this._scene.onBeforeRenderObservable.add(() => {
-            let ratio = this._canvas?.width / this._canvas?.height;
-            this._camera.orthoBottom = -this._scale;
-            this._camera.orthoTop = this._scale;
-            this._camera.orthoLeft = -this._scale * ratio;
-            this._camera.orthoRight = this._scale * ratio;
-        })
-
-        this._scene.onPointerObservable.add((pointerInfo) => {
-            switch (pointerInfo.type) {
-                case PointerEventTypes.POINTERWHEEL:
-                    const event = pointerInfo.event as MouseWheelEvent;
-                    this._scale += (event.deltaY * TextureCanvasManager.ZOOM_MOUSE_SPEED * this._scale);
-                    this._scale = Math.min(Math.max(this._scale, TextureCanvasManager.MIN_SCALE), TextureCanvasManager.MAX_SCALE);
-                    break;
-                case PointerEventTypes.POINTERDOWN:
-                    if (pointerInfo.event.button === TextureCanvasManager.PAN_BUTTON) {
-                        this._isPanning = true;
-                        this._mouseX = pointerInfo.event.x;
-                        this._mouseY = pointerInfo.event.y;
-                    }
-                    break;
-                case PointerEventTypes.POINTERUP:
-                    if (pointerInfo.event.button === TextureCanvasManager.PAN_BUTTON) {
-                        this._isPanning = false;
-                    }
-                    break;
-                case PointerEventTypes.POINTERMOVE:
-                    if (this._isPanning) {
-                        this._camera.position.x -= (pointerInfo.event.x - this._mouseX) * this._scale * TextureCanvasManager.PAN_SPEED;
-                        this._camera.position.y += (pointerInfo.event.y - this._mouseY) * this._scale * TextureCanvasManager.PAN_SPEED;
-                        this._mouseX = pointerInfo.event.x;
-                        this._mouseY = pointerInfo.event.y;
-                    }
-                    break;
-            }
-        })
-
-        this._scene.onKeyboardObservable.add((kbInfo) => {
-            switch(kbInfo.type) {
-                case KeyboardEventTypes.KEYDOWN:
-                    if (kbInfo.event.key == "+") {
-                        this._scale -= TextureCanvasManager.ZOOM_KEYBOARD_SPEED * this._scale;
-                    }
-                    if (kbInfo.event.key == "-") {
-                        this._scale += TextureCanvasManager.ZOOM_KEYBOARD_SPEED * this._scale;
-                    }
-                    this._scale = Math.min(Math.max(this._scale, TextureCanvasManager.MIN_SCALE), TextureCanvasManager.MAX_SCALE);
-                    break;
-            }
-        })
-
-    }
-
-    public dispose() {
-        if (this._planeMaterial) {
-            this._planeMaterial.dispose();
-        }
-        this._texture.dispose();
-        this._plane.dispose();
-        this._camera.dispose();
-        this._scene.dispose();
-        this._engine.dispose();
-    }
-} 
+import { Engine } from 'babylonjs/Engines/engine';
+import { Scene } from 'babylonjs/scene';
+import { Vector3 } from 'babylonjs/Maths/math.vector';
+import { Color4 } from 'babylonjs/Maths/math.color';
+import { FreeCamera } from 'babylonjs/Cameras/freeCamera';
+import { Nullable } from 'babylonjs/types'
+
+import { PlaneBuilder } from 'babylonjs/Meshes/Builders/planeBuilder';
+import { Mesh } from 'babylonjs/Meshes/mesh';
+import { Camera } from 'babylonjs/Cameras/camera';
+import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
+import { HtmlElementTexture } from 'babylonjs/Materials/Textures/htmlElementTexture';
+import { InternalTexture } from 'babylonjs/Materials/Textures/internalTexture';
+import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
+import { TextureHelper, TextureChannelToDisplay } from '../../../../../../textureHelper';
+import { ISize } from 'babylonjs/Maths/math.size';
+
+
+import { PointerEventTypes } from 'babylonjs/Events/pointerEvents';
+import { KeyboardEventTypes } from 'babylonjs/Events/keyboardEvents';
+
+export class TextureCanvasManager {
+    private _engine: Engine;
+    private _scene: Scene;
+    private _camera: FreeCamera;
+
+    private _scale : number;
+    private _isPanning : boolean = false;
+    private _mouseX : number;
+    private _mouseY : number;
+
+    private _UICanvas : HTMLCanvasElement;
+
+    private _size : ISize;
+
+    /* This is the canvas we paint onto using the canvas API */
+    private _2DCanvas : HTMLCanvasElement;
+    /* The texture we are currently editing, which is based on _2DCanvas */
+    private _texture: HtmlElementTexture;
+
+    private _displayCanvas : HTMLCanvasElement;
+    private _displayChannel : TextureChannelToDisplay = TextureChannelToDisplay.All;
+    /* This is the actual texture that is being displayed. Sometimes it's just a single channel from _textures */
+    private _displayTexture : HtmlElementTexture;
+
+    /* The texture from the original engine that we invoked the editor on */
+    private _originalTexture: BaseTexture;
+    /* This is a hidden texture which is only responsible for holding the actual texture memory in the original engine */
+    private _targetTexture : Nullable<HtmlElementTexture> = null;
+    /* The internal texture representation of the original texture */
+    private _originalInternalTexture : Nullable<InternalTexture> = null;
+
+    private _plane : Mesh;
+    private _planeMaterial : NodeMaterial;
+
+    /* Tracks which keys are currently pressed */
+    private keyMap : any = {};
+
+    private static ZOOM_MOUSE_SPEED : number = 0.0005;
+    private static ZOOM_KEYBOARD_SPEED : number = 0.2;
+    private static ZOOM_IN_KEY : string = '+';
+    private static ZOOM_OUT_KEY : string = '-';
+
+    private static PAN_SPEED : number = 0.002;
+    private static PAN_MOUSE_BUTTON : number = 0; // RMB
+    private static PAN_KEY : string = ' ';
+
+    private static MIN_SCALE : number = 0.01;
+    private static MAX_SCALE : number = 10;
+
+    public metadata : any = {
+        color: '#ffffff',
+        opacity: 1.0
+    };
+
+    public constructor(texture: BaseTexture, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvasDisplay: HTMLCanvasElement) {
+        this._UICanvas = canvasUI;
+        this._2DCanvas = canvas2D;
+        this._displayCanvas = canvasDisplay;
+
+        this._originalTexture = texture;
+        this._size = this._originalTexture.getSize();
+
+        this._engine = new Engine(this._UICanvas, true);
+        this._scene = new Scene(this._engine);
+        this._scene.clearColor = new Color4(0.2, 0.2, 0.2, 1.0);
+
+        this._camera = new FreeCamera("Camera", new Vector3(0, 0, -1), this._scene);
+        this._camera.mode = Camera.ORTHOGRAPHIC_CAMERA;
+        this._texture = new HtmlElementTexture("texture", this._2DCanvas, {engine: this._engine, scene: this._scene});
+        if (texture) {
+            /* Grab image data from original texture and paint it onto the context of a DynamicTexture */
+            const pixelData = this._originalTexture.readPixels()!;
+            TextureCanvasManager.paintPixelsOnCanvas(new Uint8Array(pixelData.buffer), this._2DCanvas);
+            this._texture.update();
+        }
+
+        this._displayTexture = new HtmlElementTexture("display", this._displayCanvas, {engine: this._engine, scene: this._scene});
+        this.copyTextureToDisplayTexture();
+        this._displayTexture.updateSamplingMode(Engine.TEXTURE_NEAREST_LINEAR);
+
+        const textureRatio = this._size.width / this._size.height;
+        
+        this._plane = PlaneBuilder.CreatePlane("plane", {width: textureRatio, height: 1}, this._scene);
+        NodeMaterial.ParseFromSnippetAsync("#TPSEV2#4", this._scene)
+            .then((material) => {
+                this._planeMaterial = material;
+                this._planeMaterial.getTextureBlocks()[0].texture = this._displayTexture;
+                this._plane.material = this._planeMaterial;
+                this._UICanvas.focus();
+            });
+        this._plane.enableEdgesRendering();
+        this._plane.edgesWidth = 4.0;
+        this._plane.edgesColor = new Color4(1,1,1,1);
+        this._plane.enablePointerMoveEvents = true;
+
+        this._engine.runRenderLoop(() => {
+            this._engine.resize();
+            this._scene.render();
+            let cursor = 'initial';
+            if (this.keyMap[TextureCanvasManager.PAN_KEY]) {
+                cursor = 'pointer';
+            }
+            this._UICanvas.parentElement!.style.cursor = cursor;
+        });
+
+        this._scale = 1;
+        this._isPanning = false;
+
+        this._scene.onBeforeRenderObservable.add(() => {
+            this._scale = Math.min(Math.max(this._scale, TextureCanvasManager.MIN_SCALE), TextureCanvasManager.MAX_SCALE);
+            const ratio = this._UICanvas?.width / this._UICanvas?.height;
+            this._camera.orthoBottom = -this._scale;
+            this._camera.orthoTop = this._scale;
+            this._camera.orthoLeft = -this._scale * ratio;
+            this._camera.orthoRight = this._scale * ratio;
+        })
+
+        this._scene.onPointerObservable.add((pointerInfo) => {
+            switch (pointerInfo.type) {
+                case PointerEventTypes.POINTERWHEEL:
+                    const event = pointerInfo.event as MouseWheelEvent;
+                    this._scale += (event.deltaY * TextureCanvasManager.ZOOM_MOUSE_SPEED * this._scale);
+                    break;
+                case PointerEventTypes.POINTERDOWN:
+                    if (pointerInfo.event.button === TextureCanvasManager.PAN_MOUSE_BUTTON && this.keyMap[TextureCanvasManager.PAN_KEY]) {
+                        this._isPanning = true;
+                        this._mouseX = pointerInfo.event.x;
+                        this._mouseY = pointerInfo.event.y;
+                        pointerInfo.event.preventDefault();
+                    }
+                    break;
+                case PointerEventTypes.POINTERUP:
+                    if (pointerInfo.event.button === TextureCanvasManager.PAN_MOUSE_BUTTON) {
+                        this._isPanning = false;
+                    }
+                    break;
+                case PointerEventTypes.POINTERMOVE:
+                    if (this._isPanning) {
+                        this._camera.position.x -= (pointerInfo.event.x - this._mouseX) * this._scale * TextureCanvasManager.PAN_SPEED;
+                        this._camera.position.y += (pointerInfo.event.y - this._mouseY) * this._scale * TextureCanvasManager.PAN_SPEED;
+                        this._mouseX = pointerInfo.event.x;
+                        this._mouseY = pointerInfo.event.y;
+                    }
+                    break;
+            }
+        })
+
+        this._scene.onKeyboardObservable.add((kbInfo) => {
+            switch(kbInfo.type) {
+                case KeyboardEventTypes.KEYDOWN:
+                    this.keyMap[kbInfo.event.key] = true;
+                    if (kbInfo.event.key === TextureCanvasManager.ZOOM_IN_KEY) {
+                        this._scale -= TextureCanvasManager.ZOOM_KEYBOARD_SPEED * this._scale;
+                    }
+                    if (kbInfo.event.key === TextureCanvasManager.ZOOM_OUT_KEY) {
+                        this._scale += TextureCanvasManager.ZOOM_KEYBOARD_SPEED * this._scale;
+                    }
+                    break;
+                case KeyboardEventTypes.KEYUP:
+                    this.keyMap[kbInfo.event.key] = false;
+                    if (kbInfo.event.key == TextureCanvasManager.PAN_KEY) {
+                        this._isPanning = false;
+                    }
+                break;
+            }
+        })
+
+    }
+
+    public updateTexture() {
+        this._texture.update();
+        if (!this._targetTexture) {
+            this._originalInternalTexture = this._originalTexture._texture;
+            this._targetTexture = new HtmlElementTexture("editor", this._2DCanvas, {engine: this._originalTexture.getScene()?.getEngine()!, scene: null});
+        }
+        this._targetTexture.update();
+        this._originalTexture._texture = this._targetTexture._texture;
+        this.copyTextureToDisplayTexture();
+    }
+
+    private copyTextureToDisplayTexture() {
+        TextureHelper.GetTextureDataAsync(this._texture, this._size.width, this._size.height, 0, this._displayChannel)
+            .then(data => {
+                TextureCanvasManager.paintPixelsOnCanvas(data, this._displayCanvas);
+                this._displayTexture.update();
+            })
+    }
+
+    public set displayChannel(channel: TextureChannelToDisplay) {
+        this._displayChannel = channel;
+        this.copyTextureToDisplayTexture();
+    }
+
+    public get displayChannel() : TextureChannelToDisplay {
+        return this._displayChannel;
+    }
+
+    public static paintPixelsOnCanvas(pixelData : Uint8Array, canvas: HTMLCanvasElement) {
+        const ctx = canvas.getContext('2d')!;
+        const imgData = ctx.createImageData(canvas.width, canvas.height);
+        imgData.data.set(pixelData);
+        ctx.putImageData(imgData, 0, 0);
+        TextureCanvasManager.flipCanvas(canvas);
+    }
+
+    /* When copying from a WebGL texture to a Canvas, the y axis is inverted. This function flips it back */
+    public static flipCanvas(canvas: HTMLCanvasElement) {
+        const ctx = canvas.getContext('2d')!;
+        const transform = ctx.getTransform();
+        ctx.globalCompositeOperation = 'copy';
+        ctx.globalAlpha = 1.0;
+        ctx.translate(0,canvas.height);
+        ctx.scale(1,-1);
+        ctx.drawImage(canvas, 0, 0);
+        ctx.setTransform(transform);
+    }
+
+    public get scene() : Scene {
+        return this._scene;
+    }
+
+    public get canvas2D() : HTMLCanvasElement {
+        return this._2DCanvas;
+    }
+
+    public get size() : ISize {
+        return this._size;
+    }
+
+    public dispose() {
+        if (this._planeMaterial) {
+            this._planeMaterial.dispose();
+        }
+        if (this._originalInternalTexture) {
+            this._originalInternalTexture.dispose();
+        }
+        this._displayTexture.dispose();
+        this._texture.dispose();
+        this._plane.dispose();
+        this._camera.dispose();
+        this._scene.dispose();
+        this._engine.dispose();
+    }
+} 

+ 109 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss

@@ -1,4 +1,110 @@
-#texture-canvas {
-    width: 100%;
-    height: 100%;
+#texture-editor {
+    display: grid;
+    height: 100%;
+    width: 100%;
+    grid-template-columns: auto auto auto auto auto;
+    grid-template-rows: 60px calc(100% - 60px);
+}
+
+#controls {
+    width: 100%;
+    grid-row: 1;
+    grid-column: 1 / 6;
+    background: #464646;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: space-around;
+    
+    #toolbar {
+        display: flex;
+        flex-direction: column;
+        justify-content: left;
+        
+        #tools {
+            display: flex;
+            flex-direction: row;
+            justify-content: center;
+            form {
+                margin: 0;
+            }
+        }
+
+        #color {
+            label {
+                color: white;
+                margin: 1em;
+            }
+        }
+    }
+    
+    #channels {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        align-items: center;
+
+        .command {
+            border: 1px solid transparent;
+            background:transparent;
+            font-size: 20px;
+        }
+    
+        .selected {
+            border: 1px solid rgb(51, 122, 183);
+        }
+    }
+}
+
+#editing-area {
+    grid-row: 2;
+    grid-column: 1 / 6;
+    #canvas-ui {
+        width: 100%;
+        height: 100%;
+    }
+}
+
+button, select {
+    background: #222222;
+    border: 1px solid rgb(51, 122, 183);
+    margin: 5px 10px 5px 10px;
+    color:white;
+    padding: 4px 5px;
+    opacity: 0.9;
+    cursor: pointer;
+}
+
+button:hover, select:hover {
+    opacity: 1.0;
+}
+
+button:active {
+    background: #282828;
+}   
+
+button:focus, select:focus {
+    border: 1px solid rgb(51, 122, 183);
+    outline: 0px;
+} 
+
+input[type="text"] {
+    border: none;
+    padding: 0;
+    border-bottom: solid 1px rgb(51, 122, 183);
+    background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 96%, rgb(51, 122, 183) 4%);
+    background-position: -1000px 0;
+    background-size: 1000px 100%;
+    background-repeat: no-repeat;  
+    color:white;    
+}
+
+input[type="text"]::placeholder {
+    color: lightgray;
+}
+
+input[type="text"]:focus  {
+    box-shadow: none;
+    outline: none;
+    background-position: 0 0;
 }

+ 74 - 29
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx

@@ -1,30 +1,75 @@
-import * as React from 'react';
-import { GlobalState } from '../../../../../globalState';
-import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
-import { TextureCanvasManager } from './textureCanvasManager';
-
-require('./textureEditor.scss');
-
-interface TextureEditorComponentProps {
-    globalState: GlobalState;
-    texture: BaseTexture;
-}
-
-export class TextureEditorComponent extends React.Component<TextureEditorComponentProps> {
-    private _textureCanvasManager: TextureCanvasManager;
-    private reactCanvas = React.createRef<HTMLCanvasElement>();
-
-    componentDidMount() {
-        this._textureCanvasManager = new TextureCanvasManager(this.reactCanvas.current!, this.props.texture);
-    }
-
-    componentWillUnmount() {
-        this._textureCanvasManager.dispose();
-    }
-
-    render() {
-        return <div id='texture-editor'>
-            <canvas id="texture-canvas" ref={this.reactCanvas} tabIndex={1}></canvas>
-        </div>
-    }
+import * as React from 'react';
+import { GlobalState } from '../../../../../globalState';
+import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
+import { TextureCanvasManager } from './textureCanvasManager';
+import { TextureChannelToDisplay } from '../../../../../../textureHelper';
+
+require('./textureEditor.scss');
+
+interface TextureEditorComponentProps {
+    globalState: GlobalState;
+    texture: BaseTexture;
+}
+
+interface TextureEditorComponentState {
+    channel: TextureChannelToDisplay;
+}
+
+export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
+    private _textureCanvasManager: TextureCanvasManager;
+    private canvasUI = React.createRef<HTMLCanvasElement>();
+    private canvas2D = React.createRef<HTMLCanvasElement>();
+    private canvasDisplay = React.createRef<HTMLCanvasElement>();
+
+    private channels = [
+        {name: "RGBA", channel: TextureChannelToDisplay.All, className: "all"},
+        {name: "R", channel: TextureChannelToDisplay.R, className: "red"},
+        {name: "G", channel: TextureChannelToDisplay.G, className: "green"},
+        {name: "B", channel: TextureChannelToDisplay.B, className: "blue"},
+        {name: "A", channel: TextureChannelToDisplay.A, className: "alpha"},
+    ]
+
+    constructor(props : TextureEditorComponentProps) {
+        super(props);
+        this.state = {
+            channel: TextureChannelToDisplay.All,
+        }
+    }
+
+    componentDidMount() {
+        this._textureCanvasManager = new TextureCanvasManager(
+            this.props.texture,
+            this.canvasUI.current!,
+            this.canvas2D.current!,
+            this.canvasDisplay.current!
+        );
+    }
+
+    componentDidUpdate() {
+        this._textureCanvasManager.displayChannel = this.state.channel;
+    }
+
+    componentWillUnmount() {
+        this._textureCanvasManager.dispose();
+    }
+
+    render() {
+        return <div id="texture-editor">
+            <div id="controls">
+                <div id="channels">
+                    {this.channels.map(
+                        item => {
+                            const classNames = (item.channel === this.state.channel) ? "selected command " + item.className : "command " + item.className;
+                            return <button className={classNames} key={item.name} onClick={() => this.setState({channel: item.channel})}>{item.name}</button>
+                        }
+                    )}
+                </div>
+            </div>
+            <div id="editing-area">
+                <canvas id="canvas-ui" ref={this.canvasUI} tabIndex={1}></canvas>
+            </div>
+            <canvas id="canvas-display" ref={this.canvasDisplay} width={this.props.texture.getSize().width} height={this.props.texture.getSize().height} hidden={true}></canvas>
+            <canvas id="canvas-2D" ref={this.canvas2D} width={this.props.texture.getSize().width} height={this.props.texture.getSize().height} hidden={true}></canvas>
+        </div>
+    }
 }

+ 8 - 1
nodeEditor/src/diagram/properties/genericNodePropertyComponent.tsx

@@ -39,7 +39,14 @@ export class GeneralPropertyTabComponent extends React.Component<IPropertyCompon
                     {
                         (!this.props.block.isInput || !(this.props.block as InputBlock).isAttribute) &&
                         <TextInputLineComponent globalState={this.props.globalState} label="Name" propertyName="name" target={this.props.block}
-                            onChange={() => this.props.globalState.onUpdateRequiredObservable.notifyObservers()} />
+                            onChange={() => this.props.globalState.onUpdateRequiredObservable.notifyObservers()}
+                            validator={ newName => 
+                            {if(!this.props.block.validateBlockName(newName)){ 
+                                this.props.globalState.onErrorMessageDialogRequiredObservable.notifyObservers(`"${newName}" is a reserved name, please choose another`);
+                                return false;
+                            }
+                            return true;
+                            }} />
                     }
                     <TextLineComponent label="Type" value={this.props.block.getClassName()} />
                     <TextInputLineComponent globalState={this.props.globalState} label="Comments" propertyName="comments" target={this.props.block}

+ 8 - 0
nodeEditor/src/sharedComponents/textInputLineComponent.tsx

@@ -10,6 +10,7 @@ interface ITextInputLineComponentProps {
     propertyName?: string;
     value?: string;
     onChange?: (value: string) => void;
+    validator?: (value: string) => boolean;
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
 }
 
@@ -58,6 +59,13 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
 
         this._localChange = true;
         const store = this.props.value !== undefined ? this.props.value : this.props.target[this.props.propertyName!];
+
+        if(this.props.validator && raisePropertyChanged) {
+            if(this.props.validator(value) == false) {
+                value = store;
+            }
+        }
+
         this.setState({ value: value });
 
         if (raisePropertyChanged) {

+ 2 - 2
package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.2.0-alpha.23",
+    "version": "4.2.0-alpha.25",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -113,4 +113,4 @@
         "xhr2": "^0.2.0",
         "xmlbuilder": "15.1.1"
     }
-}
+}

+ 1 - 0
readme-es6.md

@@ -55,6 +55,7 @@ import "@babylonjs/core/Materials/standardMaterial";
 // Side-effects only imports allowing Mesh to create default shapes (to enhance tree shaking, the construction methods on mesh are not available if the meshbuilder has not been imported).
 import "@babylonjs/core/Meshes/Builders/sphereBuilder";
 import "@babylonjs/core/Meshes/Builders/boxBuilder";
+import "@babylonjs/core/Meshes/Builders/groundBuilder";
 
 const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
 const engine = new Engine(canvas);

+ 3 - 3
serializers/src/glTF/2.0/glTFAnimation.ts

@@ -741,10 +741,10 @@ export class _GLTFAnimation {
             if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
                 if (tangentValue) {
                     if (useQuaternion) {
-                        tangent = (tangentValue as Quaternion).scale(frameDelta).asArray();
+                        tangent = (tangentValue as Quaternion).asArray();
                     }
                     else {
-                        const array = (tangentValue as Vector3).scale(frameDelta);
+                        const array = (tangentValue as Vector3);
                         tangent = Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                     }
 
@@ -761,7 +761,7 @@ export class _GLTFAnimation {
             }
             else {
                 if (tangentValue) {
-                    tangent = (tangentValue as Vector3).scale(frameDelta).asArray();
+                    tangent = (tangentValue as Vector3).asArray();
                     if (convertToRightHandedSystem) {
                         if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
                             _GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);

+ 229 - 63
serializers/src/glTF/2.0/glTFExporter.ts

@@ -1,4 +1,4 @@
-import { AccessorType, IBufferView, IAccessor, INode, IScene, IMesh, IMaterial, ITexture, IImage, ISampler, IAnimation, ImageMimeType, IMeshPrimitive, IBuffer, IGLTF, MeshPrimitiveMode, AccessorComponentType, ITextureInfo } from "babylonjs-gltf2interface";
+import { AccessorType, IBufferView, IAccessor, INode, IScene, IMesh, IMaterial, ITexture, IImage, ISampler, IAnimation, ImageMimeType, IMeshPrimitive, IBuffer, IGLTF, MeshPrimitiveMode, AccessorComponentType, ITextureInfo, ISkin } from "babylonjs-gltf2interface";
 
 import { FloatArray, Nullable, IndicesArray } from "babylonjs/types";
 import { Vector2, Vector3, Vector4, Quaternion, Matrix } from "babylonjs/Maths/math.vector";
@@ -44,6 +44,11 @@ interface _IVertexAttributeData {
     accessorType: AccessorType;
 
     /**
+     * Specifies the glTF Accessor Component Type (BYTE, UNSIGNED_BYTE, FLOAT, SHORT, INT, etc..)
+     */
+    accessorComponentType: AccessorComponentType;
+
+    /**
      * Specifies the BufferView index for the vertex attribute data
     */
     bufferViewIndex?: number;
@@ -99,6 +104,10 @@ export class _Exporter {
      */
     public _samplers: ISampler[];
     /**
+     * Stores all the generated glTF skins
+     */
+    public _skins: ISkin[];
+    /**
      * Stores all the generated animation samplers, which is referenced by glTF animations
      */
     /**
@@ -288,6 +297,7 @@ export class _Exporter {
         this._materialMap = [];
         this._textures = [];
         this._samplers = [];
+        this._skins = [];
         this._animations = [];
         this._imageData = {};
         this._options = options || {};
@@ -667,12 +677,11 @@ export class _Exporter {
      * Returns the bytelength of the data
      * @param vertexBufferKind Indicates what kind of vertex data is being passed in
      * @param meshAttributeArray Array containing the attribute data
+     * @param byteStride Specifies the space between data
      * @param binaryWriter The buffer to write the binary data to
-     * @param indices Used to specify the order of the vertex data
      * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    public writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean) {
-        const stride = byteStride / 4;
+    public writeAttributeData(vertexBufferKind: string, attributeComponentKind: AccessorComponentType, meshAttributeArray: FloatArray, stride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean, babylonTransformNode: TransformNode) {
         let vertexAttributes: number[][] = [];
         let index: number;
 
@@ -729,14 +738,56 @@ export class _Exporter {
                 }
                 break;
             }
+            case VertexBuffer.MatricesIndicesKind:
+            case VertexBuffer.MatricesIndicesExtraKind: {
+                for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
+                    index = k * stride;
+                    const vertexData = Vector4.FromArray(meshAttributeArray, index);
+                    vertexAttributes.push(vertexData.asArray());
+                }
+                break;
+            }
+            case VertexBuffer.MatricesWeightsKind:
+            case VertexBuffer.MatricesWeightsExtraKind: {
+                for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
+                    index = k * stride;
+                    const vertexData = Vector4.FromArray(meshAttributeArray, index);
+                    vertexAttributes.push(vertexData.asArray());
+                }
+                break;
+            }
             default: {
                 Tools.Warn("Unsupported Vertex Buffer Type: " + vertexBufferKind);
                 vertexAttributes = [];
             }
         }
+
+        let writeBinaryFunc;
+        switch (attributeComponentKind){
+            case AccessorComponentType.UNSIGNED_BYTE: {
+                writeBinaryFunc = binaryWriter.setUInt8.bind(binaryWriter);
+                break;
+            }
+            case AccessorComponentType.UNSIGNED_SHORT: {
+                writeBinaryFunc = binaryWriter.setUInt16.bind(binaryWriter);
+                break;
+            }
+            case AccessorComponentType.UNSIGNED_INT: {
+                writeBinaryFunc = binaryWriter.setUInt32.bind(binaryWriter);
+            }
+            case AccessorComponentType.FLOAT: {
+                writeBinaryFunc = binaryWriter.setFloat32.bind(binaryWriter);
+                break;
+            }
+            default: {
+                Tools.Warn("Unsupported Attribute Component kind: " + attributeComponentKind);
+                return;
+            }
+        }
+
         for (let vertexAttribute of vertexAttributes) {
             for (let component of vertexAttribute) {
-                binaryWriter.setFloat32(component);
+                writeBinaryFunc(component);
             }
         }
     }
@@ -786,6 +837,9 @@ export class _Exporter {
         if (this._samplers && this._samplers.length) {
             this._glTF.samplers = this._samplers;
         }
+        if (this._skins && this._skins.length) {
+            this._glTF.skins = this._skins;
+        }
         if (this._images && this._images.length) {
             if (!shouldUseGlb) {
                 this._glTF.images = this._images;
@@ -1022,29 +1076,34 @@ export class _Exporter {
     /**
      * Creates a bufferview based on the vertices type for the Babylon mesh
      * @param kind Indicates the type of vertices data
+     * @param componentType Indicates the numerical type used to store the data
      * @param babylonTransformNode The Babylon mesh to get the vertices data from
      * @param binaryWriter The buffer to write the bufferview data to
      * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private createBufferViewKind(kind: string, babylonTransformNode: TransformNode, binaryWriter: _BinaryWriter, byteStride: number, convertToRightHandedSystem: boolean) {
+    private createBufferViewKind(kind: string, attributeComponentKind: AccessorComponentType, babylonTransformNode: TransformNode, binaryWriter: _BinaryWriter, byteStride: number, convertToRightHandedSystem: boolean) {
         const bufferMesh = babylonTransformNode instanceof Mesh ?
             babylonTransformNode as Mesh : babylonTransformNode instanceof InstancedMesh ?
                 (babylonTransformNode as InstancedMesh).sourceMesh : null;
 
         if (bufferMesh) {
+            const vertexBuffer = bufferMesh.getVertexBuffer(kind);
             const vertexData = bufferMesh.getVerticesData(kind);
 
-            if (vertexData) {
-                const byteLength = vertexData.length * 4;
+            if (vertexBuffer && vertexData) {
+                const typeByteLength = VertexBuffer.GetTypeByteLength(attributeComponentKind);
+                const byteLength = vertexData.length * typeByteLength;
                 const bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                 this._bufferViews.push(bufferView);
 
                 this.writeAttributeData(
                     kind,
+                    attributeComponentKind,
                     vertexData,
-                    byteStride,
+                    byteStride / typeByteLength,
                     binaryWriter,
-                    convertToRightHandedSystem
+                    convertToRightHandedSystem,
+                    babylonTransformNode
                 );
             }
         }
@@ -1134,6 +1193,22 @@ export class _Exporter {
                 meshPrimitive.attributes.TEXCOORD_1 = this._accessors.length - 1;
                 break;
             }
+            case VertexBuffer.MatricesIndicesKind: {
+                meshPrimitive.attributes.JOINTS_0 = this._accessors.length - 1;
+                break;
+            }
+            case VertexBuffer.MatricesIndicesExtraKind: {
+                meshPrimitive.attributes.JOINTS_1 = this._accessors.length - 1;
+                break;
+            }
+            case VertexBuffer.MatricesWeightsKind: {
+                meshPrimitive.attributes.WEIGHTS_0 = this._accessors.length - 1;
+                break;
+            }
+            case VertexBuffer.MatricesWeightsExtraKind: {
+                meshPrimitive.attributes.WEIGHTS_1 = this._accessors.length - 1;
+                break;
+            }
             default: {
                 Tools.Warn("Unsupported Vertex Buffer Type: " + attributeKind);
             }
@@ -1160,12 +1235,16 @@ export class _Exporter {
             bufferMesh = (babylonTransformNode as InstancedMesh).sourceMesh;
         }
         const attributeData: _IVertexAttributeData[] = [
-            { kind: VertexBuffer.PositionKind, accessorType: AccessorType.VEC3, byteStride: 12 },
-            { kind: VertexBuffer.NormalKind, accessorType: AccessorType.VEC3, byteStride: 12 },
-            { kind: VertexBuffer.ColorKind, accessorType: AccessorType.VEC4, byteStride: 16 },
-            { kind: VertexBuffer.TangentKind, accessorType: AccessorType.VEC4, byteStride: 16 },
-            { kind: VertexBuffer.UVKind, accessorType: AccessorType.VEC2, byteStride: 8 },
-            { kind: VertexBuffer.UV2Kind, accessorType: AccessorType.VEC2, byteStride: 8 },
+            { kind: VertexBuffer.PositionKind, accessorType: AccessorType.VEC3, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 12 },
+            { kind: VertexBuffer.NormalKind, accessorType: AccessorType.VEC3, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 12 },
+            { kind: VertexBuffer.ColorKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 16 },
+            { kind: VertexBuffer.TangentKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 16 },
+            { kind: VertexBuffer.UVKind, accessorType: AccessorType.VEC2, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 8  },
+            { kind: VertexBuffer.UV2Kind, accessorType: AccessorType.VEC2, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 8  },
+            { kind: VertexBuffer.MatricesIndicesKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.UNSIGNED_SHORT, byteStride: 8 },
+            { kind: VertexBuffer.MatricesIndicesExtraKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.UNSIGNED_SHORT, byteStride: 8 },
+            { kind: VertexBuffer.MatricesWeightsKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 16 },
+            { kind: VertexBuffer.MatricesWeightsExtraKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 16 },
         ];
 
         if (bufferMesh) {
@@ -1176,14 +1255,15 @@ export class _Exporter {
             // For each BabylonMesh, create bufferviews for each 'kind'
             for (const attribute of attributeData) {
                 const attributeKind = attribute.kind;
+                const attributeComponentKind = attribute.accessorComponentType;
                 if (bufferMesh.isVerticesDataPresent(attributeKind)) {
                     const vertexBuffer = this.getVertexBufferFromMesh(attributeKind, bufferMesh);
-                    attribute.byteStride = vertexBuffer ? vertexBuffer.getSize() * 4 : VertexBuffer.DeduceStride(attributeKind) * 4;
+                    attribute.byteStride = vertexBuffer ? vertexBuffer.getSize() * VertexBuffer.GetTypeByteLength(attribute.accessorComponentType) : VertexBuffer.DeduceStride(attributeKind) * 4;
                     if (attribute.byteStride === 12) {
                         attribute.accessorType = AccessorType.VEC3;
                     }
 
-                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
+                    this.createBufferViewKind(attributeKind, attributeComponentKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
                     attribute.bufferViewIndex = this._bufferViews.length - 1;
                     vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                 }
@@ -1258,7 +1338,7 @@ export class _Exporter {
                                     if (attributeKind == VertexBuffer.PositionKind) {
                                         minMax = _GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, convertToRightHandedSystem);
                                     }
-                                    const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, AccessorComponentType.FLOAT, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                    const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, attribute.accessorComponentType, vertexData.length / stride, 0, minMax.min, minMax.max);
                                     this._accessors.push(accessor);
                                     this.setAttributeKind(meshPrimitive, attributeKind);
                                 }
@@ -1398,62 +1478,71 @@ export class _Exporter {
 
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, ImageMimeType.PNG, true).then(() => {
             return this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then((nodeMap) => {
-                this._nodeMap = nodeMap;
+                return this.createSkinsAsync(babylonScene, nodeMap, binaryWriter).then((skinMap) => {
+                    this._nodeMap = nodeMap;
 
-                this._totalByteLength = binaryWriter.getByteOffset();
-                if (this._totalByteLength == undefined) {
-                    throw new Error("undefined byte length!");
-                }
+                    this._totalByteLength = binaryWriter.getByteOffset();
+                    if (this._totalByteLength == undefined) {
+                        throw new Error("undefined byte length!");
+                    }
 
-                // Build Hierarchy with the node map.
-                for (let babylonNode of nodes) {
-                    glTFNodeIndex = this._nodeMap[babylonNode.uniqueId];
-                    if (glTFNodeIndex !== undefined) {
-                        glTFNode = this._nodes[glTFNodeIndex];
-
-                        if (babylonNode.metadata) {
-                            if (this._options.metadataSelector) {
-                                glTFNode.extras = this._options.metadataSelector(babylonNode.metadata);
-                            } else if (babylonNode.metadata.gltf) {
-                                glTFNode.extras = babylonNode.metadata.gltf.extras;
+                    // Build Hierarchy with the node map.
+                    for (let babylonNode of nodes) {
+                        glTFNodeIndex = this._nodeMap[babylonNode.uniqueId];
+                        if (glTFNodeIndex !== undefined) {
+                            glTFNode = this._nodes[glTFNodeIndex];
+
+                            if (babylonNode.metadata) {
+                                if (this._options.metadataSelector) {
+                                    glTFNode.extras = this._options.metadataSelector(babylonNode.metadata);
+                                } else if (babylonNode.metadata.gltf) {
+                                    glTFNode.extras = babylonNode.metadata.gltf.extras;
+                                }
                             }
-                        }
 
-                        if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
-                            if (this._options.shouldExportNode && !this._options.shouldExportNode(babylonNode)) {
-                                Tools.Log("Omitting " + babylonNode.name + " from scene.");
-                            }
-                            else {
-                                let convertToRightHandedSystem = this._convertToRightHandedSystemMap[babylonNode.uniqueId];
-                                if (convertToRightHandedSystem) {
-                                    if (glTFNode.translation) {
-                                        glTFNode.translation[2] *= -1;
-                                        glTFNode.translation[0] *= -1;
-                                    }
-                                    glTFNode.rotation = glTFNode.rotation ? Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(glTFNode.rotation)).asArray() : (Quaternion.FromArray([0, 1, 0, 0])).asArray();
+                            if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
+                                if (this._options.shouldExportNode && !this._options.shouldExportNode(babylonNode)) {
+                                    Tools.Log("Omitting " + babylonNode.name + " from scene.");
                                 }
+                                else {
+                                    let convertToRightHandedSystem = this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                                    if (convertToRightHandedSystem) {
+                                        if (glTFNode.translation) {
+                                            glTFNode.translation[2] *= -1;
+                                            glTFNode.translation[0] *= -1;
+                                        }
+                                        glTFNode.rotation = glTFNode.rotation ? Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(glTFNode.rotation)).asArray() : (Quaternion.FromArray([0, 1, 0, 0])).asArray();
+                                    }
 
-                                scene.nodes.push(glTFNodeIndex);
+                                    scene.nodes.push(glTFNodeIndex);
+                                }
                             }
-                        }
 
-                        directDescendents = babylonNode.getDescendants(true);
-                        if (!glTFNode.children && directDescendents && directDescendents.length) {
-                            const children: number[] = [];
-                            for (let descendent of directDescendents) {
-                                if (this._nodeMap[descendent.uniqueId] != null) {
-                                    children.push(this._nodeMap[descendent.uniqueId]);
+                            if (babylonNode instanceof Mesh) {
+                                let babylonMesh : Mesh = babylonNode;
+                                if (babylonMesh.skeleton) {
+                                    glTFNode.skin = skinMap[babylonMesh.skeleton.uniqueId];
                                 }
                             }
-                            if (children.length) {
-                                glTFNode.children = children;
+
+                            directDescendents = babylonNode.getDescendants(true);
+                            if (!glTFNode.children && directDescendents && directDescendents.length) {
+                                const children: number[] = [];
+                                for (let descendent of directDescendents) {
+                                    if (this._nodeMap[descendent.uniqueId] != null) {
+                                        children.push(this._nodeMap[descendent.uniqueId]);
+                                    }
+                                }
+                                if (children.length) {
+                                    glTFNode.children = children;
+                                }
                             }
                         }
                     }
-                }
-                if (scene.nodes.length) {
-                    this._scenes.push(scene);
-                }
+                    if (scene.nodes.length) {
+                        this._scenes.push(scene);
+                    }
+                });
             });
         });
     }
@@ -1562,6 +1651,59 @@ export class _Exporter {
             }
         });
     }
+
+    /**
+     * Creates a glTF skin from a Babylon skeleton
+     * @param babylonScene Babylon Scene
+     * @param nodes Babylon transform nodes
+     * @param binaryWriter Buffer to write binary data to
+     * @returns Node mapping of unique id to index
+     */
+    private createSkinsAsync(babylonScene: Scene, nodeMap: { [key: number]: number }, binaryWriter: _BinaryWriter): Promise<{ [key: number]: number }> {
+        let promiseChain = Promise.resolve();
+        const skinMap: { [key: number]: number } = {};
+        for (let skeleton of babylonScene.skeletons) {
+            // create skin
+            const skin: ISkin = { joints: []};
+            let inverseBindMatrices : Matrix[] = [];
+            let skeletonMesh = babylonScene.meshes.find((mesh) => {mesh.skeleton === skeleton; });
+            skin.skeleton = skeleton.overrideMesh === null ? (skeletonMesh ? nodeMap[skeletonMesh.uniqueId] : undefined) : nodeMap[skeleton.overrideMesh.uniqueId];
+            for (let bone of skeleton.bones) {
+                if (bone._index != -1) {
+                    let transformNode = bone.getTransformNode();
+                    if (transformNode) {
+                        let boneMatrix = bone.getInvertedAbsoluteTransform();
+                        if (this._convertToRightHandedSystem) {
+                            _GLTFUtilities._GetRightHandedMatrixFromRef(boneMatrix);
+                        }
+                        inverseBindMatrices.push(boneMatrix);
+                        skin.joints.push(nodeMap[transformNode.uniqueId]);
+                    }
+                }
+            }
+            // create buffer view for inverse bind matrices
+            let byteStride = 64; // 4 x 4 matrix of 32 bit float
+            let byteLength = inverseBindMatrices.length * byteStride;
+            let bufferViewOffset = binaryWriter.getByteOffset();
+            let bufferView = _GLTFUtilities._CreateBufferView(0, bufferViewOffset, byteLength, byteStride, "InverseBindMatrices" + " - " + skeleton.name);
+            this._bufferViews.push(bufferView);
+            let bufferViewIndex =  this._bufferViews.length - 1;
+            let bindMatrixAccessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, "InverseBindMatrices" + " - " + skeleton.name, AccessorType.MAT4, AccessorComponentType.FLOAT, inverseBindMatrices.length, null, null, null);
+            let inverseBindAccessorIndex = this._accessors.push(bindMatrixAccessor) - 1;
+            skin.inverseBindMatrices = inverseBindAccessorIndex;
+            this._skins.push(skin);
+            skinMap[skeleton.uniqueId] = this._skins.length - 1;
+
+            inverseBindMatrices.forEach((mat) => {
+                mat.m.forEach((cell: number) => {
+                    binaryWriter.setFloat32(cell);
+                });
+            });
+        }
+        return promiseChain.then(() => {
+            return skinMap;
+        });
+    }
 }
 
 /**
@@ -1642,7 +1784,31 @@ export class _BinaryWriter {
             if (this._byteOffset + 1 > this._arrayBuffer.byteLength) {
                 this.resizeBuffer(this._arrayBuffer.byteLength * 2);
             }
-            this._dataView.setUint8(this._byteOffset++, entry);
+            this._dataView.setUint8(this._byteOffset, entry);
+            this._byteOffset += 1;
+        }
+    }
+
+    /**
+     * Stores an UInt16 in the array buffer
+     * @param entry
+     * @param byteOffset If defined, specifies where to set the value as an offset.
+     */
+    public setUInt16(entry: number, byteOffset?: number) {
+        if (byteOffset != null) {
+            if (byteOffset < this._byteOffset) {
+                this._dataView.setUint16(byteOffset, entry, true);
+            }
+            else {
+                Tools.Error('BinaryWriter: byteoffset is greater than the current binary buffer length!');
+            }
+        }
+        else {
+            if (this._byteOffset + 2 > this._arrayBuffer.byteLength) {
+                this.resizeBuffer(this._arrayBuffer.byteLength * 2);
+            }
+            this._dataView.setUint16(this._byteOffset, entry, true);
+            this._byteOffset += 2;
         }
     }
 

+ 0 - 0
serializers/src/glTF/2.0/glTFUtilities.ts


部分文件因文件數量過多而無法顯示