Procházet zdrojové kódy

Merge pull request #8463 from BabylonJS/master

Deploy Nightly
sebavan před 5 roky
rodič
revize
70b3a5bf70

+ 48 - 48
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_perfCounter__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _controls_container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./controls/container */ "./2D/controls/container.ts");
 /* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./style */ "./2D/style.ts");
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__);
 
 
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _multiLinePoint__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../multiLinePoint */ "./2D/multiLinePoint.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
 /* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./button */ "./2D/controls/button.ts");
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* 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___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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _vector3WithInfo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../vector3WithInfo */ "./3D/vector3WithInfo.ts");
 
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -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/perfCounter");
+/* 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___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/perfCounter");
+/* 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___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/perfCounter");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _controls_container3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./controls/container3D */ "./3D/controls/container3D.ts");
 
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _shaders_fluent_vertex__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./shaders/fluent.vertex */ "./3D/materials/shaders/fluent.vertex.ts");
 /* harmony import */ var _shaders_fluent_fragment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./shaders/fluent.fragment */ "./3D/materials/shaders/fluent.fragment.ts");
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentPixelShader';
@@ -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/perfCounter");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentVertexShader';
@@ -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/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -17276,14 +17276,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Misc/perfCounter":
+/***/ "babylonjs/Misc/observable":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__;
 
 /***/ })
 

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 5 - 5
dist/preview release/inspector/babylon.inspector.bundle.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 307 - 259
dist/preview release/inspector/babylon.inspector.bundle.max.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


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

@@ -550,14 +550,14 @@ declare module INSPECTOR {
         rightControlPoint: BABYLON.Vector2 | null;
         id: string;
         selected: boolean;
-        selectKeyframe: (id: string) => void;
+        selectKeyframe: (id: string, multiselect: boolean) => void;
         selectedControlPoint: (type: string, id: string) => void;
         isLeftActive: boolean;
         isRightActive: boolean;
     }
     export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
         constructor(props: IKeyframeSvgPointProps);
-        select(): void;
+        select(e: React.MouseEvent<SVGImageElement>): void;
         render(): JSX.Element;
     }
 }
@@ -567,8 +567,10 @@ declare module INSPECTOR {
         updatePosition: (updatedKeyframe: IKeyframeSvgPoint, id: string) => void;
         scale: number;
         viewBoxScale: number;
-        selectKeyframe: (id: string) => void;
+        selectKeyframe: (id: string, multiselect: boolean) => void;
         selectedControlPoint: (type: string, id: string) => void;
+        deselectKeyframes: () => void;
+        removeSelectedKeyframes: (points: IKeyframeSvgPoint[]) => void;
     }
     export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
         private _active;
@@ -577,7 +579,6 @@ declare module INSPECTOR {
         private _draggableArea;
         private _panStart;
         private _panStop;
-        private _width;
         constructor(props: ISvgDraggableAreaProps);
         componentDidMount(): void;
         dragStart(e: React.TouchEvent<SVGSVGElement>): void;
@@ -593,6 +594,7 @@ declare module INSPECTOR {
         keyDown(e: KeyboardEvent): void;
         keyUp(e: KeyboardEvent): void;
         focus(e: React.MouseEvent<SVGSVGElement>): void;
+        isControlPointActive(): boolean;
         render(): JSX.Element;
     }
 }
@@ -698,26 +700,6 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface IPlayheadProps {
-        frame: number;
-        offset: number;
-        onCurrentFrameChange: (frame: number) => void;
-    }
-    export class Playhead extends React.Component<IPlayheadProps> {
-        private _direction;
-        private _active;
-        constructor(props: IPlayheadProps);
-        dragStart(e: React.TouchEvent<HTMLDivElement>): void;
-        dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        drag(e: React.TouchEvent<HTMLDivElement>): void;
-        drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
-        dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        calculateMove(): string;
-        render(): JSX.Element;
-    }
-}
-declare module INSPECTOR {
-    interface IPlayheadProps {
         message: string;
         open: boolean;
         close: () => void;
@@ -756,20 +738,34 @@ declare module INSPECTOR {
         entity: BABYLON.IAnimatable;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
         setNotificationMessage: (message: string) => void;
-        changed: () => void;
+        finishedUpdate: () => void;
+        addedNewAnimation: () => void;
         fps: number;
+        selectedToUpdate?: BABYLON.Animation | undefined;
     }
     export class AddAnimation extends React.Component<IAddAnimationProps, {
         animationName: string;
         animationTargetProperty: string;
-        animationType: string;
+        animationType: number;
         loopMode: number;
         animationTargetPath: string;
+        isUpdating: boolean;
     }> {
         constructor(props: IAddAnimationProps);
-        getAnimationTypeofChange(selected: string): number;
+        setInitialState(editingAnimation?: BABYLON.Animation): {
+            animationName: string;
+            animationTargetPath: string;
+            animationType: number;
+            loopMode: number;
+            animationTargetProperty: string;
+            isUpdating: boolean;
+        };
+        componentWillReceiveProps(nextProps: IAddAnimationProps): void;
+        updateAnimation(): void;
+        getTypeAsString(type: number): "Float" | "Quaternion" | "Vector3" | "Vector2" | "Size" | "Color3" | "Color4";
         addAnimation(): void;
         raiseOnPropertyChanged(newValue: BABYLON.Animation[], previousValue: BABYLON.Animation[]): void;
+        raiseOnPropertyUpdated(newValue: string | number | undefined, previousValue: string | number, property: string): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handlePathChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>): void;
@@ -786,6 +782,8 @@ declare module INSPECTOR {
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
         selectAnimation: (selected: BABYLON.Animation, coordinate?: SelectedCoordinate) => void;
         empty: () => void;
+        editAnimation: (selected: BABYLON.Animation) => void;
+        deselectAnimation: () => void;
     }
     interface Item {
         index: number;
@@ -814,7 +812,6 @@ declare module INSPECTOR {
         constructor(props: IAnimationListTreeProps);
         deleteAnimation(): void;
         generateList(): Item[] | null;
-        editAnimation(): void;
         toggleProperty(index: number): void;
         setSelectedCoordinate(animation: BABYLON.Animation, coordinate: SelectedCoordinate, index: number): void;
         setListItem(animation: BABYLON.Animation, i: number): JSX.Element | null;
@@ -901,6 +898,7 @@ declare module INSPECTOR {
         setFps: (fps: number) => void;
         globalState: GlobalState;
         snippetServer: string;
+        deselectAnimation: () => void;
     }
     export class EditorControls extends React.Component<IEditorControlsProps, {
         isAnimationTabOpen: boolean;
@@ -912,15 +910,18 @@ declare module INSPECTOR {
         framesPerSecond: number;
         snippetId: string;
         loopMode: number;
+        selected: BABYLON.Animation | undefined;
     }> {
         constructor(props: IEditorControlsProps);
         animationAdded(): void;
+        finishedUpdate(): void;
         recountAnimations(): number;
         changeLoopBehavior(): void;
         handleTabs(tab: number): void;
         handleChangeFps(fps: number): void;
         emptiedList(): void;
         animationsLoaded(numberOfAnimations: number): void;
+        editAnimation(selected: BABYLON.Animation): void;
         render(): JSX.Element;
     }
 }
@@ -976,6 +977,7 @@ declare module INSPECTOR {
         private _graphCanvas;
         private _svgCanvas;
         private _isTargetedAnimation;
+        private _pixelFrameUnit;
         private _onBeforeRenderObserver;
         private _mainAnimatable;
         constructor(props: IAnimationCurveEditorComponentProps);
@@ -998,8 +1000,9 @@ declare module INSPECTOR {
          * Keyframe Manipulation
          * This section handles events from SvgDraggableArea.
          */
-        selectKeyframe(id: string): void;
+        selectKeyframe(id: string, multiselect: boolean): 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;
         /**
@@ -1014,6 +1017,7 @@ declare module INSPECTOR {
         setLerpMode(): void;
         addKeyframeClick(): void;
         removeKeyframeClick(): void;
+        removeKeyframes(points: IKeyframeSvgPoint[]): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         /**
          * Curve Rendering Functions
@@ -1042,6 +1046,7 @@ declare module INSPECTOR {
         curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
         setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
+        deselectAnimation(): void;
         /**
          * Core functions
          * This section handles main Curve Editor Functions.
@@ -2918,4 +2923,24 @@ declare module INSPECTOR {
         private static _RemoveElementFromDOM;
         static Hide(): void;
     }
+}
+declare module INSPECTOR {
+    interface IPlayheadProps {
+        frame: number;
+        offset: number;
+        onCurrentFrameChange: (frame: number) => void;
+    }
+    export class Playhead extends React.Component<IPlayheadProps> {
+        private _direction;
+        private _active;
+        constructor(props: IPlayheadProps);
+        dragStart(e: React.TouchEvent<HTMLDivElement>): void;
+        dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        drag(e: React.TouchEvent<HTMLDivElement>): void;
+        drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
+        dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        calculateMove(): string;
+        render(): JSX.Element;
+    }
 }

+ 111 - 61
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -582,7 +582,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/floatLineCompone
     }
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/anchorSvgPoint" {
-    import * as React from "react";
+    import * as React from 'react';
     import { Vector2 } from 'babylonjs/Maths/math.vector';
     interface IAnchorSvgPointProps {
         control: Vector2;
@@ -600,7 +600,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
     }
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint" {
-    import * as React from "react";
+    import * as React from 'react';
     import { Vector2 } from 'babylonjs/Maths/math.vector';
     export interface IKeyframeSvgPoint {
         keyframePoint: Vector2;
@@ -623,14 +623,14 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         rightControlPoint: Vector2 | null;
         id: string;
         selected: boolean;
-        selectKeyframe: (id: string) => void;
+        selectKeyframe: (id: string, multiselect: boolean) => void;
         selectedControlPoint: (type: string, id: string) => void;
         isLeftActive: boolean;
         isRightActive: boolean;
     }
     export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
         constructor(props: IKeyframeSvgPointProps);
-        select(): void;
+        select(e: React.MouseEvent<SVGImageElement>): void;
         render(): JSX.Element;
     }
 }
@@ -643,8 +643,10 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         updatePosition: (updatedKeyframe: IKeyframeSvgPoint, id: string) => void;
         scale: number;
         viewBoxScale: number;
-        selectKeyframe: (id: string) => void;
+        selectKeyframe: (id: string, multiselect: boolean) => void;
         selectedControlPoint: (type: string, id: string) => void;
+        deselectKeyframes: () => void;
+        removeSelectedKeyframes: (points: IKeyframeSvgPoint[]) => void;
     }
     export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
         private _active;
@@ -653,7 +655,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         private _draggableArea;
         private _panStart;
         private _panStop;
-        private _width;
         constructor(props: ISvgDraggableAreaProps);
         componentDidMount(): void;
         dragStart(e: React.TouchEvent<SVGSVGElement>): void;
@@ -669,6 +670,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;
         render(): JSX.Element;
     }
 }
@@ -777,27 +779,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         render(): JSX.Element;
     }
 }
-declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/playhead" {
-    import * as React from 'react';
-    interface IPlayheadProps {
-        frame: number;
-        offset: number;
-        onCurrentFrameChange: (frame: number) => void;
-    }
-    export class Playhead extends React.Component<IPlayheadProps> {
-        private _direction;
-        private _active;
-        constructor(props: IPlayheadProps);
-        dragStart(e: React.TouchEvent<HTMLDivElement>): void;
-        dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        drag(e: React.TouchEvent<HTMLDivElement>): void;
-        drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
-        dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        calculateMove(): string;
-        render(): JSX.Element;
-    }
-}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/notification" {
     import * as React from "react";
     interface IPlayheadProps {
@@ -845,20 +826,34 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         entity: IAnimatable;
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
         setNotificationMessage: (message: string) => void;
-        changed: () => void;
+        finishedUpdate: () => void;
+        addedNewAnimation: () => void;
         fps: number;
+        selectedToUpdate?: Animation | undefined;
     }
     export class AddAnimation extends React.Component<IAddAnimationProps, {
         animationName: string;
         animationTargetProperty: string;
-        animationType: string;
+        animationType: number;
         loopMode: number;
         animationTargetPath: string;
+        isUpdating: boolean;
     }> {
         constructor(props: IAddAnimationProps);
-        getAnimationTypeofChange(selected: string): number;
+        setInitialState(editingAnimation?: Animation): {
+            animationName: string;
+            animationTargetPath: string;
+            animationType: number;
+            loopMode: number;
+            animationTargetProperty: string;
+            isUpdating: boolean;
+        };
+        componentWillReceiveProps(nextProps: IAddAnimationProps): void;
+        updateAnimation(): void;
+        getTypeAsString(type: number): "Float" | "Quaternion" | "Vector3" | "Vector2" | "Size" | "Color3" | "Color4";
         addAnimation(): void;
         raiseOnPropertyChanged(newValue: Animation[], previousValue: Animation[]): void;
+        raiseOnPropertyUpdated(newValue: string | number | undefined, previousValue: string | number, property: string): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handlePathChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>): void;
@@ -881,6 +876,8 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
         selectAnimation: (selected: Animation, coordinate?: SelectedCoordinate) => void;
         empty: () => void;
+        editAnimation: (selected: Animation) => void;
+        deselectAnimation: () => void;
     }
     interface Item {
         index: number;
@@ -909,7 +906,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         constructor(props: IAnimationListTreeProps);
         deleteAnimation(): void;
         generateList(): Item[] | null;
-        editAnimation(): void;
         toggleProperty(index: number): void;
         setSelectedCoordinate(animation: Animation, coordinate: SelectedCoordinate, index: number): void;
         setListItem(animation: Animation, i: number): JSX.Element | null;
@@ -1021,6 +1017,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         setFps: (fps: number) => void;
         globalState: GlobalState;
         snippetServer: string;
+        deselectAnimation: () => void;
     }
     export class EditorControls extends React.Component<IEditorControlsProps, {
         isAnimationTabOpen: boolean;
@@ -1032,15 +1029,18 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         framesPerSecond: number;
         snippetId: string;
         loopMode: number;
+        selected: Animation | undefined;
     }> {
         constructor(props: IEditorControlsProps);
         animationAdded(): void;
+        finishedUpdate(): void;
         recountAnimations(): number;
         changeLoopBehavior(): void;
         handleTabs(tab: number): void;
         handleChangeFps(fps: number): void;
         emptiedList(): void;
         animationsLoaded(numberOfAnimations: number): void;
+        editAnimation(selected: Animation): void;
         render(): JSX.Element;
     }
 }
@@ -1110,6 +1110,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         private _graphCanvas;
         private _svgCanvas;
         private _isTargetedAnimation;
+        private _pixelFrameUnit;
         private _onBeforeRenderObserver;
         private _mainAnimatable;
         constructor(props: IAnimationCurveEditorComponentProps);
@@ -1132,8 +1133,9 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
          * Keyframe Manipulation
          * This section handles events from SvgDraggableArea.
          */
-        selectKeyframe(id: string): void;
+        selectKeyframe(id: string, multiselect: boolean): 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;
         /**
@@ -1148,6 +1150,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         setLerpMode(): void;
         addKeyframeClick(): void;
         removeKeyframeClick(): void;
+        removeKeyframes(points: IKeyframeSvgPoint[]): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         /**
          * Curve Rendering Functions
@@ -1176,6 +1179,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         curvePath(keyframes: IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction): string;
         setKeyframePoint(controlPoints: Vector2[], index: number, keyframesCount: number): void;
         interpolateControlPoints(p0: Vector2, p1: Vector2, u: number, p2: Vector2, v: number, p3: Vector2): Vector2[] | undefined;
+        deselectAnimation(): void;
         /**
          * Core functions
          * This section handles main Curve Editor Functions.
@@ -3594,6 +3598,27 @@ declare module "babylonjs-inspector/inspector" {
 declare module "babylonjs-inspector/index" {
     export * from "babylonjs-inspector/inspector";
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/playhead" {
+    import * as React from 'react';
+    interface IPlayheadProps {
+        frame: number;
+        offset: number;
+        onCurrentFrameChange: (frame: number) => void;
+    }
+    export class Playhead extends React.Component<IPlayheadProps> {
+        private _direction;
+        private _active;
+        constructor(props: IPlayheadProps);
+        dragStart(e: React.TouchEvent<HTMLDivElement>): void;
+        dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        drag(e: React.TouchEvent<HTMLDivElement>): void;
+        drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
+        dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        calculateMove(): string;
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/legacy/legacy" {
     export * from "babylonjs-inspector/index";
 }
@@ -4152,14 +4177,14 @@ declare module INSPECTOR {
         rightControlPoint: BABYLON.Vector2 | null;
         id: string;
         selected: boolean;
-        selectKeyframe: (id: string) => void;
+        selectKeyframe: (id: string, multiselect: boolean) => void;
         selectedControlPoint: (type: string, id: string) => void;
         isLeftActive: boolean;
         isRightActive: boolean;
     }
     export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
         constructor(props: IKeyframeSvgPointProps);
-        select(): void;
+        select(e: React.MouseEvent<SVGImageElement>): void;
         render(): JSX.Element;
     }
 }
@@ -4169,8 +4194,10 @@ declare module INSPECTOR {
         updatePosition: (updatedKeyframe: IKeyframeSvgPoint, id: string) => void;
         scale: number;
         viewBoxScale: number;
-        selectKeyframe: (id: string) => void;
+        selectKeyframe: (id: string, multiselect: boolean) => void;
         selectedControlPoint: (type: string, id: string) => void;
+        deselectKeyframes: () => void;
+        removeSelectedKeyframes: (points: IKeyframeSvgPoint[]) => void;
     }
     export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
         private _active;
@@ -4179,7 +4206,6 @@ declare module INSPECTOR {
         private _draggableArea;
         private _panStart;
         private _panStop;
-        private _width;
         constructor(props: ISvgDraggableAreaProps);
         componentDidMount(): void;
         dragStart(e: React.TouchEvent<SVGSVGElement>): void;
@@ -4195,6 +4221,7 @@ declare module INSPECTOR {
         keyDown(e: KeyboardEvent): void;
         keyUp(e: KeyboardEvent): void;
         focus(e: React.MouseEvent<SVGSVGElement>): void;
+        isControlPointActive(): boolean;
         render(): JSX.Element;
     }
 }
@@ -4300,26 +4327,6 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface IPlayheadProps {
-        frame: number;
-        offset: number;
-        onCurrentFrameChange: (frame: number) => void;
-    }
-    export class Playhead extends React.Component<IPlayheadProps> {
-        private _direction;
-        private _active;
-        constructor(props: IPlayheadProps);
-        dragStart(e: React.TouchEvent<HTMLDivElement>): void;
-        dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        drag(e: React.TouchEvent<HTMLDivElement>): void;
-        drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
-        dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
-        calculateMove(): string;
-        render(): JSX.Element;
-    }
-}
-declare module INSPECTOR {
-    interface IPlayheadProps {
         message: string;
         open: boolean;
         close: () => void;
@@ -4358,20 +4365,34 @@ declare module INSPECTOR {
         entity: BABYLON.IAnimatable;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
         setNotificationMessage: (message: string) => void;
-        changed: () => void;
+        finishedUpdate: () => void;
+        addedNewAnimation: () => void;
         fps: number;
+        selectedToUpdate?: BABYLON.Animation | undefined;
     }
     export class AddAnimation extends React.Component<IAddAnimationProps, {
         animationName: string;
         animationTargetProperty: string;
-        animationType: string;
+        animationType: number;
         loopMode: number;
         animationTargetPath: string;
+        isUpdating: boolean;
     }> {
         constructor(props: IAddAnimationProps);
-        getAnimationTypeofChange(selected: string): number;
+        setInitialState(editingAnimation?: BABYLON.Animation): {
+            animationName: string;
+            animationTargetPath: string;
+            animationType: number;
+            loopMode: number;
+            animationTargetProperty: string;
+            isUpdating: boolean;
+        };
+        componentWillReceiveProps(nextProps: IAddAnimationProps): void;
+        updateAnimation(): void;
+        getTypeAsString(type: number): "Float" | "Quaternion" | "Vector3" | "Vector2" | "Size" | "Color3" | "Color4";
         addAnimation(): void;
         raiseOnPropertyChanged(newValue: BABYLON.Animation[], previousValue: BABYLON.Animation[]): void;
+        raiseOnPropertyUpdated(newValue: string | number | undefined, previousValue: string | number, property: string): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handlePathChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>): void;
@@ -4388,6 +4409,8 @@ declare module INSPECTOR {
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
         selectAnimation: (selected: BABYLON.Animation, coordinate?: SelectedCoordinate) => void;
         empty: () => void;
+        editAnimation: (selected: BABYLON.Animation) => void;
+        deselectAnimation: () => void;
     }
     interface Item {
         index: number;
@@ -4416,7 +4439,6 @@ declare module INSPECTOR {
         constructor(props: IAnimationListTreeProps);
         deleteAnimation(): void;
         generateList(): Item[] | null;
-        editAnimation(): void;
         toggleProperty(index: number): void;
         setSelectedCoordinate(animation: BABYLON.Animation, coordinate: SelectedCoordinate, index: number): void;
         setListItem(animation: BABYLON.Animation, i: number): JSX.Element | null;
@@ -4503,6 +4525,7 @@ declare module INSPECTOR {
         setFps: (fps: number) => void;
         globalState: GlobalState;
         snippetServer: string;
+        deselectAnimation: () => void;
     }
     export class EditorControls extends React.Component<IEditorControlsProps, {
         isAnimationTabOpen: boolean;
@@ -4514,15 +4537,18 @@ declare module INSPECTOR {
         framesPerSecond: number;
         snippetId: string;
         loopMode: number;
+        selected: BABYLON.Animation | undefined;
     }> {
         constructor(props: IEditorControlsProps);
         animationAdded(): void;
+        finishedUpdate(): void;
         recountAnimations(): number;
         changeLoopBehavior(): void;
         handleTabs(tab: number): void;
         handleChangeFps(fps: number): void;
         emptiedList(): void;
         animationsLoaded(numberOfAnimations: number): void;
+        editAnimation(selected: BABYLON.Animation): void;
         render(): JSX.Element;
     }
 }
@@ -4578,6 +4604,7 @@ declare module INSPECTOR {
         private _graphCanvas;
         private _svgCanvas;
         private _isTargetedAnimation;
+        private _pixelFrameUnit;
         private _onBeforeRenderObserver;
         private _mainAnimatable;
         constructor(props: IAnimationCurveEditorComponentProps);
@@ -4600,8 +4627,9 @@ declare module INSPECTOR {
          * Keyframe Manipulation
          * This section handles events from SvgDraggableArea.
          */
-        selectKeyframe(id: string): void;
+        selectKeyframe(id: string, multiselect: boolean): 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;
         /**
@@ -4616,6 +4644,7 @@ declare module INSPECTOR {
         setLerpMode(): void;
         addKeyframeClick(): void;
         removeKeyframeClick(): void;
+        removeKeyframes(points: IKeyframeSvgPoint[]): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         /**
          * Curve Rendering Functions
@@ -4644,6 +4673,7 @@ declare module INSPECTOR {
         curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
         setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
+        deselectAnimation(): void;
         /**
          * Core functions
          * This section handles main Curve Editor Functions.
@@ -6520,4 +6550,24 @@ declare module INSPECTOR {
         private static _RemoveElementFromDOM;
         static Hide(): void;
     }
+}
+declare module INSPECTOR {
+    interface IPlayheadProps {
+        frame: number;
+        offset: number;
+        onCurrentFrameChange: (frame: number) => void;
+    }
+    export class Playhead extends React.Component<IPlayheadProps> {
+        private _direction;
+        private _active;
+        constructor(props: IPlayheadProps);
+        dragStart(e: React.TouchEvent<HTMLDivElement>): void;
+        dragStart(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        drag(e: React.TouchEvent<HTMLDivElement>): void;
+        drag(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        dragEnd(e: React.TouchEvent<HTMLDivElement>): void;
+        dragEnd(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
+        calculateMove(): string;
+        render(): JSX.Element;
+    }
 }

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

@@ -9,7 +9,7 @@
 - Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added SubSurfaceScattering on PBR materials ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added editing of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
-- Added Curve editor to manage selected entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
+- Added Curve editor to manage entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
 - Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 - Added support for **thin instances** for faster mesh instances. [Doc](https://doc.babylonjs.com/how_to/how_to_use_thininstances) ([Popov72](https://github.com/Popov72))
 

+ 144 - 95
inspector/src/components/actionTabs/tabs/propertyGrids/animations/addAnimation.tsx

@@ -7,6 +7,7 @@ import { Vector2, Vector3, Quaternion } from 'babylonjs/Maths/math.vector';
 import { Size } from 'babylonjs/Maths/math.size';
 import { Color3, Color4 } from 'babylonjs/Maths/math.color';
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
+import { IAnimationKey } from 'babylonjs/Animations/animationKey';
 
 interface IAddAnimationProps {
   isOpen: boolean;
@@ -14,8 +15,10 @@ interface IAddAnimationProps {
   entity: IAnimatable;
   onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
   setNotificationMessage: (message: string) => void;
-  changed: () => void;
+  finishedUpdate: () => void;
+  addedNewAnimation: () => void;
   fps: number;
+  selectedToUpdate?: Animation | undefined;
 }
 
 export class AddAnimation extends React.Component<
@@ -23,49 +26,95 @@ export class AddAnimation extends React.Component<
   {
     animationName: string;
     animationTargetProperty: string;
-    animationType: string;
+    animationType: number;
     loopMode: number;
     animationTargetPath: string;
+    isUpdating: boolean;
   }
 > {
   constructor(props: IAddAnimationProps) {
     super(props);
-    this.state = {
-      animationName: '',
+    this.state = this.setInitialState(this.props.selectedToUpdate);
+  }
+
+  setInitialState(editingAnimation?: Animation) {
+    return {
+      animationName: editingAnimation ? editingAnimation.name : '',
       animationTargetPath: '',
-      animationType: 'Float',
-      loopMode: Animation.ANIMATIONLOOPMODE_CYCLE,
-      animationTargetProperty: '',
+      animationType: editingAnimation
+        ? editingAnimation.dataType
+        : Animation.ANIMATIONTYPE_FLOAT,
+      loopMode: editingAnimation
+        ? editingAnimation.loopMode ?? Animation.ANIMATIONLOOPMODE_CYCLE
+        : Animation.ANIMATIONLOOPMODE_CYCLE,
+      animationTargetProperty: editingAnimation
+        ? editingAnimation.targetProperty
+        : '',
+      isUpdating: editingAnimation ? true : false,
     };
   }
 
-  getAnimationTypeofChange(selected: string) {
-    let dataType = 0;
-    switch (selected) {
-      case 'Float':
-        dataType = Animation.ANIMATIONTYPE_FLOAT;
-        break;
-      case 'Quaternion':
-        dataType = Animation.ANIMATIONTYPE_QUATERNION;
-        break;
-      case 'Vector3':
-        dataType = Animation.ANIMATIONTYPE_VECTOR3;
-        break;
-      case 'Vector2':
-        dataType = Animation.ANIMATIONTYPE_VECTOR2;
-        break;
-      case 'Size':
-        dataType = Animation.ANIMATIONTYPE_SIZE;
-        break;
-      case 'Color3':
-        dataType = Animation.ANIMATIONTYPE_COLOR3;
-        break;
-      case 'Color4':
-        dataType = Animation.ANIMATIONTYPE_COLOR4;
-        break;
+  componentWillReceiveProps(nextProps: IAddAnimationProps) {
+    if (
+      nextProps.selectedToUpdate !== undefined &&
+      nextProps.selectedToUpdate !== this.props.selectedToUpdate
+    ) {
+      this.setState(this.setInitialState(nextProps.selectedToUpdate));
+    } else {
+      if (nextProps.isOpen === true && nextProps.isOpen !== this.props.isOpen)
+        this.setState(this.setInitialState());
+    }
+  }
+
+  updateAnimation() {
+    if (this.props.selectedToUpdate !== undefined) {
+      const oldNameValue = this.props.selectedToUpdate.name;
+      this.props.selectedToUpdate.name = this.state.animationName;
+      this.raiseOnPropertyUpdated(
+        oldNameValue,
+        this.state.animationName,
+        'name'
+      );
+
+      const oldLoopModeValue = this.props.selectedToUpdate.loopMode;
+      this.props.selectedToUpdate.loopMode = this.state.loopMode;
+      this.raiseOnPropertyUpdated(
+        oldLoopModeValue,
+        this.state.loopMode,
+        'loopMode'
+      );
+
+      const oldTargetPropertyValue = this.props.selectedToUpdate.targetProperty;
+      this.props.selectedToUpdate.targetProperty = this.state.animationTargetProperty;
+      this.raiseOnPropertyUpdated(
+        oldTargetPropertyValue,
+        this.state.animationTargetProperty,
+        'targetProperty'
+      );
+
+      this.props.finishedUpdate();
     }
+  }
 
-    return dataType;
+  getTypeAsString(type: number) {
+    switch (type) {
+      case Animation.ANIMATIONTYPE_FLOAT:
+        return 'Float';
+      case Animation.ANIMATIONTYPE_QUATERNION:
+        return 'Quaternion';
+      case Animation.ANIMATIONTYPE_VECTOR3:
+        return 'Vector3';
+      case Animation.ANIMATIONTYPE_VECTOR2:
+        return 'Vector2';
+      case Animation.ANIMATIONTYPE_SIZE:
+        return 'Size';
+      case Animation.ANIMATIONTYPE_COLOR3:
+        return 'Color3';
+      case Animation.ANIMATIONTYPE_COLOR4:
+        return 'Color4';
+      default:
+        return 'Float';
+    }
   }
 
   addAnimation() {
@@ -76,9 +125,8 @@ export class AddAnimation extends React.Component<
       let matchTypeTargetProperty = this.state.animationTargetProperty.split(
         '.'
       );
-      let animationDataType = this.getAnimationTypeofChange(
-        this.state.animationType
-      );
+      let animationDataType = this.state.animationType;
+
       let matched = false;
 
       if (matchTypeTargetProperty.length === 1) {
@@ -116,9 +164,6 @@ export class AddAnimation extends React.Component<
                 ? (matched = true)
                 : (matched = false);
               break;
-            default:
-              console.log('not recognized');
-              break;
           }
         } else {
           this.props.setNotificationMessage(
@@ -139,55 +184,37 @@ export class AddAnimation extends React.Component<
 
       if (matched) {
         let startValue;
-        let endValue;
         let outTangent;
-        let inTangent;
+
         // Default start and end values for new animations
         switch (animationDataType) {
           case Animation.ANIMATIONTYPE_FLOAT:
             startValue = 1;
-            endValue = 1;
             outTangent = 0;
-            inTangent = 0;
             break;
           case Animation.ANIMATIONTYPE_VECTOR2:
             startValue = new Vector2(1, 1);
-            endValue = new Vector2(1, 1);
             outTangent = Vector2.Zero();
-            inTangent = Vector2.Zero();
             break;
           case Animation.ANIMATIONTYPE_VECTOR3:
             startValue = new Vector3(1, 1, 1);
-            endValue = new Vector3(1, 1, 1);
             outTangent = Vector3.Zero();
-            inTangent = Vector3.Zero();
             break;
           case Animation.ANIMATIONTYPE_QUATERNION:
             startValue = new Quaternion(1, 1, 1, 1);
-            endValue = new Quaternion(1, 1, 1, 1);
             outTangent = Quaternion.Zero();
-            inTangent = Quaternion.Zero();
             break;
           case Animation.ANIMATIONTYPE_COLOR3:
             startValue = new Color3(1, 1, 1);
-            endValue = new Color3(1, 1, 1);
             outTangent = new Color3(0, 0, 0);
-            inTangent = new Color3(0, 0, 0);
             break;
           case Animation.ANIMATIONTYPE_COLOR4:
             startValue = new Color4(1, 1, 1, 1);
-            endValue = new Color4(1, 1, 1, 1);
             outTangent = new Color4(0, 0, 0, 0);
-            inTangent = new Color4(0, 0, 0, 0);
             break;
           case Animation.ANIMATIONTYPE_SIZE:
             startValue = new Size(1, 1);
-            endValue = new Size(1, 1);
             outTangent = Size.Zero();
-            inTangent = Size.Zero();
-            break;
-          default:
-            console.log('not recognized');
             break;
         }
 
@@ -220,19 +247,13 @@ export class AddAnimation extends React.Component<
           );
 
           // Start with two keyframes
-          var keys = [];
+          var keys: IAnimationKey[] = [];
           keys.push({
             frame: 0,
             value: startValue,
             outTangent: outTangent,
           });
 
-          keys.push({
-            inTangent: inTangent,
-            frame: 100,
-            value: endValue,
-          });
-
           animation.setKeys(keys);
 
           if (this.props.entity.animations) {
@@ -243,13 +264,12 @@ export class AddAnimation extends React.Component<
             ];
             this.raiseOnPropertyChanged(updatedCollection, store);
             this.props.entity.animations = updatedCollection;
-            this.props.changed();
-            this.props.close();
+            this.props.addedNewAnimation();
             //Cleaning form fields
             this.setState({
               animationName: '',
               animationTargetPath: '',
-              animationType: 'Float',
+              animationType: Animation.ANIMATIONTYPE_FLOAT,
               loopMode: Animation.ANIMATIONLOOPMODE_CYCLE,
               animationTargetProperty: '',
             });
@@ -257,7 +277,9 @@ export class AddAnimation extends React.Component<
         }
       } else {
         this.props.setNotificationMessage(
-          `The property "${this.state.animationTargetProperty}" is not a "${this.state.animationType}" type`
+          `The property "${
+            this.state.animationTargetProperty
+          }" is not a "${this.getTypeAsString(this.state.animationType)}" type`
         );
       }
     } else {
@@ -280,6 +302,23 @@ export class AddAnimation extends React.Component<
     });
   }
 
+  raiseOnPropertyUpdated(
+    newValue: string | number | undefined,
+    previousValue: string | number,
+    property: string
+  ) {
+    if (!this.props.onPropertyChangedObservable) {
+      return;
+    }
+
+    this.props.onPropertyChangedObservable.notifyObservers({
+      object: this.props.selectedToUpdate,
+      property: property,
+      value: newValue,
+      initialValue: previousValue,
+    });
+  }
+
   handleNameChange(event: React.ChangeEvent<HTMLInputElement>) {
     event.preventDefault();
     this.setState({ animationName: event.target.value.trim() });
@@ -292,7 +331,7 @@ export class AddAnimation extends React.Component<
 
   handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>) {
     event.preventDefault();
-    this.setState({ animationType: event.target.value });
+    this.setState({ animationType: parseInt(event.target.value) });
   }
 
   handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>) {
@@ -312,15 +351,17 @@ export class AddAnimation extends React.Component<
         style={{ display: this.props.isOpen ? 'block' : 'none' }}
       >
         <div className='sub-content'>
-          <div className='label-input'>
-            <label>Target Path</label>
-            <input
-              type='text'
-              value={this.state.animationTargetPath}
-              onChange={(e) => this.handlePathChange(e)}
-              disabled
-            ></input>
-          </div>
+          {this.state.isUpdating ? null : (
+            <div className='label-input'>
+              <label>Target Path</label>
+              <input
+                type='text'
+                value={this.state.animationTargetPath}
+                onChange={(e) => this.handlePathChange(e)}
+                disabled
+              ></input>
+            </div>
+          )}
           <div className='label-input'>
             <label>Display Name</label>
             <input
@@ -337,21 +378,25 @@ export class AddAnimation extends React.Component<
               onChange={(e) => this.handlePropertyChange(e)}
             ></input>
           </div>
-          <div className='label-input'>
-            <label>Type</label>
-            <select
-              onChange={(e) => this.handleTypeChange(e)}
-              value={this.state.animationType}
-            >
-              <option value='Float'>Float</option>
-              <option value='Vector3'>Vector3</option>
-              <option value='Vector2'>Vector2</option>
-              <option value='Quaternion'>Quaternion</option>
-              <option value='Color3'>Color3</option>
-              <option value='Color4'>Color4</option>
-              <option value='Size'>Size</option>
-            </select>
-          </div>
+          {this.state.isUpdating ? null : (
+            <div className='label-input'>
+              <label>Type</label>
+              <select
+                onChange={(e) => this.handleTypeChange(e)}
+                value={this.state.animationType}
+              >
+                <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>
+          )}
           <div className='label-input'>
             <label>Loop Mode</label>
             <select
@@ -369,8 +414,12 @@ export class AddAnimation extends React.Component<
           </div>
           <div className='confirm-buttons'>
             <ButtonLineComponent
-              label={'Create'}
-              onClick={() => this.addAnimation()}
+              label={this.state.isUpdating ? 'Update' : 'Create'}
+              onClick={
+                this.state.isUpdating
+                  ? () => this.updateAnimation()
+                  : () => this.addAnimation()
+              }
             />
           </div>
         </div>

+ 49 - 28
inspector/src/components/actionTabs/tabs/propertyGrids/animations/anchorSvgPoint.tsx

@@ -1,36 +1,57 @@
-
-import * as React from "react";
+import * as React from 'react';
 import { Vector2 } from 'babylonjs/Maths/math.vector';
 
 interface IAnchorSvgPointProps {
-    control: Vector2;
-    anchor: Vector2;
-    active: boolean;
-    type: string;
-    index: string;
-    selected: boolean;
-    selectControlPoint: (id: string) => void;
+  control: Vector2;
+  anchor: Vector2;
+  active: boolean;
+  type: string;
+  index: string;
+  selected: boolean;
+  selectControlPoint: (id: string) => void;
 }
 
+export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps> {
+  constructor(props: IAnchorSvgPointProps) {
+    super(props);
+  }
 
-export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps>{
-    constructor(props: IAnchorSvgPointProps) {
-        super(props);
-    }
-
-    select() {
-        this.props.selectControlPoint(this.props.type);
-    }
+  select() {
+    this.props.selectControlPoint(this.props.type);
+  }
 
-    render() {
-        return (
-            <>
-                <svg x={this.props.control.x} y={this.props.control.y} style={{ overflow: 'visible' }} onClick={() => this.select()}>
-                    <circle type={this.props.type} data-id={this.props.index} className={`draggable control-point ${this.props.active ? 'active' : ''}`} cx="0" cy="0" r="1" stroke="white" strokeWidth={this.props.selected ? 0 : 0} fill={this.props.active ? "#e9db1e" : "white"} />
-                </svg>
-                <line className={`control-point ${this.props.active ? 'active' : ''}`} x1={this.props.anchor.x} y1={this.props.anchor.y} x2={this.props.control.x} y2={this.props.control.y} strokeWidth="1" />
-            </>
-        )
-    }
+  render() {
+    return (
+      <>
+        <svg
+          x={this.props.control.x}
+          y={this.props.control.y}
+          style={{ overflow: 'visible' }}
+          onClick={() => this.select()}
+        >
+          <circle
+            type={this.props.type}
+            data-id={this.props.index}
+            className={`draggable control-point ${
+              this.props.active ? 'active' : ''
+            }`}
+            cx='0'
+            cy='0'
+            r='0.7%'
+            stroke='white'
+            strokeWidth={this.props.selected ? 0 : 0}
+            fill={this.props.active ? '#e9db1e' : 'white'}
+          />
+        </svg>
+        <line
+          className={`control-point ${this.props.active ? 'active' : ''}`}
+          x1={this.props.anchor.x}
+          y1={this.props.anchor.y}
+          x2={this.props.control.x}
+          y2={this.props.control.y}
+          strokeWidth='0.8%'
+        />
+      </>
+    );
+  }
 }
-

+ 118 - 40
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -8,7 +8,6 @@ import { IAnimationKey } from 'babylonjs/Animations/animationKey';
 import { IKeyframeSvgPoint } from './keyframeSvgPoint';
 import { SvgDraggableArea } from './svgDraggableArea';
 import { Timeline } from './timeline';
-import { Playhead } from './playhead';
 import { Notification } from './notification';
 import { GraphActionsBar } from './graphActionsBar';
 import { Scene } from 'babylonjs/scene';
@@ -85,6 +84,8 @@ export class AnimationCurveEditorComponent extends React.Component<
   private _svgCanvas: React.RefObject<SvgDraggableArea>;
   private _isTargetedAnimation: boolean;
 
+  private _pixelFrameUnit: number;
+
   private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
   private _mainAnimatable: Nullable<Animatable>;
   constructor(props: IAnimationCurveEditorComponentProps) {
@@ -95,7 +96,7 @@ export class AnimationCurveEditorComponent extends React.Component<
     //this._selectedCurve = React.createRef();
     this._svgCanvas = React.createRef();
 
-    console.log(this.props.entity instanceof TargetedAnimation);
+    this._pixelFrameUnit = 10;
 
     let initialSelection;
     let initialPathData;
@@ -165,7 +166,7 @@ export class AnimationCurveEditorComponent extends React.Component<
       selectedPathData: initialPathData,
       selectedCoordinate: 0,
       animationLimit: 120,
-      fps: 0,
+      fps: 60,
     };
   }
 
@@ -256,10 +257,16 @@ export class AnimationCurveEditorComponent extends React.Component<
    * Keyframe Manipulation
    * This section handles events from SvgDraggableArea.
    */
-  selectKeyframe(id: string) {
+  selectKeyframe(id: string, multiselect: boolean) {
+    let selectedKeyFrame = this.state.svgKeyframes?.find((kf) => kf.id === id)
+      ?.selected;
+    if (!multiselect) {
+      this.deselectKeyframes();
+    }
+
     let updatedKeyframes = this.state.svgKeyframes?.map((kf) => {
       if (kf.id === id) {
-        kf.selected = !kf.selected;
+        kf.selected = !selectedKeyFrame;
       }
       return kf;
     });
@@ -284,6 +291,16 @@ export class AnimationCurveEditorComponent extends React.Component<
     this.setState({ svgKeyframes: updatedKeyframes });
   }
 
+  deselectKeyframes() {
+    let updatedKeyframes = this.state.svgKeyframes?.map((kf) => {
+      kf.isLeftActive = false;
+      kf.isRightActive = false;
+      kf.selected = false;
+      return kf;
+    });
+    this.setState({ svgKeyframes: updatedKeyframes });
+  }
+
   updateValuePerCoordinate(
     dataType: number,
     value: number | Vector2 | Vector3 | Color3 | Color4 | Size | Quaternion,
@@ -398,7 +415,9 @@ export class AnimationCurveEditorComponent extends React.Component<
       ) {
         newFrame = 1;
       } else {
-        newFrame = Math.round(updatedSvgKeyFrame.keyframePoint.x);
+        newFrame = Math.round(
+          updatedSvgKeyFrame.keyframePoint.x / this._pixelFrameUnit
+        );
       }
     }
 
@@ -589,6 +608,31 @@ export class AnimationCurveEditorComponent extends React.Component<
     }
   }
 
+  removeKeyframes(points: IKeyframeSvgPoint[]) {
+    if (this.state.selected !== null) {
+      let currentAnimation = this.state.selected;
+
+      const indexesToRemove = points.map((p) => {
+        return {
+          index: parseInt(p.id.split('_')[3]),
+          coordinate: parseInt(p.id.split('_')[2]),
+        };
+      });
+
+      if (currentAnimation.dataType === Animation.ANIMATIONTYPE_FLOAT) {
+        let keys = currentAnimation.getKeys();
+
+        let filteredKeys = keys.filter((_, i) => {
+          return !indexesToRemove.find((x) => x.index === i);
+        });
+        currentAnimation.setKeys(filteredKeys);
+        this.deselectKeyframes();
+
+        this.selectAnimation(currentAnimation);
+      }
+    }
+  }
+
   addKeyFrame(event: React.MouseEvent<SVGSVGElement>) {
     event.preventDefault();
 
@@ -778,9 +822,9 @@ export class AnimationCurveEditorComponent extends React.Component<
         const curveColor =
           valueType === Animation.ANIMATIONTYPE_FLOAT ? colors[4] : colors[d];
         // START OF LINE/CURVE
-        let data: string | undefined = `M${startKey.frame}, ${
-          this._heightScale - startValue[d] * middle
-        }`; //
+        let data: string | undefined = `M${
+          startKey.frame * this._pixelFrameUnit
+        }, ${this._heightScale - startValue[d] * middle}`; //
 
         if (this.state && this.state.lerpMode) {
           data = this.linearInterpolation(keyframes, data, middle);
@@ -912,13 +956,18 @@ export class AnimationCurveEditorComponent extends React.Component<
       let inTangent;
       let defaultWeight = 5;
 
+      let defaultTangent: number | null = null;
+      if (i !== 0 || i !== keyframes.length - 1) {
+        defaultTangent = 0;
+      }
+
       var inT =
         key.inTangent === undefined
-          ? null
+          ? defaultTangent
           : this.getValueAsArray(type, key.inTangent)[coordinate];
       var outT =
         key.outTangent === undefined
-          ? null
+          ? defaultTangent
           : this.getValueAsArray(type, key.outTangent)[coordinate];
 
       let y = this._heightScale - keyframe_valueAsArray * middle;
@@ -937,14 +986,20 @@ export class AnimationCurveEditorComponent extends React.Component<
 
       if (inT !== null) {
         let valueIn = y * inT + y;
-        inTangent = new Vector2(key.frame - defaultWeight, valueIn);
+        inTangent = new Vector2(
+          key.frame * this._pixelFrameUnit - defaultWeight,
+          valueIn
+        );
       } else {
         inTangent = null;
       }
 
       if (outT !== null) {
         let valueOut = y * outT + y;
-        outTangent = new Vector2(key.frame + defaultWeight, valueOut);
+        outTangent = new Vector2(
+          key.frame * this._pixelFrameUnit + defaultWeight,
+          valueOut
+        );
       } else {
         outTangent = null;
       }
@@ -952,7 +1007,7 @@ export class AnimationCurveEditorComponent extends React.Component<
       if (i === 0) {
         svgKeyframe = {
           keyframePoint: new Vector2(
-            key.frame,
+            key.frame * this._pixelFrameUnit,
             this._heightScale - keyframe_valueAsArray * middle
           ),
           rightControlPoint: outTangent,
@@ -968,7 +1023,7 @@ export class AnimationCurveEditorComponent extends React.Component<
       } else {
         svgKeyframe = {
           keyframePoint: new Vector2(
-            key.frame,
+            key.frame * this._pixelFrameUnit,
             this._heightScale - keyframe_valueAsArray * middle
           ),
           rightControlPoint: outTangent,
@@ -1164,6 +1219,15 @@ export class AnimationCurveEditorComponent extends React.Component<
     return [controlA, controlB, controlC, controlD];
   }
 
+  deselectAnimation() {
+    this.setState({
+      selected: null,
+      svgKeyframes: [],
+      selectedPathData: [],
+      selectedCoordinate: 0,
+    });
+  }
+
   /**
    * Core functions
    * This section handles main Curve Editor Functions.
@@ -1364,7 +1428,7 @@ export class AnimationCurveEditorComponent extends React.Component<
         (e.clientX - CTM.e) / CTM.a,
         (e.clientY - CTM.f) / CTM.d
       );
-      let selectedFrame = Math.round(position.x);
+      let selectedFrame = Math.round(position.x / this._pixelFrameUnit);
       this.setState({ currentFrame: selectedFrame });
     }
   }
@@ -1427,6 +1491,7 @@ export class AnimationCurveEditorComponent extends React.Component<
         <div className='content'>
           <div className='row'>
             <EditorControls
+              deselectAnimation={() => this.deselectAnimation()}
               selectAnimation={(
                 animation: Animation,
                 axis?: SelectedCoordinate
@@ -1453,13 +1518,19 @@ export class AnimationCurveEditorComponent extends React.Component<
               {this.state.svgKeyframes && (
                 <SvgDraggableArea
                   ref={this._svgCanvas}
-                  selectKeyframe={(id: string) => this.selectKeyframe(id)}
+                  selectKeyframe={(id: string, multiselect: boolean) =>
+                    this.selectKeyframe(id, multiselect)
+                  }
                   viewBoxScale={this.state.frameAxisLength.length}
                   scale={this.state.scale}
                   keyframeSvgPoints={this.state.svgKeyframes}
+                  removeSelectedKeyframes={(points: IKeyframeSvgPoint[]) =>
+                    this.removeKeyframes(points)
+                  }
                   selectedControlPoint={(type: string, id: string) =>
                     this.selectedControlPoint(type, id)
                   }
+                  deselectKeyframes={() => this.deselectKeyframes()}
                   updatePosition={(
                     updatedSvgKeyFrame: IKeyframeSvgPoint,
                     id: string
@@ -1530,44 +1601,51 @@ export class AnimationCurveEditorComponent extends React.Component<
                       </text>
                       <line x1={f.value} y1='0' x2={f.value} y2='5%'></line>
 
+                      {f.value % this.state.fps === 0 && f.value !== 0 ? (
+                        <line
+                          x1={f.value}
+                          y1='-100'
+                          x2={f.value}
+                          y2='5%'
+                        ></line>
+                      ) : null}
+
                       {this.isCurrentFrame(f.label) ? (
                         <svg>
                           <line
                             x1={f.value}
                             y1='0'
                             x2={f.value}
-                            y2='40'
+                            y2='-100%'
                             style={{
-                              stroke: 'rgba(18, 80, 107, 0.26)',
-                              strokeWidth: 6,
+                              stroke: 'white',
+                              strokeWidth: 0.4,
                             }}
                           />
+                          <svg x={f.value} y='-1'>
+                            <circle
+                              className='svg-playhead'
+                              cx='0'
+                              cy='0'
+                              r='2%'
+                              fill='white'
+                            />
+                            <text
+                              x='-0.6%'
+                              y='1%'
+                              style={{
+                                fontSize: `${0.17 * this.state.scale}em`,
+                              }}
+                            >
+                              {f.label}
+                            </text>
+                          </svg>
                         </svg>
                       ) : null}
-
-                      {f.value % this.state.fps === 0 && f.value !== 0 ? (
-                        <line
-                          x1={f.value}
-                          y1='-100'
-                          x2={f.value}
-                          y2='5%'
-                        ></line>
-                      ) : null}
                     </svg>
                   ))}
                 </SvgDraggableArea>
               )}
-
-              {this.state.selected === null ||
-              this.state.selected === undefined ? null : (
-                <Playhead
-                  frame={this.state.currentFrame}
-                  offset={this.state.playheadOffset}
-                  onCurrentFrameChange={(frame: number) =>
-                    this.changeCurrentFrame(frame)
-                  }
-                />
-              )}
             </div>
           </div>
           <div className='row-bottom'>

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

@@ -17,6 +17,8 @@ interface IAnimationListTreeProps {
     coordinate?: SelectedCoordinate
   ) => void;
   empty: () => void;
+  editAnimation: (selected: Animation) => void;
+  deselectAnimation: () => void;
 }
 
 interface Item {
@@ -73,6 +75,7 @@ export class AnimationListTree extends React.Component<
           .entity as IAnimatable).animations = updatedAnimations as Nullable<
           Animation[]
         >;
+        this.props.deselectAnimation();
         this._list = this.generateList();
       }
     }
@@ -96,10 +99,6 @@ export class AnimationListTree extends React.Component<
     return animationList ?? null;
   }
 
-  editAnimation() {
-    console.log('Edit animation'); // TODO. Implement the edit options here
-  }
-
   toggleProperty(index: number) {
     if (this._list !== null) {
       let item = this._list[index];
@@ -139,7 +138,7 @@ export class AnimationListTree extends React.Component<
               <IconButtonLineComponent
                 tooltip='Options'
                 icon='small animation-options'
-                onClick={() => this.editAnimation()}
+                onClick={() => this.props.editAnimation(animation)}
               />
               {!(this.props.entity instanceof TargetedAnimation) ? (
                 this.props.selected &&
@@ -203,7 +202,7 @@ export class AnimationListTree extends React.Component<
               <IconButtonLineComponent
                 tooltip='Options'
                 icon='small animation-options'
-                onClick={() => this.editAnimation()}
+                onClick={() => this.props.editAnimation(animation)}
               />
               {!(this.props.entity instanceof TargetedAnimation) ? (
                 this.props.selected &&

+ 28 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/animations/editorControls.tsx

@@ -25,6 +25,7 @@ interface IEditorControlsProps {
   setFps: (fps: number) => void;
   globalState: GlobalState;
   snippetServer: string;
+  deselectAnimation: () => void;
 }
 
 export class EditorControls extends React.Component<
@@ -39,6 +40,7 @@ export class EditorControls extends React.Component<
     framesPerSecond: number;
     snippetId: string;
     loopMode: number;
+    selected: Animation | undefined;
   }
 > {
   constructor(props: IEditorControlsProps) {
@@ -56,11 +58,11 @@ export class EditorControls extends React.Component<
       animationsCount: count,
       framesPerSecond: 60,
       snippetId: '',
+      selected: undefined,
     };
   }
 
   animationAdded() {
-    // select recently created animation/first coordinate...
     this.setState({
       animationsCount: this.recountAnimations(),
       isEditTabOpen: true,
@@ -68,6 +70,14 @@ export class EditorControls extends React.Component<
     });
   }
 
+  finishedUpdate() {
+    this.setState({
+      isEditTabOpen: true,
+      isAnimationTabOpen: false,
+      selected: undefined,
+    });
+  }
+
   recountAnimations() {
     return (this.props.entity as IAnimatable).animations?.length ?? 0;
   }
@@ -160,6 +170,16 @@ export class EditorControls extends React.Component<
     });
   }
 
+  editAnimation(selected: Animation) {
+    this.setState({
+      selected: selected,
+      isEditTabOpen: false,
+      isAnimationTabOpen: true,
+      isLoadTabOpen: false,
+      isSaveTabOpen: false,
+    });
+  }
+
   render() {
     return (
       <div className='animation-list'>
@@ -229,9 +249,11 @@ export class EditorControls extends React.Component<
             setNotificationMessage={(message: string) => {
               this.props.setNotificationMessage(message);
             }}
-            changed={() => this.animationAdded()}
+            addedNewAnimation={() => this.animationAdded()}
             onPropertyChangedObservable={this.props.onPropertyChangedObservable}
             fps={this.state.framesPerSecond}
+            selectedToUpdate={this.state.selected}
+            finishedUpdate={() => this.finishedUpdate()}
           />
         )}
 
@@ -262,12 +284,16 @@ export class EditorControls extends React.Component<
 
         {this.state.isEditTabOpen ? (
           <AnimationListTree
+            deselectAnimation={() => this.props.deselectAnimation()}
             isTargetedAnimation={this.props.isTargetedAnimation}
             entity={this.props.entity}
             selected={this.props.selected}
             onPropertyChangedObservable={this.props.onPropertyChangedObservable}
             empty={() => this.emptiedList()}
             selectAnimation={this.props.selectAnimation}
+            editAnimation={(selected: Animation) =>
+              this.editAnimation(selected)
+            }
           />
         ) : null}
       </div>

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

@@ -78,12 +78,6 @@ export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
             onClick={this.props.setLerpMode}
           />
         </div>
-        <div
-          className='icon close'
-          onClick={(event: React.MouseEvent<HTMLDivElement, MouseEvent>) =>
-            this.props.close(event)
-          }
-        ></div>
       </div>
     );
   }

+ 84 - 42
inspector/src/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint.tsx

@@ -1,59 +1,101 @@
-import * as React from "react";
+import * as React from 'react';
 import { Vector2 } from 'babylonjs/Maths/math.vector';
 import { AnchorSvgPoint } from './anchorSvgPoint';
 
-const keyInactive = require("./assets/keyInactiveIcon.svg") as string;
+const keyInactive = require('./assets/keyInactiveIcon.svg') as string;
 //const keyActive = require("./assets/keyActiveIcon.svg") as string; uncomment when setting active multiselect
-const keySelected = require("./assets/keySelectedIcon.svg") as string;
+const keySelected = require('./assets/keySelectedIcon.svg') as string;
 
 export interface IKeyframeSvgPoint {
-    keyframePoint: Vector2;
-    rightControlPoint: Vector2 | null;
-    leftControlPoint: Vector2 | null;
-    id: string;
-    selected: boolean;
-    isLeftActive: boolean;
-    isRightActive: boolean;
-    curveId?: ICurveMetaData;
+  keyframePoint: Vector2;
+  rightControlPoint: Vector2 | null;
+  leftControlPoint: Vector2 | null;
+  id: string;
+  selected: boolean;
+  isLeftActive: boolean;
+  isRightActive: boolean;
+  curveId?: ICurveMetaData;
 }
 
 export interface ICurveMetaData {
-    id: number;
-    animationName: string;
-    property: string;
+  id: number;
+  animationName: string;
+  property: string;
 }
 
 interface IKeyframeSvgPointProps {
-    keyframePoint: Vector2;
-    leftControlPoint: Vector2 | null;
-    rightControlPoint: Vector2 | null;
-    id: string;
-    selected: boolean;
-    selectKeyframe: (id: string) => void;
-    selectedControlPoint: (type: string, id: string) => void;
-    isLeftActive: boolean;
-    isRightActive: boolean;
+  keyframePoint: Vector2;
+  leftControlPoint: Vector2 | null;
+  rightControlPoint: Vector2 | null;
+  id: string;
+  selected: boolean;
+  selectKeyframe: (id: string, multiselect: boolean) => void;
+  selectedControlPoint: (type: string, id: string) => void;
+  isLeftActive: boolean;
+  isRightActive: boolean;
 }
 
-export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps>{
+export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
+  constructor(props: IKeyframeSvgPointProps) {
+    super(props);
+  }
 
-    constructor(props: IKeyframeSvgPointProps) {
-        super(props);
+  select(e: React.MouseEvent<SVGImageElement>) {
+    e.preventDefault();
+    let multiSelect = false;
+    if (e.buttons === 0 && e.ctrlKey) {
+      multiSelect = true;
     }
+    this.props.selectKeyframe(this.props.id, multiSelect);
+  }
 
-    select() {
-        this.props.selectKeyframe(this.props.id);
-    }
-
-    render() {
-        return (
-            <>
-                <svg className="draggable" x={this.props.keyframePoint.x} y={this.props.keyframePoint.y} style={{ overflow: 'visible', cursor: 'pointer' }} >
-                    <image data-id={this.props.id} className="draggable" x="-1" y="-1.5" width="3" height="3" href={this.props.selected ? keySelected : keyInactive} onClick={() => this.select()} />
-                </svg>
-                {this.props.leftControlPoint && <AnchorSvgPoint type="left" index={this.props.id} control={this.props.leftControlPoint} anchor={this.props.keyframePoint} active={this.props.selected} selected={this.props.isLeftActive} selectControlPoint={(type: string) => this.props.selectedControlPoint(type, this.props.id)} />}
-                {this.props.rightControlPoint && <AnchorSvgPoint type="right" index={this.props.id} control={this.props.rightControlPoint} anchor={this.props.keyframePoint} active={this.props.selected} selected={this.props.isRightActive} selectControlPoint={(type: string) => this.props.selectedControlPoint(type, this.props.id)} />}
-            </>
-        )
-    }
-} 
+  render() {
+    return (
+      <>
+        <svg
+          className='draggable'
+          x={this.props.keyframePoint.x}
+          y={this.props.keyframePoint.y}
+          style={{ overflow: 'visible', cursor: 'pointer' }}
+        >
+          <image
+            data-id={this.props.id}
+            className='draggable'
+            x='-1'
+            y='-1.5'
+            width='3'
+            height='3'
+            href={this.props.selected ? keySelected : keyInactive}
+            onClick={(e) => this.select(e)}
+          />
+        </svg>
+        {this.props.leftControlPoint && (
+          <AnchorSvgPoint
+            type='left'
+            index={this.props.id}
+            control={this.props.leftControlPoint}
+            anchor={this.props.keyframePoint}
+            active={this.props.selected}
+            selected={this.props.isLeftActive}
+            selectControlPoint={(type: string) =>
+              this.props.selectedControlPoint(type, this.props.id)
+            }
+          />
+        )}
+        {this.props.rightControlPoint && (
+          <AnchorSvgPoint
+            type='right'
+            index={this.props.id}
+            control={this.props.rightControlPoint}
+            anchor={this.props.keyframePoint}
+            active={this.props.selected}
+            selected={this.props.isRightActive}
+            selectControlPoint={(type: string) =>
+              this.props.selectedControlPoint(type, this.props.id)
+            }
+          />
+        )}
+      </>
+    );
+  }
+}

+ 36 - 9
inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx

@@ -7,8 +7,10 @@ interface ISvgDraggableAreaProps {
   updatePosition: (updatedKeyframe: IKeyframeSvgPoint, id: string) => void;
   scale: number;
   viewBoxScale: number;
-  selectKeyframe: (id: string) => void;
+  selectKeyframe: (id: string, multiselect: boolean) => void;
   selectedControlPoint: (type: string, id: string) => void;
+  deselectKeyframes: () => void;
+  removeSelectedKeyframes: (points: IKeyframeSvgPoint[]) => void;
 }
 
 export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
@@ -18,7 +20,6 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
   private _draggableArea: React.RefObject<SVGSVGElement>;
   private _panStart: Vector2;
   private _panStop: Vector2;
-  private _width: number;
 
   constructor(props: ISvgDraggableAreaProps) {
     super(props);
@@ -39,11 +40,9 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
       this.keyUp.bind(this)
     );
     setTimeout(() => {
-      this._width =
-        this._draggableArea.current?.clientWidth !== undefined
-          ? this._draggableArea.current?.clientWidth
-          : 0;
-      console.log(this._width);
+      this._draggableArea.current?.clientWidth !== undefined
+        ? this._draggableArea.current?.clientWidth
+        : 0;
     }, 500);
   }
 
@@ -61,7 +60,7 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
     }
 
     if (e.target.classList.contains('pannable')) {
-      if (e.buttons === 1 && e.ctrlKey) {
+      if (e.buttons === 1 && e.shiftKey) {
         this._panStart.set(e.clientX, e.clientY);
       }
     }
@@ -83,8 +82,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
           // Check for NaN values here.
           if (this._isCurrentPointControl === 'left') {
             point.leftControlPoint = coord;
+            point.isLeftActive = true;
           } else if (this._isCurrentPointControl === 'right') {
             point.rightControlPoint = coord;
+            point.isRightActive = true;
           } else {
             point.keyframePoint = coord;
           }
@@ -191,11 +192,35 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
     if (e.keyCode === 17) {
       this._draggableArea.current?.style.setProperty('cursor', 'initial');
     }
+
+    if (e.keyCode === 8) {
+      const pointsToDelete = this.props.keyframeSvgPoints.filter(
+        (kf) => kf.selected
+      );
+      this.props.removeSelectedKeyframes(pointsToDelete);
+    }
   }
 
   focus(e: React.MouseEvent<SVGSVGElement>) {
     e.preventDefault();
     this._draggableArea.current?.focus();
+
+    if ((e.target as SVGSVGElement).className.baseVal == 'linear pannable') {
+      if (this.isControlPointActive()) {
+        this.props.deselectKeyframes();
+      }
+    }
+  }
+
+  isControlPointActive() {
+    const activeControlPoints = this.props.keyframeSvgPoints.filter(
+      (x) => x.isLeftActive || x.isRightActive
+    );
+    if (activeControlPoints.length !== 0) {
+      return false;
+    } else {
+      return true;
+    }
   }
 
   render() {
@@ -232,7 +257,9 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
               selectedControlPoint={(type: string, id: string) =>
                 this.props.selectedControlPoint(type, id)
               }
-              selectKeyframe={(id: string) => this.props.selectKeyframe(id)}
+              selectKeyframe={(id: string, multiselect: boolean) =>
+                this.props.selectKeyframe(id, multiselect)
+              }
             />
           ))}
         </svg>

+ 35 - 18
inspector/src/components/actionTabs/tabs/propertyGrids/animations/timeline.tsx

@@ -71,11 +71,17 @@ export class Timeline extends React.Component<
 
   calculateScrollWidth(start: number, end: number) {
     if (this._scrollContainer.current && this.props.animationLimit !== 0) {
-      const containerWidth = this._scrollContainer.current.clientWidth;
+      const containerMarginLeftRight = 6;
+      const containerWidth =
+        this._scrollContainer.current.clientWidth - containerMarginLeftRight;
       const scrollFrameLimit = this.props.animationLimit;
       const scrollFrameLength = end - start;
-      const widthPercentage = (scrollFrameLength * 100) / scrollFrameLimit;
-      const scrollPixelWidth = (widthPercentage * containerWidth) / 100;
+      const widthPercentage = Math.round(
+        (scrollFrameLength * 100) / scrollFrameLimit
+      );
+      const scrollPixelWidth = Math.round(
+        (widthPercentage * containerWidth) / 100
+      );
       if (scrollPixelWidth === Infinity || scrollPixelWidth > containerWidth) {
         return containerWidth;
       }
@@ -312,7 +318,7 @@ export class Timeline extends React.Component<
       const scrollContainerWith = this._scrollContainer.current.clientWidth;
       const startPixel = moved - 233;
       const limitRight =
-        scrollContainerWith - (this.state.scrollWidth || 0) - 5;
+        scrollContainerWith - (this.state.scrollWidth || 0) - 3;
 
       if (moved > 233 && startPixel < limitRight) {
         this._scrollbarHandle.current.style.left = moved + 'px';
@@ -346,11 +352,15 @@ export class Timeline extends React.Component<
       const frameChange = (resizePercentage * this.state.end) / 100;
       const framesTo = Math.round(frameChange);
 
-      this.setState({
-        end: framesTo,
-        scrollWidth: this.calculateScrollWidth(this.state.start, framesTo),
-        selectionLength: this.range(this.state.start, framesTo),
-      });
+      if (!(framesTo <= this.state.start + 20)) {
+        if (framesTo <= this.props.animationLimit) {
+          this.setState({
+            end: framesTo,
+            scrollWidth: this.calculateScrollWidth(this.state.start, framesTo),
+            selectionLength: this.range(this.state.start, framesTo),
+          });
+        }
+      }
     }
   }
 
@@ -365,21 +375,28 @@ export class Timeline extends React.Component<
 
       const frameChange = (resizePercentage * this.state.end) / 100;
 
-      let framesTo;
+      let framesTo: number;
       if (Math.sign(moving) === 1) {
         framesTo = this.state.start + Math.round(frameChange);
       } else {
         framesTo = this.state.start - Math.round(frameChange);
       }
-      let Toleft = framesTo * pixelFrameRatio + 233;
 
-      this._scrollbarHandle.current.style.left = Toleft + 'px';
-
-      this.setState({
-        start: framesTo,
-        scrollWidth: this.calculateScrollWidth(framesTo, this.state.end),
-        selectionLength: this.range(framesTo, this.state.end),
-      });
+      if (!(framesTo >= this.state.end - 20)) {
+        this.setState(
+          {
+            start: framesTo,
+            scrollWidth: this.calculateScrollWidth(framesTo, this.state.end),
+            selectionLength: this.range(framesTo, this.state.end),
+          },
+          () => {
+            let Toleft = framesTo * pixelFrameRatio + 233;
+            if (this._scrollbarHandle.current) {
+              this._scrollbarHandle.current.style.left = Toleft + 'px';
+            }
+          }
+        );
+      }
     }
   }