Преглед изворни кода

Merge branch 'master' into resize-editor

Alejandro Toledo пре 5 година
родитељ
комит
cba86cdfc4
46 измењених фајлова са 2536 додато и 2167 уклоњено
  1. 5 1
      Playground/src/components/rendererComponent.tsx
  2. 113 101
      dist/preview release/babylon.d.ts
  3. 2 2
      dist/preview release/babylon.js
  4. 231 252
      dist/preview release/babylon.max.js
  5. 1 1
      dist/preview release/babylon.max.js.map
  6. 233 209
      dist/preview release/babylon.module.d.ts
  7. 113 101
      dist/preview release/documentation.d.ts
  8. 2 0
      dist/preview release/gui/babylon.gui.js
  9. 1 1
      dist/preview release/gui/babylon.gui.js.map
  10. 1 1
      dist/preview release/gui/babylon.gui.min.js
  11. 6 6
      dist/preview release/inspector/babylon.inspector.bundle.js
  12. 283 312
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  13. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  14. 21 19
      dist/preview release/inspector/babylon.inspector.d.ts
  15. 45 43
      dist/preview release/inspector/babylon.inspector.module.d.ts
  16. 233 209
      dist/preview release/viewer/babylon.module.d.ts
  17. 72 68
      dist/preview release/viewer/babylon.viewer.js
  18. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  19. 4 2
      dist/preview release/what's new.md
  20. 2 0
      gui/src/2D/controls/image.ts
  21. 13 2
      inspector/src/components/actionTabs/lines/numericInputComponent.tsx
  22. 13 9
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  23. 2 2
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/bottomBar.tsx
  24. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar.tsx
  25. 0 108
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.ts
  26. 99 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.tsx
  27. 0 149
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.ts
  28. 144 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.tsx
  29. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar.tsx
  30. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent.tsx
  31. 0 75
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts
  32. 17 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss
  33. 8 6
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx
  34. 15 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/toolSettings.tsx
  35. 10 0
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  36. 4 1
      inspector/src/inspector.ts
  37. 233 35
      serializers/src/glTF/2.0/glTFAnimation.ts
  38. 230 14
      serializers/src/glTF/2.0/glTFExporter.ts
  39. 19 0
      serializers/src/glTF/2.0/glTFUtilities.ts
  40. 12 8
      src/Culling/ray.ts
  41. 24 184
      src/Helpers/photoDome.ts
  42. 294 0
      src/Helpers/textureDome.ts
  43. 18 238
      src/Helpers/videoDome.ts
  44. 3 3
      src/Rendering/utilityLayerRenderer.ts
  45. BIN
      tests/validation/ReferenceImages/gltfSerializerMorphTargetAnimation.png
  46. 5 0
      tests/validation/config.json

+ 5 - 1
Playground/src/components/rendererComponent.tsx

@@ -250,7 +250,11 @@ export class RenderingComponent extends React.Component<IRenderingComponentProps
                 });
                 return;
             } else if (globalObject.scene.then) {
-                globalObject.scene.then(function () {});
+                globalObject.scene.then(() => {
+                    if (this._engine!.scenes[0] && displayInspector) {
+                        this.props.globalState.onInspectorRequiredObservable.notifyObservers(true);
+                    }
+                });
             } else {
                 this._engine.scenes[0].executeWhenReady(function () {});
             }

+ 113 - 101
dist/preview release/babylon.d.ts

@@ -54878,80 +54878,111 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
-     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
-     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
      * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class PhotoDome extends TransformNode {
+    export abstract class TextureDome<T extends Texture> extends TransformNode {
+        protected onError: Nullable<(message?: string, exception?: any) => void>;
         /**
-         * Define the image as a Monoscopic panoramic 360 image.
+         * Define the source as a Monoscopic panoramic 360/180.
          */
         static readonly MODE_MONOSCOPIC: number;
         /**
-         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
          */
         static readonly MODE_TOPBOTTOM: number;
         /**
-         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Define the source as a Stereoscopic Side by Side panoramic 360/180.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _useDirectMapping;
+        private _halfDome;
+        protected _useDirectMapping: boolean;
         /**
          * The texture being displayed on the sphere
          */
-        protected _photoTexture: Texture;
+        protected _texture: T;
         /**
-         * Gets or sets the texture being displayed on the sphere
+         * Gets the texture being displayed on the sphere
          */
-        get photoTexture(): Texture;
-        set photoTexture(value: Texture);
+        get texture(): T;
         /**
-         * Observable raised when an error occured while loading the 360 image
+         * Sets the texture being displayed on the sphere
          */
-        onLoadErrorObservable: Observable<string>;
+        set texture(newTexture: T);
         /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;
         /**
-         * The surface used for the skybox
+         * The surface used for the dome
          */
         protected _mesh: Mesh;
         /**
-         * Gets the mesh used for the skybox.
+         * Gets the mesh used for the dome.
          */
         get mesh(): Mesh;
         /**
+         * A mesh that will be used to mask the back of the dome in case it is a 180 degree movie.
+         */
+        private _halfDomeMask;
+        /**
          * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
          * Also see the options.resolution property.
          */
         get fovMultiplier(): number;
         set fovMultiplier(value: number);
-        private _imageMode;
+        protected _textureMode: number;
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-         * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-         * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Gets or set the current texture mode for the texture. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
-        get imageMode(): number;
-        set imageMode(value: number);
+        get textureMode(): number;
+        /**
+         * Sets the current texture mode for the texture. It can be:
+          * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set textureMode(value: number);
+        /**
+         * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
+         */
+        get halfDome(): boolean;
+        /**
+         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+         */
+        set halfDome(enabled: boolean);
+        /**
+         * Oberserver used in Stereoscopic VR Mode.
+         */
+        private _onBeforeCameraRenderObserver;
+        /**
+         * Observable raised when an error occured while loading the 360 image
+         */
+        onLoadErrorObservable: Observable<string>;
         /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
+         * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
          * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOfPhoto defines the url of the photo to display
-         * @param options defines an object containing optional or exposed sub element properties
-         * @param onError defines a callback called when an error occured while loading the texture
+         * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
+         * @param options An object containing optional or exposed sub element properties
          */
-        constructor(name: string, urlOfPhoto: string, options: {
+        constructor(name: string, textureUrlOrElement: string | string[] | HTMLVideoElement, options: {
             resolution?: number;
+            clickToPlay?: boolean;
+            autoPlay?: boolean;
+            loop?: boolean;
             size?: number;
-            useDirectMapping?: boolean;
+            poster?: string;
             faceForward?: boolean;
+            useDirectMapping?: boolean;
+            halfDomeMode?: boolean;
         }, scene: Scene, onError?: Nullable<(message?: string, exception?: any) => void>);
-        private _onBeforeCameraRenderObserver;
-        private _changeImageMode;
+        protected abstract _initTexture(urlsOrElement: string | string[] | HTMLElement, scene: Scene, options: any): T;
+        protected _changeTextureMode(value: number): void;
         /**
          * Releases resources associated with this node.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -54962,6 +54993,51 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
+     * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+     */
+    export class PhotoDome extends TextureDome<Texture> {
+        /**
+         * Define the image as a Monoscopic panoramic 360 image.
+         */
+        static readonly MODE_MONOSCOPIC: number;
+        /**
+         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         */
+        static readonly MODE_TOPBOTTOM: number;
+        /**
+         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         */
+        static readonly MODE_SIDEBYSIDE: number;
+        /**
+         * Gets or sets the texture being displayed on the sphere
+         */
+        get photoTexture(): Texture;
+        /**
+         * sets the texture being displayed on the sphere
+         */
+        set photoTexture(value: Texture);
+        /**
+         * Gets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        get imageMode(): number;
+        /**
+         * Sets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set imageMode(value: number);
+        protected _initTexture(urlsOrElement: string, scene: Scene, options: any): Texture;
+    }
+}
+declare module BABYLON {
+    /**
      * Class used to host RGBD texture specific utilities
      */
     export class RGBDTextureTools {
@@ -57662,7 +57738,7 @@ declare module BABYLON {
      * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class VideoDome extends TransformNode {
+    export class VideoDome extends TextureDome<VideoTexture> {
         /**
          * Define the video source as a Monoscopic panoramic 360 video.
          */
@@ -57675,84 +57751,20 @@ declare module BABYLON {
          * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _halfDome;
-        private _useDirectMapping;
         /**
-         * The video texture being displayed on the sphere
-         */
-        protected _videoTexture: VideoTexture;
-        /**
-         * Gets the video texture being displayed on the sphere
+         * Get the video texture associated with this video dome
          */
         get videoTexture(): VideoTexture;
         /**
-         * The skybox material
-         */
-        protected _material: BackgroundMaterial;
-        /**
-         * The surface used for the video dome
-         */
-        protected _mesh: Mesh;
-        /**
-         * Gets the mesh used for the video dome.
-         */
-        get mesh(): Mesh;
-        /**
-         * A mesh that will be used to mask the back of the video dome in case it is a 180 degree movie.
-         */
-        private _halfDomeMask;
-        /**
-         * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-         * Also see the options.resolution property.
-         */
-        get fovMultiplier(): number;
-        set fovMultiplier(value: number);
-        private _videoMode;
-        /**
-         * Gets or set the current video mode for the video. It can be:
-         * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-         * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-         * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+         * Get the video mode of this dome
          */
         get videoMode(): number;
-        set videoMode(value: number);
-        /**
-         * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-         *
-         */
-        get halfDome(): boolean;
-        /**
-         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
-         */
-        set halfDome(enabled: boolean);
-        /**
-         * Oberserver used in Stereoscopic VR Mode.
-         */
-        private _onBeforeCameraRenderObserver;
-        /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
-         * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOrVideo defines the url(s) or the video element to use
-         * @param options An object containing optional or exposed sub element properties
-         */
-        constructor(name: string, urlsOrVideo: string | string[] | HTMLVideoElement, options: {
-            resolution?: number;
-            clickToPlay?: boolean;
-            autoPlay?: boolean;
-            loop?: boolean;
-            size?: number;
-            poster?: string;
-            faceForward?: boolean;
-            useDirectMapping?: boolean;
-            halfDomeMode?: boolean;
-        }, scene: Scene);
-        private _changeVideoMode;
         /**
-         * Releases resources associated with this node.
-         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
+         * Set the video mode of this dome.
+         * @see textureMode
          */
-        dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
+        set videoMode(value: number);
+        protected _initTexture(urlsOrElement: string | string[] | HTMLVideoElement, scene: Scene, options: any): VideoTexture;
     }
 }
 declare module BABYLON {

Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
dist/preview release/babylon.js


+ 231 - 252
dist/preview release/babylon.max.js

@@ -28554,11 +28554,14 @@ _scene__WEBPACK_IMPORTED_MODULE_4__["Scene"].prototype._internalPick = function
         else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
             continue;
         }
+        var world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
         if (mesh.hasThinInstances && mesh.thinInstanceEnablePicking) {
+            var tmpMatrix = _Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["TmpVectors"].Matrix[0];
             var thinMatrices = mesh.thinInstanceGetWorldMatrices();
             for (var index = 0; index < thinMatrices.length; index++) {
-                var world = thinMatrices[index];
-                var result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate);
+                var thinMatrix = thinMatrices[index];
+                thinMatrix.multiplyToRef(world, tmpMatrix);
+                var result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, tmpMatrix, fastCheck, onlyBoundingInfo, trianglePredicate);
                 if (result) {
                     pickingInfo = result;
                     pickingInfo.thinInstanceIndex = index;
@@ -28569,7 +28572,6 @@ _scene__WEBPACK_IMPORTED_MODULE_4__["Scene"].prototype._internalPick = function
             }
         }
         else {
-            var world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
             var result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate);
             if (result) {
                 pickingInfo = result;
@@ -28596,11 +28598,14 @@ _scene__WEBPACK_IMPORTED_MODULE_4__["Scene"].prototype._internalMultiPick = func
         else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
             continue;
         }
+        var world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
         if (mesh.hasThinInstances && mesh.thinInstanceEnablePicking) {
+            var tmpMatrix = _Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["TmpVectors"].Matrix[0];
             var thinMatrices = mesh.thinInstanceGetWorldMatrices();
             for (var index = 0; index < thinMatrices.length; index++) {
-                var world = thinMatrices[index];
-                var result = this._internalPickForMesh(null, rayFunction, mesh, world, false, false, trianglePredicate);
+                var thinMatrix = thinMatrices[index];
+                thinMatrix.multiplyToRef(world, tmpMatrix);
+                var result = this._internalPickForMesh(null, rayFunction, mesh, tmpMatrix, false, false, trianglePredicate);
                 if (result) {
                     result.thinInstanceIndex = index;
                     pickingInfos.push(result);
@@ -28608,7 +28613,6 @@ _scene__WEBPACK_IMPORTED_MODULE_4__["Scene"].prototype._internalMultiPick = func
             }
         }
         else {
-            var world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
             var result = this._internalPickForMesh(null, rayFunction, mesh, world, false, false, trianglePredicate);
             if (result) {
                 pickingInfos.push(result);
@@ -50422,18 +50426,8 @@ __webpack_require__.r(__webpack_exports__);
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PhotoDome", function() { return PhotoDome; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var _Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Misc/observable */ "./Misc/observable.ts");
-/* harmony import */ var _Meshes_transformNode__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Meshes/transformNode */ "./Meshes/transformNode.ts");
-/* harmony import */ var _Meshes_mesh__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Meshes/mesh */ "./Meshes/mesh.ts");
-/* harmony import */ var _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../Materials/Textures/texture */ "./Materials/Textures/texture.ts");
-/* harmony import */ var _Materials_Background_backgroundMaterial__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../Materials/Background/backgroundMaterial */ "./Materials/Background/backgroundMaterial.ts");
-/* harmony import */ var _Meshes_Builders_sphereBuilder__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../Meshes/Builders/sphereBuilder */ "./Meshes/Builders/sphereBuilder.ts");
-/* harmony import */ var _Maths_math_vector__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../Maths/math.vector */ "./Maths/math.vector.ts");
-
-
-
-
-
+/* harmony import */ var _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Materials/Textures/texture */ "./Materials/Textures/texture.ts");
+/* harmony import */ var _textureDome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./textureDome */ "./Helpers/textureDome.ts");
 
 
 
@@ -50445,188 +50439,70 @@ __webpack_require__.r(__webpack_exports__);
  */
 var PhotoDome = /** @class */ (function (_super) {
     Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(PhotoDome, _super);
-    /**
-     * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
-     * @param name Element's name, child elements will append suffixes for their own names.
-     * @param urlsOfPhoto defines the url of the photo to display
-     * @param options defines an object containing optional or exposed sub element properties
-     * @param onError defines a callback called when an error occured while loading the texture
-     */
-    function PhotoDome(name, urlOfPhoto, options, scene, onError) {
-        if (onError === void 0) { onError = null; }
-        var _this = _super.call(this, name, scene) || this;
-        _this._useDirectMapping = false;
-        /**
-         * Observable raised when an error occured while loading the 360 image
-         */
-        _this.onLoadErrorObservable = new _Misc_observable__WEBPACK_IMPORTED_MODULE_1__["Observable"]();
-        _this._imageMode = PhotoDome.MODE_MONOSCOPIC;
-        _this._onBeforeCameraRenderObserver = null;
-        // set defaults and manage values
-        name = name || "photoDome";
-        options.resolution = (Math.abs(options.resolution) | 0) || 32;
-        options.size = Math.abs(options.size) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
-        if (options.useDirectMapping === undefined) {
-            _this._useDirectMapping = true;
-        }
-        else {
-            _this._useDirectMapping = options.useDirectMapping;
-        }
-        if (options.faceForward === undefined) {
-            options.faceForward = true;
-        }
-        _this._setReady(false);
-        // create
-        var material = _this._material = new _Materials_Background_backgroundMaterial__WEBPACK_IMPORTED_MODULE_5__["BackgroundMaterial"](name + "_material", scene);
-        _this._mesh = _Meshes_mesh__WEBPACK_IMPORTED_MODULE_3__["Mesh"].CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, _Meshes_mesh__WEBPACK_IMPORTED_MODULE_3__["Mesh"].BACKSIDE);
-        // configure material
-        material.opacityFresnel = false;
-        material.useEquirectangularFOV = true;
-        material.fovMultiplier = 1.0;
-        _this.photoTexture = new _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_4__["Texture"](urlOfPhoto, scene, true, !_this._useDirectMapping, undefined, undefined, function (message, exception) {
-            _this.onLoadErrorObservable.notifyObservers(message || "Unknown error occured");
-            if (onError) {
-                onError(message, exception);
-            }
-        });
-        _this.photoTexture.anisotropicFilteringLevel = 1;
-        _this.photoTexture.onLoadObservable.addOnce(function () {
-            _this._setReady(true);
-        });
-        // configure mesh
-        _this._mesh.material = material;
-        _this._mesh.parent = _this;
-        // Initial rotation
-        if (options.faceForward && scene.activeCamera) {
-            var camera = scene.activeCamera;
-            var forward = _Maths_math_vector__WEBPACK_IMPORTED_MODULE_7__["Vector3"].Forward();
-            var direction = _Maths_math_vector__WEBPACK_IMPORTED_MODULE_7__["Vector3"].TransformNormal(forward, camera.getViewMatrix());
-            direction.normalize();
-            _this.rotation.y = Math.acos(_Maths_math_vector__WEBPACK_IMPORTED_MODULE_7__["Vector3"].Dot(forward, direction));
-        }
-        return _this;
+    function PhotoDome() {
+        return _super !== null && _super.apply(this, arguments) || this;
     }
     Object.defineProperty(PhotoDome.prototype, "photoTexture", {
         /**
          * Gets or sets the texture being displayed on the sphere
          */
         get: function () {
-            return this._photoTexture;
-        },
-        set: function (value) {
-            if (this._photoTexture === value) {
-                return;
-            }
-            this._photoTexture = value;
-            if (this._useDirectMapping) {
-                this._photoTexture.wrapU = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_4__["Texture"].CLAMP_ADDRESSMODE;
-                this._photoTexture.wrapV = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_4__["Texture"].CLAMP_ADDRESSMODE;
-                this._material.diffuseTexture = this._photoTexture;
-            }
-            else {
-                this._photoTexture.coordinatesMode = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_4__["Texture"].FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
-                this._photoTexture.wrapV = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_4__["Texture"].CLAMP_ADDRESSMODE;
-                this._material.reflectionTexture = this._photoTexture;
-            }
-        },
-        enumerable: false,
-        configurable: true
-    });
-    Object.defineProperty(PhotoDome.prototype, "mesh", {
-        /**
-         * Gets the mesh used for the skybox.
-         */
-        get: function () {
-            return this._mesh;
+            return this.texture;
         },
-        enumerable: false,
-        configurable: true
-    });
-    Object.defineProperty(PhotoDome.prototype, "fovMultiplier", {
         /**
-         * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-         * Also see the options.resolution property.
+         * sets the texture being displayed on the sphere
          */
-        get: function () {
-            return this._material.fovMultiplier;
-        },
         set: function (value) {
-            this._material.fovMultiplier = value;
+            this.texture = value;
         },
         enumerable: false,
         configurable: true
     });
     Object.defineProperty(PhotoDome.prototype, "imageMode", {
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-         * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-         * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Gets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
         get: function () {
-            return this._imageMode;
+            return this.textureMode;
         },
+        /**
+         * Sets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
         set: function (value) {
-            if (this._imageMode === value) {
-                return;
-            }
-            this._changeImageMode(value);
+            this.textureMode = value;
         },
         enumerable: false,
         configurable: true
     });
-    PhotoDome.prototype._changeImageMode = function (value) {
+    PhotoDome.prototype._initTexture = function (urlsOrElement, scene, options) {
         var _this = this;
-        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
-        this._imageMode = value;
-        // Default Setup and Reset.
-        this._photoTexture.uScale = 1;
-        this._photoTexture.vScale = 1;
-        this._photoTexture.uOffset = 0;
-        this._photoTexture.vOffset = 0;
-        switch (value) {
-            case PhotoDome.MODE_SIDEBYSIDE:
-                this._photoTexture.uScale = 0.5;
-                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add(function (camera) {
-                    _this._photoTexture.uOffset = camera.isRightCamera ? 0.5 : 0.0;
-                });
-                break;
-            case PhotoDome.MODE_TOPBOTTOM:
-                this._photoTexture.vScale = 0.5;
-                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add(function (camera) {
-                    _this._photoTexture.vOffset = camera.isRightCamera ? 0.5 : 0.0;
-                });
-                break;
-        }
-    };
-    /**
-     * Releases resources associated with this node.
-     * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-     * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
-     */
-    PhotoDome.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
-        if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
-        this._photoTexture.dispose();
-        this._mesh.dispose();
-        this._material.dispose();
-        this.onLoadErrorObservable.clear();
-        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
-        _super.prototype.dispose.call(this, doNotRecurse, disposeMaterialAndTextures);
+        return new _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_1__["Texture"](urlsOrElement, scene, true, !this._useDirectMapping, undefined, undefined, function (message, exception) {
+            _this.onLoadErrorObservable.notifyObservers(message || "Unknown error occured");
+            if (_this.onError) {
+                _this.onError(message, exception);
+            }
+        });
     };
     /**
      * Define the image as a Monoscopic panoramic 360 image.
      */
-    PhotoDome.MODE_MONOSCOPIC = 0;
+    PhotoDome.MODE_MONOSCOPIC = _textureDome__WEBPACK_IMPORTED_MODULE_2__["TextureDome"].MODE_MONOSCOPIC;
     /**
      * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
      */
-    PhotoDome.MODE_TOPBOTTOM = 1;
+    PhotoDome.MODE_TOPBOTTOM = _textureDome__WEBPACK_IMPORTED_MODULE_2__["TextureDome"].MODE_TOPBOTTOM;
     /**
      * Define the image as a Stereoscopic Side by Side panoramic 360 image.
      */
-    PhotoDome.MODE_SIDEBYSIDE = 2;
+    PhotoDome.MODE_SIDEBYSIDE = _textureDome__WEBPACK_IMPORTED_MODULE_2__["TextureDome"].MODE_SIDEBYSIDE;
     return PhotoDome;
-}(_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_2__["TransformNode"]));
+}(_textureDome__WEBPACK_IMPORTED_MODULE_2__["TextureDome"]));
 
 
 
@@ -50807,23 +50683,23 @@ _scene__WEBPACK_IMPORTED_MODULE_1__["Scene"].prototype.createDefaultXRExperience
 
 /***/ }),
 
-/***/ "./Helpers/videoDome.ts":
-/*!******************************!*\
-  !*** ./Helpers/videoDome.ts ***!
-  \******************************/
-/*! exports provided: VideoDome */
+/***/ "./Helpers/textureDome.ts":
+/*!********************************!*\
+  !*** ./Helpers/textureDome.ts ***!
+  \********************************/
+/*! exports provided: TextureDome */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VideoDome", function() { return VideoDome; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextureDome", function() { return TextureDome; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Meshes/transformNode */ "./Meshes/transformNode.ts");
 /* harmony import */ var _Meshes_mesh__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Meshes/mesh */ "./Meshes/mesh.ts");
 /* harmony import */ var _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Materials/Textures/texture */ "./Materials/Textures/texture.ts");
-/* harmony import */ var _Materials_Textures_videoTexture__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../Materials/Textures/videoTexture */ "./Materials/Textures/videoTexture.ts");
-/* harmony import */ var _Materials_Background_backgroundMaterial__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../Materials/Background/backgroundMaterial */ "./Materials/Background/backgroundMaterial.ts");
-/* harmony import */ var _Meshes_Builders_sphereBuilder__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../Meshes/Builders/sphereBuilder */ "./Meshes/Builders/sphereBuilder.ts");
+/* harmony import */ var _Materials_Background_backgroundMaterial__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../Materials/Background/backgroundMaterial */ "./Materials/Background/backgroundMaterial.ts");
+/* harmony import */ var _Meshes_Builders_sphereBuilder__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../Meshes/Builders/sphereBuilder */ "./Meshes/Builders/sphereBuilder.ts");
+/* harmony import */ var _Misc_observable__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../Misc/observable */ "./Misc/observable.ts");
 /* harmony import */ var _Maths_math_vector__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../Maths/math.vector */ "./Maths/math.vector.ts");
 /* harmony import */ var _Maths_math__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../Maths/math */ "./Maths/math.ts");
 
@@ -50837,31 +50713,37 @@ __webpack_require__.r(__webpack_exports__);
 
 
 /**
- * Display a 360/180 degree video on an approximately spherical surface, useful for VR applications or skyboxes.
- * As a subclass of TransformNode, this allow parenting to the camera or multiple videos with different locations in the scene.
- * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
+ * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+ * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
+ * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
  * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
  */
-var VideoDome = /** @class */ (function (_super) {
-    Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(VideoDome, _super);
+var TextureDome = /** @class */ (function (_super) {
+    Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(TextureDome, _super);
     /**
-     * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
+     * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
      * @param name Element's name, child elements will append suffixes for their own names.
-     * @param urlsOrVideo defines the url(s) or the video element to use
+     * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
      * @param options An object containing optional or exposed sub element properties
      */
-    function VideoDome(name, urlsOrVideo, options, scene) {
+    function TextureDome(name, textureUrlOrElement, options, scene, onError) {
+        if (onError === void 0) { onError = null; }
         var _this = _super.call(this, name, scene) || this;
+        _this.onError = onError;
         _this._halfDome = false;
         _this._useDirectMapping = false;
-        _this._videoMode = VideoDome.MODE_MONOSCOPIC;
+        _this._textureMode = TextureDome.MODE_MONOSCOPIC;
         /**
          * Oberserver used in Stereoscopic VR Mode.
          */
         _this._onBeforeCameraRenderObserver = null;
+        /**
+         * Observable raised when an error occured while loading the 360 image
+         */
+        _this.onLoadErrorObservable = new _Misc_observable__WEBPACK_IMPORTED_MODULE_6__["Observable"]();
         scene = _this.getScene();
         // set defaults and manage values
-        name = name || "videoDome";
+        name = name || "textureDome";
         options.resolution = Math.abs(options.resolution) | 0 || 32;
         options.clickToPlay = Boolean(options.clickToPlay);
         options.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
@@ -50877,46 +50759,30 @@ var VideoDome = /** @class */ (function (_super) {
             options.faceForward = true;
         }
         _this._setReady(false);
-        // create
-        var tempOptions = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
-        var material = (_this._material = new _Materials_Background_backgroundMaterial__WEBPACK_IMPORTED_MODULE_5__["BackgroundMaterial"](name + "_material", scene));
-        var texture = (_this._videoTexture = new _Materials_Textures_videoTexture__WEBPACK_IMPORTED_MODULE_4__["VideoTexture"](name + "_texture", urlsOrVideo, scene, false, _this._useDirectMapping, _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].TRILINEAR_SAMPLINGMODE, tempOptions));
         _this._mesh = _Meshes_mesh__WEBPACK_IMPORTED_MODULE_2__["Mesh"].CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, _Meshes_mesh__WEBPACK_IMPORTED_MODULE_2__["Mesh"].BACKSIDE);
-        texture.anisotropicFilteringLevel = 1;
-        texture.onLoadObservable.addOnce(function () {
-            _this._setReady(true);
-        });
         // configure material
+        var material = (_this._material = new _Materials_Background_backgroundMaterial__WEBPACK_IMPORTED_MODULE_4__["BackgroundMaterial"](name + "_material", scene));
         material.useEquirectangularFOV = true;
         material.fovMultiplier = 1.0;
         material.opacityFresnel = false;
-        if (_this._useDirectMapping) {
-            texture.wrapU = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].CLAMP_ADDRESSMODE;
-            texture.wrapV = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].CLAMP_ADDRESSMODE;
-            material.diffuseTexture = texture;
-        }
-        else {
-            texture.coordinatesMode = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
-            texture.wrapV = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].CLAMP_ADDRESSMODE;
-            material.reflectionTexture = texture;
-        }
+        var texture = _this._initTexture(textureUrlOrElement, scene, options);
+        _this.texture = texture;
         // configure mesh
         _this._mesh.material = material;
         _this._mesh.parent = _this;
-        // create a (disabled until needed) mask to cover unneeded segments of 180 videos.
-        _this._halfDomeMask = _Meshes_Builders_sphereBuilder__WEBPACK_IMPORTED_MODULE_6__["SphereBuilder"].CreateSphere("", { slice: 0.5, diameter: options.size * 0.99, segments: options.resolution, sideOrientation: _Meshes_mesh__WEBPACK_IMPORTED_MODULE_2__["Mesh"].BACKSIDE }, scene);
+        // create a (disabled until needed) mask to cover unneeded segments of 180 texture.
+        _this._halfDomeMask = _Meshes_Builders_sphereBuilder__WEBPACK_IMPORTED_MODULE_5__["SphereBuilder"].CreateSphere("", { slice: 0.5, diameter: options.size * 0.98, segments: options.resolution * 2, sideOrientation: _Meshes_mesh__WEBPACK_IMPORTED_MODULE_2__["Mesh"].BACKSIDE }, scene);
         _this._halfDomeMask.rotate(_Maths_math__WEBPACK_IMPORTED_MODULE_8__["Axis"].X, -Math.PI / 2);
         // set the parent, so it will always be positioned correctly AND will be disposed when the main sphere is disposed
         _this._halfDomeMask.parent = _this._mesh;
         _this._halfDome = !!options.halfDomeMode;
         // enable or disable according to the settings
         _this._halfDomeMask.setEnabled(_this._halfDome);
-        // optional configuration
-        if (options.clickToPlay) {
-            scene.onPointerUp = function () {
-                _this._videoTexture.video.play();
-            };
-        }
+        // create
+        _this._texture.anisotropicFilteringLevel = 1;
+        _this._texture.onLoadObservable.addOnce(function () {
+            _this._setReady(true);
+        });
         // Initial rotation
         if (options.faceForward && scene.activeCamera) {
             var camera = scene.activeCamera;
@@ -50925,22 +50791,41 @@ var VideoDome = /** @class */ (function (_super) {
             direction.normalize();
             _this.rotation.y = Math.acos(_Maths_math_vector__WEBPACK_IMPORTED_MODULE_7__["Vector3"].Dot(forward, direction));
         }
-        _this._changeVideoMode(_this._videoMode);
+        _this._changeTextureMode(_this._textureMode);
         return _this;
     }
-    Object.defineProperty(VideoDome.prototype, "videoTexture", {
+    Object.defineProperty(TextureDome.prototype, "texture", {
         /**
-         * Gets the video texture being displayed on the sphere
+         * Gets the texture being displayed on the sphere
          */
         get: function () {
-            return this._videoTexture;
+            return this._texture;
+        },
+        /**
+         * Sets the texture being displayed on the sphere
+         */
+        set: function (newTexture) {
+            if (this._texture === newTexture) {
+                return;
+            }
+            this._texture = newTexture;
+            if (this._useDirectMapping) {
+                this._texture.wrapU = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].CLAMP_ADDRESSMODE;
+                this._texture.wrapV = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].CLAMP_ADDRESSMODE;
+                this._material.diffuseTexture = this._texture;
+            }
+            else {
+                this._texture.coordinatesMode = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
+                this._texture.wrapV = _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_3__["Texture"].CLAMP_ADDRESSMODE;
+                this._material.reflectionTexture = this._texture;
+            }
         },
         enumerable: false,
         configurable: true
     });
-    Object.defineProperty(VideoDome.prototype, "mesh", {
+    Object.defineProperty(TextureDome.prototype, "mesh", {
         /**
-         * Gets the mesh used for the video dome.
+         * Gets the mesh used for the dome.
          */
         get: function () {
             return this._mesh;
@@ -50948,7 +50833,7 @@ var VideoDome = /** @class */ (function (_super) {
         enumerable: false,
         configurable: true
     });
-    Object.defineProperty(VideoDome.prototype, "fovMultiplier", {
+    Object.defineProperty(TextureDome.prototype, "fovMultiplier", {
         /**
          * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
          * Also see the options.resolution property.
@@ -50962,29 +50847,34 @@ var VideoDome = /** @class */ (function (_super) {
         enumerable: false,
         configurable: true
     });
-    Object.defineProperty(VideoDome.prototype, "videoMode", {
+    Object.defineProperty(TextureDome.prototype, "textureMode", {
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-         * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-         * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+         * Gets or set the current texture mode for the texture. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
         get: function () {
-            return this._videoMode;
+            return this._textureMode;
         },
+        /**
+         * Sets the current texture mode for the texture. It can be:
+          * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
         set: function (value) {
-            if (this._videoMode === value) {
+            if (this._textureMode === value) {
                 return;
             }
-            this._changeVideoMode(value);
+            this._changeTextureMode(value);
         },
         enumerable: false,
         configurable: true
     });
-    Object.defineProperty(VideoDome.prototype, "halfDome", {
+    Object.defineProperty(TextureDome.prototype, "halfDome", {
         /**
-         * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-         *
+         * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
          */
         get: function () {
             return this._halfDome;
@@ -50999,37 +50889,38 @@ var VideoDome = /** @class */ (function (_super) {
         enumerable: false,
         configurable: true
     });
-    VideoDome.prototype._changeVideoMode = function (value) {
+    TextureDome.prototype._changeTextureMode = function (value) {
         var _this = this;
         this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
-        this._videoMode = value;
+        this._textureMode = value;
         // Default Setup and Reset.
-        this._videoTexture.uScale = 1;
-        this._videoTexture.vScale = 1;
-        this._videoTexture.uOffset = 0;
-        this._videoTexture.vOffset = 0;
+        this._texture.uScale = 1;
+        this._texture.vScale = 1;
+        this._texture.uOffset = 0;
+        this._texture.vOffset = 0;
         switch (value) {
-            case VideoDome.MODE_MONOSCOPIC:
+            case TextureDome.MODE_MONOSCOPIC:
                 if (this._halfDome) {
-                    this._videoTexture.uScale = 2;
+                    this._texture.uScale = 2;
+                    this._texture.uOffset = -1;
                 }
                 break;
-            case VideoDome.MODE_SIDEBYSIDE:
-                // in half-dome mode the uScale should be double of 360 videos
+            case TextureDome.MODE_SIDEBYSIDE:
+                // in half-dome mode the uScale should be double of 360 texture
                 // Use 0.99999 to boost perf by not switching program
-                this._videoTexture.uScale = this._halfDome ? 0.99999 : 0.5;
+                this._texture.uScale = this._halfDome ? 0.99999 : 0.5;
                 var rightOffset_1 = this._halfDome ? 0.0 : 0.5;
                 var leftOffset_1 = this._halfDome ? 0.5 : 0.0;
                 this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add(function (camera) {
-                    _this._videoTexture.uOffset = camera.isRightCamera ? rightOffset_1 : leftOffset_1;
+                    _this._texture.uOffset = camera.isRightCamera ? rightOffset_1 : leftOffset_1;
                 });
                 break;
-            case VideoDome.MODE_TOPBOTTOM:
-                // in half-dome mode the vScale should be double of 360 videos
+            case TextureDome.MODE_TOPBOTTOM:
+                // in half-dome mode the vScale should be double of 360 texture
                 // Use 0.99999 to boost perf by not switching program
-                this._videoTexture.vScale = this._halfDome ? 0.99999 : 0.5;
+                this._texture.vScale = this._halfDome ? 0.99999 : 0.5;
                 this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add(function (camera) {
-                    _this._videoTexture.vOffset = camera.isRightCamera ? 0.5 : 0.0;
+                    _this._texture.vOffset = camera.isRightCamera ? 0.5 : 0.0;
                 });
                 break;
         }
@@ -51039,28 +50930,116 @@ var VideoDome = /** @class */ (function (_super) {
      * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
      * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
      */
-    VideoDome.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
+    TextureDome.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
         if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
-        this._videoTexture.dispose();
+        this._texture.dispose();
         this._mesh.dispose();
         this._material.dispose();
         this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
+        this.onLoadErrorObservable.clear();
         _super.prototype.dispose.call(this, doNotRecurse, disposeMaterialAndTextures);
     };
     /**
+     * Define the source as a Monoscopic panoramic 360/180.
+     */
+    TextureDome.MODE_MONOSCOPIC = 0;
+    /**
+     * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
+     */
+    TextureDome.MODE_TOPBOTTOM = 1;
+    /**
+     * Define the source as a Stereoscopic Side by Side panoramic 360/180.
+     */
+    TextureDome.MODE_SIDEBYSIDE = 2;
+    return TextureDome;
+}(_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__["TransformNode"]));
+
+
+
+/***/ }),
+
+/***/ "./Helpers/videoDome.ts":
+/*!******************************!*\
+  !*** ./Helpers/videoDome.ts ***!
+  \******************************/
+/*! exports provided: VideoDome */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VideoDome", function() { return VideoDome; });
+/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
+/* harmony import */ var _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Materials/Textures/texture */ "./Materials/Textures/texture.ts");
+/* harmony import */ var _Materials_Textures_videoTexture__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Materials/Textures/videoTexture */ "./Materials/Textures/videoTexture.ts");
+/* harmony import */ var _textureDome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./textureDome */ "./Helpers/textureDome.ts");
+
+
+
+
+/**
+ * Display a 360/180 degree video on an approximately spherical surface, useful for VR applications or skyboxes.
+ * As a subclass of TransformNode, this allow parenting to the camera or multiple videos with different locations in the scene.
+ * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
+ * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+ */
+var VideoDome = /** @class */ (function (_super) {
+    Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(VideoDome, _super);
+    function VideoDome() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    Object.defineProperty(VideoDome.prototype, "videoTexture", {
+        /**
+         * Get the video texture associated with this video dome
+         */
+        get: function () {
+            return this._texture;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    Object.defineProperty(VideoDome.prototype, "videoMode", {
+        /**
+         * Get the video mode of this dome
+         */
+        get: function () {
+            return this.textureMode;
+        },
+        /**
+         * Set the video mode of this dome.
+         * @see textureMode
+         */
+        set: function (value) {
+            this.textureMode = value;
+        },
+        enumerable: false,
+        configurable: true
+    });
+    VideoDome.prototype._initTexture = function (urlsOrElement, scene, options) {
+        var _this = this;
+        var tempOptions = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
+        var texture = new _Materials_Textures_videoTexture__WEBPACK_IMPORTED_MODULE_2__["VideoTexture"]((this.name || "videoDome") + "_texture", urlsOrElement, scene, false, this._useDirectMapping, _Materials_Textures_texture__WEBPACK_IMPORTED_MODULE_1__["Texture"].TRILINEAR_SAMPLINGMODE, tempOptions);
+        // optional configuration
+        if (options.clickToPlay) {
+            scene.onPointerUp = function () {
+                _this._texture.video.play();
+            };
+        }
+        return texture;
+    };
+    /**
      * Define the video source as a Monoscopic panoramic 360 video.
      */
-    VideoDome.MODE_MONOSCOPIC = 0;
+    VideoDome.MODE_MONOSCOPIC = _textureDome__WEBPACK_IMPORTED_MODULE_3__["TextureDome"].MODE_MONOSCOPIC;
     /**
      * Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
      */
-    VideoDome.MODE_TOPBOTTOM = 1;
+    VideoDome.MODE_TOPBOTTOM = _textureDome__WEBPACK_IMPORTED_MODULE_3__["TextureDome"].MODE_TOPBOTTOM;
     /**
      * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
      */
-    VideoDome.MODE_SIDEBYSIDE = 2;
+    VideoDome.MODE_SIDEBYSIDE = _textureDome__WEBPACK_IMPORTED_MODULE_3__["TextureDome"].MODE_SIDEBYSIDE;
     return VideoDome;
-}(_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__["TransformNode"]));
+}(_textureDome__WEBPACK_IMPORTED_MODULE_3__["TextureDome"]));
 
 
 
@@ -177163,7 +177142,7 @@ var UtilityLayerRenderer = /** @class */ (function () {
                 // allow every non pointer down event to flow to the utility layer
                 if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerEventTypes"].POINTERDOWN) {
                     if (!prePointerInfo.skipOnPointerObservable) {
-                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerInfo"](prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerInfo"](prePointerInfo.type, prePointerInfo.event, utilityScenePick), prePointerInfo.type);
                     }
                     if (prePointerInfo.type === _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerEventTypes"].POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
                         _this._pointerCaptures[pointerEvent.pointerId] = false;
@@ -177174,7 +177153,7 @@ var UtilityLayerRenderer = /** @class */ (function () {
                     // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
                     if (utilityScenePick && utilityScenePick.hit) {
                         if (!prePointerInfo.skipOnPointerObservable) {
-                            _this.utilityLayerScene.onPointerObservable.notifyObservers(new _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerInfo"](prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                            _this.utilityLayerScene.onPointerObservable.notifyObservers(new _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerInfo"](prePointerInfo.type, prePointerInfo.event, utilityScenePick), prePointerInfo.type);
                         }
                         prePointerInfo.skipOnPointerObservable = true;
                     }
@@ -177322,7 +177301,7 @@ var UtilityLayerRenderer = /** @class */ (function () {
     });
     UtilityLayerRenderer.prototype._notifyObservers = function (prePointerInfo, pickInfo, pointerEvent) {
         if (!prePointerInfo.skipOnPointerObservable) {
-            this.utilityLayerScene.onPointerObservable.notifyObservers(new _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerInfo"](prePointerInfo.type, prePointerInfo.event, pickInfo));
+            this.utilityLayerScene.onPointerObservable.notifyObservers(new _Events_pointerEvents__WEBPACK_IMPORTED_MODULE_2__["PointerInfo"](prePointerInfo.type, prePointerInfo.event, pickInfo), prePointerInfo.type);
             this._lastPointerEvents[pointerEvent.pointerId] = true;
         }
     };

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/babylon.max.js.map


+ 233 - 209
dist/preview release/babylon.module.d.ts

@@ -57273,90 +57273,121 @@ declare module "babylonjs/Helpers/environmentHelper" {
         dispose(): void;
     }
 }
-declare module "babylonjs/Helpers/photoDome" {
-    import { Observable } from "babylonjs/Misc/observable";
-    import { Nullable } from "babylonjs/types";
+declare module "babylonjs/Helpers/textureDome" {
     import { Scene } from "babylonjs/scene";
     import { TransformNode } from "babylonjs/Meshes/transformNode";
     import { Mesh } from "babylonjs/Meshes/mesh";
     import { Texture } from "babylonjs/Materials/Textures/texture";
     import { BackgroundMaterial } from "babylonjs/Materials/Background/backgroundMaterial";
     import "babylonjs/Meshes/Builders/sphereBuilder";
+    import { Nullable } from "babylonjs/types";
+    import { Observable } from "babylonjs/Misc/observable";
     /**
-     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
-     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
      * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class PhotoDome extends TransformNode {
+    export abstract class TextureDome<T extends Texture> extends TransformNode {
+        protected onError: Nullable<(message?: string, exception?: any) => void>;
         /**
-         * Define the image as a Monoscopic panoramic 360 image.
+         * Define the source as a Monoscopic panoramic 360/180.
          */
         static readonly MODE_MONOSCOPIC: number;
         /**
-         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
          */
         static readonly MODE_TOPBOTTOM: number;
         /**
-         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Define the source as a Stereoscopic Side by Side panoramic 360/180.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _useDirectMapping;
+        private _halfDome;
+        protected _useDirectMapping: boolean;
         /**
          * The texture being displayed on the sphere
          */
-        protected _photoTexture: Texture;
+        protected _texture: T;
         /**
-         * Gets or sets the texture being displayed on the sphere
+         * Gets the texture being displayed on the sphere
          */
-        get photoTexture(): Texture;
-        set photoTexture(value: Texture);
+        get texture(): T;
         /**
-         * Observable raised when an error occured while loading the 360 image
+         * Sets the texture being displayed on the sphere
          */
-        onLoadErrorObservable: Observable<string>;
+        set texture(newTexture: T);
         /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;
         /**
-         * The surface used for the skybox
+         * The surface used for the dome
          */
         protected _mesh: Mesh;
         /**
-         * Gets the mesh used for the skybox.
+         * Gets the mesh used for the dome.
          */
         get mesh(): Mesh;
         /**
+         * A mesh that will be used to mask the back of the dome in case it is a 180 degree movie.
+         */
+        private _halfDomeMask;
+        /**
          * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
          * Also see the options.resolution property.
          */
         get fovMultiplier(): number;
         set fovMultiplier(value: number);
-        private _imageMode;
+        protected _textureMode: number;
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-         * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-         * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Gets or set the current texture mode for the texture. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
-        get imageMode(): number;
-        set imageMode(value: number);
+        get textureMode(): number;
+        /**
+         * Sets the current texture mode for the texture. It can be:
+          * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set textureMode(value: number);
+        /**
+         * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
+         */
+        get halfDome(): boolean;
+        /**
+         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+         */
+        set halfDome(enabled: boolean);
+        /**
+         * Oberserver used in Stereoscopic VR Mode.
+         */
+        private _onBeforeCameraRenderObserver;
         /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
+         * Observable raised when an error occured while loading the 360 image
+         */
+        onLoadErrorObservable: Observable<string>;
+        /**
+         * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
          * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOfPhoto defines the url of the photo to display
-         * @param options defines an object containing optional or exposed sub element properties
-         * @param onError defines a callback called when an error occured while loading the texture
+         * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
+         * @param options An object containing optional or exposed sub element properties
          */
-        constructor(name: string, urlOfPhoto: string, options: {
+        constructor(name: string, textureUrlOrElement: string | string[] | HTMLVideoElement, options: {
             resolution?: number;
+            clickToPlay?: boolean;
+            autoPlay?: boolean;
+            loop?: boolean;
             size?: number;
-            useDirectMapping?: boolean;
+            poster?: string;
             faceForward?: boolean;
+            useDirectMapping?: boolean;
+            halfDomeMode?: boolean;
         }, scene: Scene, onError?: Nullable<(message?: string, exception?: any) => void>);
-        private _onBeforeCameraRenderObserver;
-        private _changeImageMode;
+        protected abstract _initTexture(urlsOrElement: string | string[] | HTMLElement, scene: Scene, options: any): T;
+        protected _changeTextureMode(value: number): void;
         /**
          * Releases resources associated with this node.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -57365,6 +57396,54 @@ declare module "babylonjs/Helpers/photoDome" {
         dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
     }
 }
+declare module "babylonjs/Helpers/photoDome" {
+    import { Scene } from "babylonjs/scene";
+    import { Texture } from "babylonjs/Materials/Textures/texture";
+    import { TextureDome } from "babylonjs/Helpers/textureDome";
+    /**
+     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
+     * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+     */
+    export class PhotoDome extends TextureDome<Texture> {
+        /**
+         * Define the image as a Monoscopic panoramic 360 image.
+         */
+        static readonly MODE_MONOSCOPIC: number;
+        /**
+         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         */
+        static readonly MODE_TOPBOTTOM: number;
+        /**
+         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         */
+        static readonly MODE_SIDEBYSIDE: number;
+        /**
+         * Gets or sets the texture being displayed on the sphere
+         */
+        get photoTexture(): Texture;
+        /**
+         * sets the texture being displayed on the sphere
+         */
+        set photoTexture(value: Texture);
+        /**
+         * Gets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        get imageMode(): number;
+        /**
+         * Sets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set imageMode(value: number);
+        protected _initTexture(urlsOrElement: string, scene: Scene, options: any): Texture;
+    }
+}
 declare module "babylonjs/Misc/rgbdTextureTools" {
     import "babylonjs/Shaders/rgbdDecode.fragment";
     import "babylonjs/Engines/Extensions/engine.renderTarget";
@@ -60240,18 +60319,15 @@ declare module "babylonjs/Helpers/sceneHelpers" {
 }
 declare module "babylonjs/Helpers/videoDome" {
     import { Scene } from "babylonjs/scene";
-    import { TransformNode } from "babylonjs/Meshes/transformNode";
-    import { Mesh } from "babylonjs/Meshes/mesh";
     import { VideoTexture } from "babylonjs/Materials/Textures/videoTexture";
-    import { BackgroundMaterial } from "babylonjs/Materials/Background/backgroundMaterial";
-    import "babylonjs/Meshes/Builders/sphereBuilder";
+    import { TextureDome } from "babylonjs/Helpers/textureDome";
     /**
      * Display a 360/180 degree video on an approximately spherical surface, useful for VR applications or skyboxes.
      * As a subclass of TransformNode, this allow parenting to the camera or multiple videos with different locations in the scene.
      * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class VideoDome extends TransformNode {
+    export class VideoDome extends TextureDome<VideoTexture> {
         /**
          * Define the video source as a Monoscopic panoramic 360 video.
          */
@@ -60264,84 +60340,20 @@ declare module "babylonjs/Helpers/videoDome" {
          * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _halfDome;
-        private _useDirectMapping;
-        /**
-         * The video texture being displayed on the sphere
-         */
-        protected _videoTexture: VideoTexture;
         /**
-         * Gets the video texture being displayed on the sphere
+         * Get the video texture associated with this video dome
          */
         get videoTexture(): VideoTexture;
         /**
-         * The skybox material
-         */
-        protected _material: BackgroundMaterial;
-        /**
-         * The surface used for the video dome
-         */
-        protected _mesh: Mesh;
-        /**
-         * Gets the mesh used for the video dome.
-         */
-        get mesh(): Mesh;
-        /**
-         * A mesh that will be used to mask the back of the video dome in case it is a 180 degree movie.
-         */
-        private _halfDomeMask;
-        /**
-         * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-         * Also see the options.resolution property.
-         */
-        get fovMultiplier(): number;
-        set fovMultiplier(value: number);
-        private _videoMode;
-        /**
-         * Gets or set the current video mode for the video. It can be:
-         * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-         * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-         * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+         * Get the video mode of this dome
          */
         get videoMode(): number;
-        set videoMode(value: number);
         /**
-         * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-         *
-         */
-        get halfDome(): boolean;
-        /**
-         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
-         */
-        set halfDome(enabled: boolean);
-        /**
-         * Oberserver used in Stereoscopic VR Mode.
+         * Set the video mode of this dome.
+         * @see textureMode
          */
-        private _onBeforeCameraRenderObserver;
-        /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
-         * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOrVideo defines the url(s) or the video element to use
-         * @param options An object containing optional or exposed sub element properties
-         */
-        constructor(name: string, urlsOrVideo: string | string[] | HTMLVideoElement, options: {
-            resolution?: number;
-            clickToPlay?: boolean;
-            autoPlay?: boolean;
-            loop?: boolean;
-            size?: number;
-            poster?: string;
-            faceForward?: boolean;
-            useDirectMapping?: boolean;
-            halfDomeMode?: boolean;
-        }, scene: Scene);
-        private _changeVideoMode;
-        /**
-         * Releases resources associated with this node.
-         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
-         */
-        dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
+        set videoMode(value: number);
+        protected _initTexture(urlsOrElement: string | string[] | HTMLVideoElement, scene: Scene, options: any): VideoTexture;
     }
 }
 declare module "babylonjs/Helpers/index" {
@@ -134488,80 +134500,111 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
-     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
-     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
      * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class PhotoDome extends TransformNode {
+    export abstract class TextureDome<T extends Texture> extends TransformNode {
+        protected onError: Nullable<(message?: string, exception?: any) => void>;
         /**
-         * Define the image as a Monoscopic panoramic 360 image.
+         * Define the source as a Monoscopic panoramic 360/180.
          */
         static readonly MODE_MONOSCOPIC: number;
         /**
-         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
          */
         static readonly MODE_TOPBOTTOM: number;
         /**
-         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Define the source as a Stereoscopic Side by Side panoramic 360/180.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _useDirectMapping;
+        private _halfDome;
+        protected _useDirectMapping: boolean;
         /**
          * The texture being displayed on the sphere
          */
-        protected _photoTexture: Texture;
+        protected _texture: T;
         /**
-         * Gets or sets the texture being displayed on the sphere
+         * Gets the texture being displayed on the sphere
          */
-        get photoTexture(): Texture;
-        set photoTexture(value: Texture);
+        get texture(): T;
         /**
-         * Observable raised when an error occured while loading the 360 image
+         * Sets the texture being displayed on the sphere
          */
-        onLoadErrorObservable: Observable<string>;
+        set texture(newTexture: T);
         /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;
         /**
-         * The surface used for the skybox
+         * The surface used for the dome
          */
         protected _mesh: Mesh;
         /**
-         * Gets the mesh used for the skybox.
+         * Gets the mesh used for the dome.
          */
         get mesh(): Mesh;
         /**
+         * A mesh that will be used to mask the back of the dome in case it is a 180 degree movie.
+         */
+        private _halfDomeMask;
+        /**
          * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
          * Also see the options.resolution property.
          */
         get fovMultiplier(): number;
         set fovMultiplier(value: number);
-        private _imageMode;
+        protected _textureMode: number;
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-         * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-         * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Gets or set the current texture mode for the texture. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
-        get imageMode(): number;
-        set imageMode(value: number);
+        get textureMode(): number;
+        /**
+         * Sets the current texture mode for the texture. It can be:
+          * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set textureMode(value: number);
+        /**
+         * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
+         */
+        get halfDome(): boolean;
+        /**
+         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+         */
+        set halfDome(enabled: boolean);
+        /**
+         * Oberserver used in Stereoscopic VR Mode.
+         */
+        private _onBeforeCameraRenderObserver;
         /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
+         * Observable raised when an error occured while loading the 360 image
+         */
+        onLoadErrorObservable: Observable<string>;
+        /**
+         * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
          * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOfPhoto defines the url of the photo to display
-         * @param options defines an object containing optional or exposed sub element properties
-         * @param onError defines a callback called when an error occured while loading the texture
+         * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
+         * @param options An object containing optional or exposed sub element properties
          */
-        constructor(name: string, urlOfPhoto: string, options: {
+        constructor(name: string, textureUrlOrElement: string | string[] | HTMLVideoElement, options: {
             resolution?: number;
+            clickToPlay?: boolean;
+            autoPlay?: boolean;
+            loop?: boolean;
             size?: number;
-            useDirectMapping?: boolean;
+            poster?: string;
             faceForward?: boolean;
+            useDirectMapping?: boolean;
+            halfDomeMode?: boolean;
         }, scene: Scene, onError?: Nullable<(message?: string, exception?: any) => void>);
-        private _onBeforeCameraRenderObserver;
-        private _changeImageMode;
+        protected abstract _initTexture(urlsOrElement: string | string[] | HTMLElement, scene: Scene, options: any): T;
+        protected _changeTextureMode(value: number): void;
         /**
          * Releases resources associated with this node.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -134572,6 +134615,51 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
+     * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+     */
+    export class PhotoDome extends TextureDome<Texture> {
+        /**
+         * Define the image as a Monoscopic panoramic 360 image.
+         */
+        static readonly MODE_MONOSCOPIC: number;
+        /**
+         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         */
+        static readonly MODE_TOPBOTTOM: number;
+        /**
+         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         */
+        static readonly MODE_SIDEBYSIDE: number;
+        /**
+         * Gets or sets the texture being displayed on the sphere
+         */
+        get photoTexture(): Texture;
+        /**
+         * sets the texture being displayed on the sphere
+         */
+        set photoTexture(value: Texture);
+        /**
+         * Gets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        get imageMode(): number;
+        /**
+         * Sets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set imageMode(value: number);
+        protected _initTexture(urlsOrElement: string, scene: Scene, options: any): Texture;
+    }
+}
+declare module BABYLON {
+    /**
      * Class used to host RGBD texture specific utilities
      */
     export class RGBDTextureTools {
@@ -137272,7 +137360,7 @@ declare module BABYLON {
      * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class VideoDome extends TransformNode {
+    export class VideoDome extends TextureDome<VideoTexture> {
         /**
          * Define the video source as a Monoscopic panoramic 360 video.
          */
@@ -137285,84 +137373,20 @@ declare module BABYLON {
          * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _halfDome;
-        private _useDirectMapping;
-        /**
-         * The video texture being displayed on the sphere
-         */
-        protected _videoTexture: VideoTexture;
         /**
-         * Gets the video texture being displayed on the sphere
+         * Get the video texture associated with this video dome
          */
         get videoTexture(): VideoTexture;
         /**
-         * The skybox material
-         */
-        protected _material: BackgroundMaterial;
-        /**
-         * The surface used for the video dome
-         */
-        protected _mesh: Mesh;
-        /**
-         * Gets the mesh used for the video dome.
-         */
-        get mesh(): Mesh;
-        /**
-         * A mesh that will be used to mask the back of the video dome in case it is a 180 degree movie.
-         */
-        private _halfDomeMask;
-        /**
-         * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-         * Also see the options.resolution property.
-         */
-        get fovMultiplier(): number;
-        set fovMultiplier(value: number);
-        private _videoMode;
-        /**
-         * Gets or set the current video mode for the video. It can be:
-         * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-         * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-         * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+         * Get the video mode of this dome
          */
         get videoMode(): number;
-        set videoMode(value: number);
         /**
-         * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-         *
-         */
-        get halfDome(): boolean;
-        /**
-         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
-         */
-        set halfDome(enabled: boolean);
-        /**
-         * Oberserver used in Stereoscopic VR Mode.
-         */
-        private _onBeforeCameraRenderObserver;
-        /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
-         * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOrVideo defines the url(s) or the video element to use
-         * @param options An object containing optional or exposed sub element properties
+         * Set the video mode of this dome.
+         * @see textureMode
          */
-        constructor(name: string, urlsOrVideo: string | string[] | HTMLVideoElement, options: {
-            resolution?: number;
-            clickToPlay?: boolean;
-            autoPlay?: boolean;
-            loop?: boolean;
-            size?: number;
-            poster?: string;
-            faceForward?: boolean;
-            useDirectMapping?: boolean;
-            halfDomeMode?: boolean;
-        }, scene: Scene);
-        private _changeVideoMode;
-        /**
-         * Releases resources associated with this node.
-         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
-         */
-        dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
+        set videoMode(value: number);
+        protected _initTexture(urlsOrElement: string | string[] | HTMLVideoElement, scene: Scene, options: any): VideoTexture;
     }
 }
 declare module BABYLON {

+ 113 - 101
dist/preview release/documentation.d.ts

@@ -54878,80 +54878,111 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
-     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
-     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
      * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class PhotoDome extends TransformNode {
+    export abstract class TextureDome<T extends Texture> extends TransformNode {
+        protected onError: Nullable<(message?: string, exception?: any) => void>;
         /**
-         * Define the image as a Monoscopic panoramic 360 image.
+         * Define the source as a Monoscopic panoramic 360/180.
          */
         static readonly MODE_MONOSCOPIC: number;
         /**
-         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
          */
         static readonly MODE_TOPBOTTOM: number;
         /**
-         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Define the source as a Stereoscopic Side by Side panoramic 360/180.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _useDirectMapping;
+        private _halfDome;
+        protected _useDirectMapping: boolean;
         /**
          * The texture being displayed on the sphere
          */
-        protected _photoTexture: Texture;
+        protected _texture: T;
         /**
-         * Gets or sets the texture being displayed on the sphere
+         * Gets the texture being displayed on the sphere
          */
-        get photoTexture(): Texture;
-        set photoTexture(value: Texture);
+        get texture(): T;
         /**
-         * Observable raised when an error occured while loading the 360 image
+         * Sets the texture being displayed on the sphere
          */
-        onLoadErrorObservable: Observable<string>;
+        set texture(newTexture: T);
         /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;
         /**
-         * The surface used for the skybox
+         * The surface used for the dome
          */
         protected _mesh: Mesh;
         /**
-         * Gets the mesh used for the skybox.
+         * Gets the mesh used for the dome.
          */
         get mesh(): Mesh;
         /**
+         * A mesh that will be used to mask the back of the dome in case it is a 180 degree movie.
+         */
+        private _halfDomeMask;
+        /**
          * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
          * Also see the options.resolution property.
          */
         get fovMultiplier(): number;
         set fovMultiplier(value: number);
-        private _imageMode;
+        protected _textureMode: number;
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-         * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-         * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Gets or set the current texture mode for the texture. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
-        get imageMode(): number;
-        set imageMode(value: number);
+        get textureMode(): number;
+        /**
+         * Sets the current texture mode for the texture. It can be:
+          * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set textureMode(value: number);
+        /**
+         * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
+         */
+        get halfDome(): boolean;
+        /**
+         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+         */
+        set halfDome(enabled: boolean);
+        /**
+         * Oberserver used in Stereoscopic VR Mode.
+         */
+        private _onBeforeCameraRenderObserver;
+        /**
+         * Observable raised when an error occured while loading the 360 image
+         */
+        onLoadErrorObservable: Observable<string>;
         /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
+         * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
          * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOfPhoto defines the url of the photo to display
-         * @param options defines an object containing optional or exposed sub element properties
-         * @param onError defines a callback called when an error occured while loading the texture
+         * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
+         * @param options An object containing optional or exposed sub element properties
          */
-        constructor(name: string, urlOfPhoto: string, options: {
+        constructor(name: string, textureUrlOrElement: string | string[] | HTMLVideoElement, options: {
             resolution?: number;
+            clickToPlay?: boolean;
+            autoPlay?: boolean;
+            loop?: boolean;
             size?: number;
-            useDirectMapping?: boolean;
+            poster?: string;
             faceForward?: boolean;
+            useDirectMapping?: boolean;
+            halfDomeMode?: boolean;
         }, scene: Scene, onError?: Nullable<(message?: string, exception?: any) => void>);
-        private _onBeforeCameraRenderObserver;
-        private _changeImageMode;
+        protected abstract _initTexture(urlsOrElement: string | string[] | HTMLElement, scene: Scene, options: any): T;
+        protected _changeTextureMode(value: number): void;
         /**
          * Releases resources associated with this node.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -54962,6 +54993,51 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
+     * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+     */
+    export class PhotoDome extends TextureDome<Texture> {
+        /**
+         * Define the image as a Monoscopic panoramic 360 image.
+         */
+        static readonly MODE_MONOSCOPIC: number;
+        /**
+         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         */
+        static readonly MODE_TOPBOTTOM: number;
+        /**
+         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         */
+        static readonly MODE_SIDEBYSIDE: number;
+        /**
+         * Gets or sets the texture being displayed on the sphere
+         */
+        get photoTexture(): Texture;
+        /**
+         * sets the texture being displayed on the sphere
+         */
+        set photoTexture(value: Texture);
+        /**
+         * Gets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        get imageMode(): number;
+        /**
+         * Sets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set imageMode(value: number);
+        protected _initTexture(urlsOrElement: string, scene: Scene, options: any): Texture;
+    }
+}
+declare module BABYLON {
+    /**
      * Class used to host RGBD texture specific utilities
      */
     export class RGBDTextureTools {
@@ -57662,7 +57738,7 @@ declare module BABYLON {
      * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class VideoDome extends TransformNode {
+    export class VideoDome extends TextureDome<VideoTexture> {
         /**
          * Define the video source as a Monoscopic panoramic 360 video.
          */
@@ -57675,84 +57751,20 @@ declare module BABYLON {
          * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _halfDome;
-        private _useDirectMapping;
         /**
-         * The video texture being displayed on the sphere
-         */
-        protected _videoTexture: VideoTexture;
-        /**
-         * Gets the video texture being displayed on the sphere
+         * Get the video texture associated with this video dome
          */
         get videoTexture(): VideoTexture;
         /**
-         * The skybox material
-         */
-        protected _material: BackgroundMaterial;
-        /**
-         * The surface used for the video dome
-         */
-        protected _mesh: Mesh;
-        /**
-         * Gets the mesh used for the video dome.
-         */
-        get mesh(): Mesh;
-        /**
-         * A mesh that will be used to mask the back of the video dome in case it is a 180 degree movie.
-         */
-        private _halfDomeMask;
-        /**
-         * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-         * Also see the options.resolution property.
-         */
-        get fovMultiplier(): number;
-        set fovMultiplier(value: number);
-        private _videoMode;
-        /**
-         * Gets or set the current video mode for the video. It can be:
-         * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-         * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-         * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+         * Get the video mode of this dome
          */
         get videoMode(): number;
-        set videoMode(value: number);
-        /**
-         * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-         *
-         */
-        get halfDome(): boolean;
-        /**
-         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
-         */
-        set halfDome(enabled: boolean);
-        /**
-         * Oberserver used in Stereoscopic VR Mode.
-         */
-        private _onBeforeCameraRenderObserver;
-        /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
-         * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOrVideo defines the url(s) or the video element to use
-         * @param options An object containing optional or exposed sub element properties
-         */
-        constructor(name: string, urlsOrVideo: string | string[] | HTMLVideoElement, options: {
-            resolution?: number;
-            clickToPlay?: boolean;
-            autoPlay?: boolean;
-            loop?: boolean;
-            size?: number;
-            poster?: string;
-            faceForward?: boolean;
-            useDirectMapping?: boolean;
-            halfDomeMode?: boolean;
-        }, scene: Scene);
-        private _changeVideoMode;
         /**
-         * Releases resources associated with this node.
-         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
+         * Set the video mode of this dome.
+         * @see textureMode
          */
-        dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
+        set videoMode(value: number);
+        protected _initTexture(urlsOrElement: string | string[] | HTMLVideoElement, scene: Scene, options: any): VideoTexture;
     }
 }
 declare module BABYLON {

+ 2 - 0
dist/preview release/gui/babylon.gui.js

@@ -6863,6 +6863,7 @@ var Image = /** @class */ (function (_super) {
         configurable: true
     });
     Image.prototype._onImageLoaded = function () {
+        this._imageDataCache.data = null;
         this._imageWidth = this._domImage.width;
         this._imageHeight = this._domImage.height;
         this._loaded = true;
@@ -7106,6 +7107,7 @@ var Image = /** @class */ (function (_super) {
             var canvas = this._workingCanvas;
             var context_1 = canvas.getContext("2d");
             this._imageDataCache.data = imageData = context_1.getImageData(0, 0, width, height).data;
+            this._imageDataCache.key = key;
         }
         x = (x - this._currentMeasure.left) | 0;
         y = (y - this._currentMeasure.top) | 0;

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


Разлика између датотеке није приказан због своје велике величине
+ 6 - 6
dist/preview release/inspector/babylon.inspector.bundle.js


Разлика између датотеке није приказан због своје велике величине
+ 283 - 312
dist/preview release/inspector/babylon.inspector.bundle.max.js


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


+ 21 - 19
dist/preview release/inspector/babylon.inspector.d.ts

@@ -300,6 +300,7 @@ declare module INSPECTOR {
             value: string;
         }): boolean;
         updateValue(evt: any): void;
+        onBlur(): void;
         render(): JSX.Element;
     }
 }
@@ -1369,7 +1370,7 @@ declare module INSPECTOR {
         channels: IChannel[];
         setChannels(channelState: IChannel[]): void;
     }
-    export class ChannelsBar extends React.Component<IChannelsBarProps> {
+    export class ChannelsBar extends React.PureComponent<IChannelsBarProps> {
         render(): JSX.Element;
     }
 }
@@ -1382,13 +1383,6 @@ declare module INSPECTOR {
         b?: number;
         a?: number;
     }
-    export interface IToolGUI {
-        adt: BABYLON.GUI.AdvancedDynamicTexture;
-        toolWindow: BABYLON.GUI.StackPanel;
-        isDragging: boolean;
-        dragCoords: BABYLON.Nullable<BABYLON.Vector2>;
-        style: BABYLON.GUI.Style;
-    }
     export class TextureCanvasManager {
         private _engine;
         private _scene;
@@ -1429,7 +1423,6 @@ declare module INSPECTOR {
         private static DESELECT_KEY;
         private _tool;
         private _setPixelData;
-        private _GUI;
         private _window;
         private _metadata;
         private _editing3D;
@@ -1457,8 +1450,6 @@ declare module INSPECTOR {
         get tool(): BABYLON.Nullable<ITool>;
         set face(face: number);
         set mipLevel(mipLevel: number);
-        /** Returns the tool GUI object, allowing tools to access the GUI */
-        get GUI(): IToolGUI;
         /** Returns the 3D scene used for postprocesses */
         get scene3D(): BABYLON.Scene;
         set metadata(metadata: IMetadata);
@@ -1487,7 +1478,7 @@ declare module INSPECTOR {
         width: number;
         height: number;
     }
-    export class PropertiesBar extends React.Component<IPropertiesBarProps, IPropertiesBarState> {
+    export class PropertiesBar extends React.PureComponent<IPropertiesBarProps, IPropertiesBarState> {
         private _resetButton;
         private _uploadButton;
         private _saveButton;
@@ -1503,12 +1494,12 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface BottomBarProps {
+    interface IBottomBarProps {
         name: string;
         mipLevel: number;
         hasMips: boolean;
     }
-    export class BottomBar extends React.Component<BottomBarProps> {
+    export class BottomBar extends React.PureComponent<IBottomBarProps> {
         render(): JSX.Element;
     }
 }
@@ -1519,7 +1510,7 @@ declare module INSPECTOR {
         canvas3D: React.RefObject<HTMLCanvasElement>;
         texture: BABYLON.BaseTexture;
     }
-    export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+    export class TextureCanvasComponent extends React.PureComponent<ITextureCanvasComponentProps> {
         shouldComponentUpdate(nextProps: ITextureCanvasComponentProps): boolean;
         render(): JSX.Element;
     }
@@ -1544,8 +1535,15 @@ declare module INSPECTOR {
     export default _default;
 }
 declare module INSPECTOR {
+    interface IToolSettingsProps {
+        tool: ITool | undefined;
+    }
+    export class ToolSettings extends React.Component<IToolSettingsProps> {
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface ITextureEditorComponentProps {
-        globalState: GlobalState;
         texture: BABYLON.BaseTexture;
         url: string;
         window: React.RefObject<PopupComponent>;
@@ -1577,8 +1575,6 @@ declare module INSPECTOR {
         setMetadata: (data: any) => void;
         /** Returns the texture coordinates under the cursor */
         getMouseCoordinates: (pointerInfo: BABYLON.PointerInfo) => BABYLON.Vector2;
-        /** An object which holds the GUI's ADT as well as the tool window. */
-        GUI: IToolGUI;
         /** Provides access to the BABYLON namespace */
         BABYLON: any;
         /** Provides a canvas that you can use the canvas API to paint on. */
@@ -1588,6 +1584,9 @@ declare module INSPECTOR {
         /** Call this when you are finished painting. */
         stopPainting: () => void;
     }
+    export interface IToolGUIProps {
+        instance: IToolType;
+    }
     /** An interface representing the definition of a tool */
     export interface IToolData {
         /** Name to display on the toolbar */
@@ -1600,6 +1599,7 @@ declare module INSPECTOR {
         usesWindow?: boolean;
         /** Whether the tool uses postprocesses */
         is3D?: boolean;
+        settingsComponent?: React.ComponentType<IToolGUIProps>;
     }
     export interface IToolType {
         /** Called when the tool is selected. */
@@ -1666,10 +1666,12 @@ declare module INSPECTOR {
         private _adtInstrumentation;
         private popoutWindowRef;
         private textureLineRef;
+        private _textureInspectorSize;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
         updateTexture(file: File): void;
-        onOpenTextureEditor(): void;
+        openTextureEditor(): void;
+        onOpenTextureEditor(window: Window): void;
         onCloseTextureEditor(window: Window | null, callback?: {
             (): void;
         }): void;

+ 45 - 43
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -340,6 +340,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/numericInputComp
             value: string;
         }): boolean;
         updateValue(evt: any): void;
+        onBlur(): void;
         render(): JSX.Element;
     }
 }
@@ -1547,7 +1548,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         channels: IChannel[];
         setChannels(channelState: IChannel[]): void;
     }
-    export class ChannelsBar extends React.Component<IChannelsBarProps> {
+    export class ChannelsBar extends React.PureComponent<IChannelsBarProps> {
         render(): JSX.Element;
     }
 }
@@ -1560,9 +1561,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     import { PointerInfo } from 'babylonjs/Events/pointerEvents';
     import { ITool } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar";
     import { IChannel } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar";
-    import { StackPanel } from 'babylonjs-gui/2D/controls/stackPanel';
-    import { Style } from 'babylonjs-gui/2D/style';
-    import { AdvancedDynamicTexture } from 'babylonjs-gui/2D/advancedDynamicTexture';
     import { IMetadata } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
     export interface IPixelData {
         x?: number;
@@ -1572,13 +1570,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         b?: number;
         a?: number;
     }
-    export interface IToolGUI {
-        adt: AdvancedDynamicTexture;
-        toolWindow: StackPanel;
-        isDragging: boolean;
-        dragCoords: Nullable<Vector2>;
-        style: Style;
-    }
     export class TextureCanvasManager {
         private _engine;
         private _scene;
@@ -1619,7 +1610,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         private static DESELECT_KEY;
         private _tool;
         private _setPixelData;
-        private _GUI;
         private _window;
         private _metadata;
         private _editing3D;
@@ -1647,8 +1637,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         get tool(): Nullable<ITool>;
         set face(face: number);
         set mipLevel(mipLevel: number);
-        /** Returns the tool GUI object, allowing tools to access the GUI */
-        get GUI(): IToolGUI;
         /** Returns the 3D scene used for postprocesses */
         get scene3D(): Scene;
         set metadata(metadata: IMetadata);
@@ -1680,7 +1668,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         width: number;
         height: number;
     }
-    export class PropertiesBar extends React.Component<IPropertiesBarProps, IPropertiesBarState> {
+    export class PropertiesBar extends React.PureComponent<IPropertiesBarProps, IPropertiesBarState> {
         private _resetButton;
         private _uploadButton;
         private _saveButton;
@@ -1697,12 +1685,12 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/bottomBar" {
     import * as React from 'react';
-    interface BottomBarProps {
+    interface IBottomBarProps {
         name: string;
         mipLevel: number;
         hasMips: boolean;
     }
-    export class BottomBar extends React.Component<BottomBarProps> {
+    export class BottomBar extends React.PureComponent<IBottomBarProps> {
         render(): JSX.Element;
     }
 }
@@ -1715,7 +1703,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         canvas3D: React.RefObject<HTMLCanvasElement>;
         texture: BaseTexture;
     }
-    export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+    export class TextureCanvasComponent extends React.PureComponent<ITextureCanvasComponentProps> {
         shouldComponentUpdate(nextProps: ITextureCanvasComponentProps): boolean;
         render(): JSX.Element;
     }
@@ -1744,10 +1732,19 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     const _default: import("babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent").IToolData[];
     export default _default;
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolSettings" {
+    import * as React from 'react';
+    import { ITool } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar";
+    interface IToolSettingsProps {
+        tool: ITool | undefined;
+    }
+    export class ToolSettings extends React.Component<IToolSettingsProps> {
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent" {
     import * as React from 'react';
-    import { GlobalState } from "babylonjs-inspector/components/globalState";
-    import { IPixelData, IToolGUI } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager";
+    import { IPixelData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager";
     import { ITool } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar";
     import { IChannel } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar";
     import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
@@ -1757,7 +1754,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     import { PointerInfo } from 'babylonjs/Events/pointerEvents';
     import { PopupComponent } from "babylonjs-inspector/components/popupComponent";
     interface ITextureEditorComponentProps {
-        globalState: GlobalState;
         texture: BaseTexture;
         url: string;
         window: React.RefObject<PopupComponent>;
@@ -1789,8 +1785,6 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         setMetadata: (data: any) => void;
         /** Returns the texture coordinates under the cursor */
         getMouseCoordinates: (pointerInfo: PointerInfo) => Vector2;
-        /** An object which holds the GUI's ADT as well as the tool window. */
-        GUI: IToolGUI;
         /** Provides access to the BABYLON namespace */
         BABYLON: any;
         /** Provides a canvas that you can use the canvas API to paint on. */
@@ -1800,6 +1794,9 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         /** Call this when you are finished painting. */
         stopPainting: () => void;
     }
+    export interface IToolGUIProps {
+        instance: IToolType;
+    }
     /** An interface representing the definition of a tool */
     export interface IToolData {
         /** Name to display on the toolbar */
@@ -1812,6 +1809,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         usesWindow?: boolean;
         /** Whether the tool uses postprocesses */
         is3D?: boolean;
+        settingsComponent?: React.ComponentType<IToolGUIProps>;
     }
     export interface IToolType {
         /** Called when the tool is selected. */
@@ -1885,10 +1883,12 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         private _adtInstrumentation;
         private popoutWindowRef;
         private textureLineRef;
+        private _textureInspectorSize;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
         updateTexture(file: File): void;
-        onOpenTextureEditor(): void;
+        openTextureEditor(): void;
+        onOpenTextureEditor(window: Window): void;
         onCloseTextureEditor(window: Window | null, callback?: {
             (): void;
         }): void;
@@ -4461,6 +4461,7 @@ declare module INSPECTOR {
             value: string;
         }): boolean;
         updateValue(evt: any): void;
+        onBlur(): void;
         render(): JSX.Element;
     }
 }
@@ -5530,7 +5531,7 @@ declare module INSPECTOR {
         channels: IChannel[];
         setChannels(channelState: IChannel[]): void;
     }
-    export class ChannelsBar extends React.Component<IChannelsBarProps> {
+    export class ChannelsBar extends React.PureComponent<IChannelsBarProps> {
         render(): JSX.Element;
     }
 }
@@ -5543,13 +5544,6 @@ declare module INSPECTOR {
         b?: number;
         a?: number;
     }
-    export interface IToolGUI {
-        adt: BABYLON.GUI.AdvancedDynamicTexture;
-        toolWindow: BABYLON.GUI.StackPanel;
-        isDragging: boolean;
-        dragCoords: BABYLON.Nullable<BABYLON.Vector2>;
-        style: BABYLON.GUI.Style;
-    }
     export class TextureCanvasManager {
         private _engine;
         private _scene;
@@ -5590,7 +5584,6 @@ declare module INSPECTOR {
         private static DESELECT_KEY;
         private _tool;
         private _setPixelData;
-        private _GUI;
         private _window;
         private _metadata;
         private _editing3D;
@@ -5618,8 +5611,6 @@ declare module INSPECTOR {
         get tool(): BABYLON.Nullable<ITool>;
         set face(face: number);
         set mipLevel(mipLevel: number);
-        /** Returns the tool GUI object, allowing tools to access the GUI */
-        get GUI(): IToolGUI;
         /** Returns the 3D scene used for postprocesses */
         get scene3D(): BABYLON.Scene;
         set metadata(metadata: IMetadata);
@@ -5648,7 +5639,7 @@ declare module INSPECTOR {
         width: number;
         height: number;
     }
-    export class PropertiesBar extends React.Component<IPropertiesBarProps, IPropertiesBarState> {
+    export class PropertiesBar extends React.PureComponent<IPropertiesBarProps, IPropertiesBarState> {
         private _resetButton;
         private _uploadButton;
         private _saveButton;
@@ -5664,12 +5655,12 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface BottomBarProps {
+    interface IBottomBarProps {
         name: string;
         mipLevel: number;
         hasMips: boolean;
     }
-    export class BottomBar extends React.Component<BottomBarProps> {
+    export class BottomBar extends React.PureComponent<IBottomBarProps> {
         render(): JSX.Element;
     }
 }
@@ -5680,7 +5671,7 @@ declare module INSPECTOR {
         canvas3D: React.RefObject<HTMLCanvasElement>;
         texture: BABYLON.BaseTexture;
     }
-    export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+    export class TextureCanvasComponent extends React.PureComponent<ITextureCanvasComponentProps> {
         shouldComponentUpdate(nextProps: ITextureCanvasComponentProps): boolean;
         render(): JSX.Element;
     }
@@ -5705,8 +5696,15 @@ declare module INSPECTOR {
     export default _default;
 }
 declare module INSPECTOR {
+    interface IToolSettingsProps {
+        tool: ITool | undefined;
+    }
+    export class ToolSettings extends React.Component<IToolSettingsProps> {
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface ITextureEditorComponentProps {
-        globalState: GlobalState;
         texture: BABYLON.BaseTexture;
         url: string;
         window: React.RefObject<PopupComponent>;
@@ -5738,8 +5736,6 @@ declare module INSPECTOR {
         setMetadata: (data: any) => void;
         /** Returns the texture coordinates under the cursor */
         getMouseCoordinates: (pointerInfo: BABYLON.PointerInfo) => BABYLON.Vector2;
-        /** An object which holds the GUI's ADT as well as the tool window. */
-        GUI: IToolGUI;
         /** Provides access to the BABYLON namespace */
         BABYLON: any;
         /** Provides a canvas that you can use the canvas API to paint on. */
@@ -5749,6 +5745,9 @@ declare module INSPECTOR {
         /** Call this when you are finished painting. */
         stopPainting: () => void;
     }
+    export interface IToolGUIProps {
+        instance: IToolType;
+    }
     /** An interface representing the definition of a tool */
     export interface IToolData {
         /** Name to display on the toolbar */
@@ -5761,6 +5760,7 @@ declare module INSPECTOR {
         usesWindow?: boolean;
         /** Whether the tool uses postprocesses */
         is3D?: boolean;
+        settingsComponent?: React.ComponentType<IToolGUIProps>;
     }
     export interface IToolType {
         /** Called when the tool is selected. */
@@ -5827,10 +5827,12 @@ declare module INSPECTOR {
         private _adtInstrumentation;
         private popoutWindowRef;
         private textureLineRef;
+        private _textureInspectorSize;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
         updateTexture(file: File): void;
-        onOpenTextureEditor(): void;
+        openTextureEditor(): void;
+        onOpenTextureEditor(window: Window): void;
         onCloseTextureEditor(window: Window | null, callback?: {
             (): void;
         }): void;

+ 233 - 209
dist/preview release/viewer/babylon.module.d.ts

@@ -57273,90 +57273,121 @@ declare module "babylonjs/Helpers/environmentHelper" {
         dispose(): void;
     }
 }
-declare module "babylonjs/Helpers/photoDome" {
-    import { Observable } from "babylonjs/Misc/observable";
-    import { Nullable } from "babylonjs/types";
+declare module "babylonjs/Helpers/textureDome" {
     import { Scene } from "babylonjs/scene";
     import { TransformNode } from "babylonjs/Meshes/transformNode";
     import { Mesh } from "babylonjs/Meshes/mesh";
     import { Texture } from "babylonjs/Materials/Textures/texture";
     import { BackgroundMaterial } from "babylonjs/Materials/Background/backgroundMaterial";
     import "babylonjs/Meshes/Builders/sphereBuilder";
+    import { Nullable } from "babylonjs/types";
+    import { Observable } from "babylonjs/Misc/observable";
     /**
-     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
-     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
      * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class PhotoDome extends TransformNode {
+    export abstract class TextureDome<T extends Texture> extends TransformNode {
+        protected onError: Nullable<(message?: string, exception?: any) => void>;
         /**
-         * Define the image as a Monoscopic panoramic 360 image.
+         * Define the source as a Monoscopic panoramic 360/180.
          */
         static readonly MODE_MONOSCOPIC: number;
         /**
-         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
          */
         static readonly MODE_TOPBOTTOM: number;
         /**
-         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Define the source as a Stereoscopic Side by Side panoramic 360/180.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _useDirectMapping;
+        private _halfDome;
+        protected _useDirectMapping: boolean;
         /**
          * The texture being displayed on the sphere
          */
-        protected _photoTexture: Texture;
+        protected _texture: T;
         /**
-         * Gets or sets the texture being displayed on the sphere
+         * Gets the texture being displayed on the sphere
          */
-        get photoTexture(): Texture;
-        set photoTexture(value: Texture);
+        get texture(): T;
         /**
-         * Observable raised when an error occured while loading the 360 image
+         * Sets the texture being displayed on the sphere
          */
-        onLoadErrorObservable: Observable<string>;
+        set texture(newTexture: T);
         /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;
         /**
-         * The surface used for the skybox
+         * The surface used for the dome
          */
         protected _mesh: Mesh;
         /**
-         * Gets the mesh used for the skybox.
+         * Gets the mesh used for the dome.
          */
         get mesh(): Mesh;
         /**
+         * A mesh that will be used to mask the back of the dome in case it is a 180 degree movie.
+         */
+        private _halfDomeMask;
+        /**
          * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
          * Also see the options.resolution property.
          */
         get fovMultiplier(): number;
         set fovMultiplier(value: number);
-        private _imageMode;
+        protected _textureMode: number;
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-         * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-         * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Gets or set the current texture mode for the texture. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
-        get imageMode(): number;
-        set imageMode(value: number);
+        get textureMode(): number;
+        /**
+         * Sets the current texture mode for the texture. It can be:
+          * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set textureMode(value: number);
+        /**
+         * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
+         */
+        get halfDome(): boolean;
+        /**
+         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+         */
+        set halfDome(enabled: boolean);
+        /**
+         * Oberserver used in Stereoscopic VR Mode.
+         */
+        private _onBeforeCameraRenderObserver;
         /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
+         * Observable raised when an error occured while loading the 360 image
+         */
+        onLoadErrorObservable: Observable<string>;
+        /**
+         * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
          * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOfPhoto defines the url of the photo to display
-         * @param options defines an object containing optional or exposed sub element properties
-         * @param onError defines a callback called when an error occured while loading the texture
+         * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
+         * @param options An object containing optional or exposed sub element properties
          */
-        constructor(name: string, urlOfPhoto: string, options: {
+        constructor(name: string, textureUrlOrElement: string | string[] | HTMLVideoElement, options: {
             resolution?: number;
+            clickToPlay?: boolean;
+            autoPlay?: boolean;
+            loop?: boolean;
             size?: number;
-            useDirectMapping?: boolean;
+            poster?: string;
             faceForward?: boolean;
+            useDirectMapping?: boolean;
+            halfDomeMode?: boolean;
         }, scene: Scene, onError?: Nullable<(message?: string, exception?: any) => void>);
-        private _onBeforeCameraRenderObserver;
-        private _changeImageMode;
+        protected abstract _initTexture(urlsOrElement: string | string[] | HTMLElement, scene: Scene, options: any): T;
+        protected _changeTextureMode(value: number): void;
         /**
          * Releases resources associated with this node.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -57365,6 +57396,54 @@ declare module "babylonjs/Helpers/photoDome" {
         dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
     }
 }
+declare module "babylonjs/Helpers/photoDome" {
+    import { Scene } from "babylonjs/scene";
+    import { Texture } from "babylonjs/Materials/Textures/texture";
+    import { TextureDome } from "babylonjs/Helpers/textureDome";
+    /**
+     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
+     * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+     */
+    export class PhotoDome extends TextureDome<Texture> {
+        /**
+         * Define the image as a Monoscopic panoramic 360 image.
+         */
+        static readonly MODE_MONOSCOPIC: number;
+        /**
+         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         */
+        static readonly MODE_TOPBOTTOM: number;
+        /**
+         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         */
+        static readonly MODE_SIDEBYSIDE: number;
+        /**
+         * Gets or sets the texture being displayed on the sphere
+         */
+        get photoTexture(): Texture;
+        /**
+         * sets the texture being displayed on the sphere
+         */
+        set photoTexture(value: Texture);
+        /**
+         * Gets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        get imageMode(): number;
+        /**
+         * Sets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set imageMode(value: number);
+        protected _initTexture(urlsOrElement: string, scene: Scene, options: any): Texture;
+    }
+}
 declare module "babylonjs/Misc/rgbdTextureTools" {
     import "babylonjs/Shaders/rgbdDecode.fragment";
     import "babylonjs/Engines/Extensions/engine.renderTarget";
@@ -60240,18 +60319,15 @@ declare module "babylonjs/Helpers/sceneHelpers" {
 }
 declare module "babylonjs/Helpers/videoDome" {
     import { Scene } from "babylonjs/scene";
-    import { TransformNode } from "babylonjs/Meshes/transformNode";
-    import { Mesh } from "babylonjs/Meshes/mesh";
     import { VideoTexture } from "babylonjs/Materials/Textures/videoTexture";
-    import { BackgroundMaterial } from "babylonjs/Materials/Background/backgroundMaterial";
-    import "babylonjs/Meshes/Builders/sphereBuilder";
+    import { TextureDome } from "babylonjs/Helpers/textureDome";
     /**
      * Display a 360/180 degree video on an approximately spherical surface, useful for VR applications or skyboxes.
      * As a subclass of TransformNode, this allow parenting to the camera or multiple videos with different locations in the scene.
      * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class VideoDome extends TransformNode {
+    export class VideoDome extends TextureDome<VideoTexture> {
         /**
          * Define the video source as a Monoscopic panoramic 360 video.
          */
@@ -60264,84 +60340,20 @@ declare module "babylonjs/Helpers/videoDome" {
          * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _halfDome;
-        private _useDirectMapping;
-        /**
-         * The video texture being displayed on the sphere
-         */
-        protected _videoTexture: VideoTexture;
         /**
-         * Gets the video texture being displayed on the sphere
+         * Get the video texture associated with this video dome
          */
         get videoTexture(): VideoTexture;
         /**
-         * The skybox material
-         */
-        protected _material: BackgroundMaterial;
-        /**
-         * The surface used for the video dome
-         */
-        protected _mesh: Mesh;
-        /**
-         * Gets the mesh used for the video dome.
-         */
-        get mesh(): Mesh;
-        /**
-         * A mesh that will be used to mask the back of the video dome in case it is a 180 degree movie.
-         */
-        private _halfDomeMask;
-        /**
-         * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-         * Also see the options.resolution property.
-         */
-        get fovMultiplier(): number;
-        set fovMultiplier(value: number);
-        private _videoMode;
-        /**
-         * Gets or set the current video mode for the video. It can be:
-         * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-         * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-         * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+         * Get the video mode of this dome
          */
         get videoMode(): number;
-        set videoMode(value: number);
         /**
-         * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-         *
-         */
-        get halfDome(): boolean;
-        /**
-         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
-         */
-        set halfDome(enabled: boolean);
-        /**
-         * Oberserver used in Stereoscopic VR Mode.
+         * Set the video mode of this dome.
+         * @see textureMode
          */
-        private _onBeforeCameraRenderObserver;
-        /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
-         * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOrVideo defines the url(s) or the video element to use
-         * @param options An object containing optional or exposed sub element properties
-         */
-        constructor(name: string, urlsOrVideo: string | string[] | HTMLVideoElement, options: {
-            resolution?: number;
-            clickToPlay?: boolean;
-            autoPlay?: boolean;
-            loop?: boolean;
-            size?: number;
-            poster?: string;
-            faceForward?: boolean;
-            useDirectMapping?: boolean;
-            halfDomeMode?: boolean;
-        }, scene: Scene);
-        private _changeVideoMode;
-        /**
-         * Releases resources associated with this node.
-         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
-         */
-        dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
+        set videoMode(value: number);
+        protected _initTexture(urlsOrElement: string | string[] | HTMLVideoElement, scene: Scene, options: any): VideoTexture;
     }
 }
 declare module "babylonjs/Helpers/index" {
@@ -134488,80 +134500,111 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
-     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
-     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
      * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class PhotoDome extends TransformNode {
+    export abstract class TextureDome<T extends Texture> extends TransformNode {
+        protected onError: Nullable<(message?: string, exception?: any) => void>;
         /**
-         * Define the image as a Monoscopic panoramic 360 image.
+         * Define the source as a Monoscopic panoramic 360/180.
          */
         static readonly MODE_MONOSCOPIC: number;
         /**
-         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
          */
         static readonly MODE_TOPBOTTOM: number;
         /**
-         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Define the source as a Stereoscopic Side by Side panoramic 360/180.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _useDirectMapping;
+        private _halfDome;
+        protected _useDirectMapping: boolean;
         /**
          * The texture being displayed on the sphere
          */
-        protected _photoTexture: Texture;
+        protected _texture: T;
         /**
-         * Gets or sets the texture being displayed on the sphere
+         * Gets the texture being displayed on the sphere
          */
-        get photoTexture(): Texture;
-        set photoTexture(value: Texture);
+        get texture(): T;
         /**
-         * Observable raised when an error occured while loading the 360 image
+         * Sets the texture being displayed on the sphere
          */
-        onLoadErrorObservable: Observable<string>;
+        set texture(newTexture: T);
         /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;
         /**
-         * The surface used for the skybox
+         * The surface used for the dome
          */
         protected _mesh: Mesh;
         /**
-         * Gets the mesh used for the skybox.
+         * Gets the mesh used for the dome.
          */
         get mesh(): Mesh;
         /**
+         * A mesh that will be used to mask the back of the dome in case it is a 180 degree movie.
+         */
+        private _halfDomeMask;
+        /**
          * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
          * Also see the options.resolution property.
          */
         get fovMultiplier(): number;
         set fovMultiplier(value: number);
-        private _imageMode;
+        protected _textureMode: number;
         /**
-         * Gets or set the current video mode for the video. It can be:
-         * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-         * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-         * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         * Gets or set the current texture mode for the texture. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
          */
-        get imageMode(): number;
-        set imageMode(value: number);
+        get textureMode(): number;
+        /**
+         * Sets the current texture mode for the texture. It can be:
+          * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set textureMode(value: number);
+        /**
+         * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
+         */
+        get halfDome(): boolean;
+        /**
+         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+         */
+        set halfDome(enabled: boolean);
+        /**
+         * Oberserver used in Stereoscopic VR Mode.
+         */
+        private _onBeforeCameraRenderObserver;
         /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
+         * Observable raised when an error occured while loading the 360 image
+         */
+        onLoadErrorObservable: Observable<string>;
+        /**
+         * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
          * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOfPhoto defines the url of the photo to display
-         * @param options defines an object containing optional or exposed sub element properties
-         * @param onError defines a callback called when an error occured while loading the texture
+         * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
+         * @param options An object containing optional or exposed sub element properties
          */
-        constructor(name: string, urlOfPhoto: string, options: {
+        constructor(name: string, textureUrlOrElement: string | string[] | HTMLVideoElement, options: {
             resolution?: number;
+            clickToPlay?: boolean;
+            autoPlay?: boolean;
+            loop?: boolean;
             size?: number;
-            useDirectMapping?: boolean;
+            poster?: string;
             faceForward?: boolean;
+            useDirectMapping?: boolean;
+            halfDomeMode?: boolean;
         }, scene: Scene, onError?: Nullable<(message?: string, exception?: any) => void>);
-        private _onBeforeCameraRenderObserver;
-        private _changeImageMode;
+        protected abstract _initTexture(urlsOrElement: string | string[] | HTMLElement, scene: Scene, options: any): T;
+        protected _changeTextureMode(value: number): void;
         /**
          * Releases resources associated with this node.
          * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
@@ -134572,6 +134615,51 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
+     * As a subclass of TransformNode, this allow parenting to the camera with different locations in the scene.
+     * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
+     * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+     */
+    export class PhotoDome extends TextureDome<Texture> {
+        /**
+         * Define the image as a Monoscopic panoramic 360 image.
+         */
+        static readonly MODE_MONOSCOPIC: number;
+        /**
+         * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
+         */
+        static readonly MODE_TOPBOTTOM: number;
+        /**
+         * Define the image as a Stereoscopic Side by Side panoramic 360 image.
+         */
+        static readonly MODE_SIDEBYSIDE: number;
+        /**
+         * Gets or sets the texture being displayed on the sphere
+         */
+        get photoTexture(): Texture;
+        /**
+         * sets the texture being displayed on the sphere
+         */
+        set photoTexture(value: Texture);
+        /**
+         * Gets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        get imageMode(): number;
+        /**
+         * Sets the current video mode for the video. It can be:
+         * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+         * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+         * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+         */
+        set imageMode(value: number);
+        protected _initTexture(urlsOrElement: string, scene: Scene, options: any): Texture;
+    }
+}
+declare module BABYLON {
+    /**
      * Class used to host RGBD texture specific utilities
      */
     export class RGBDTextureTools {
@@ -137272,7 +137360,7 @@ declare module BABYLON {
      * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
      * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
      */
-    export class VideoDome extends TransformNode {
+    export class VideoDome extends TextureDome<VideoTexture> {
         /**
          * Define the video source as a Monoscopic panoramic 360 video.
          */
@@ -137285,84 +137373,20 @@ declare module BABYLON {
          * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
          */
         static readonly MODE_SIDEBYSIDE: number;
-        private _halfDome;
-        private _useDirectMapping;
-        /**
-         * The video texture being displayed on the sphere
-         */
-        protected _videoTexture: VideoTexture;
         /**
-         * Gets the video texture being displayed on the sphere
+         * Get the video texture associated with this video dome
          */
         get videoTexture(): VideoTexture;
         /**
-         * The skybox material
-         */
-        protected _material: BackgroundMaterial;
-        /**
-         * The surface used for the video dome
-         */
-        protected _mesh: Mesh;
-        /**
-         * Gets the mesh used for the video dome.
-         */
-        get mesh(): Mesh;
-        /**
-         * A mesh that will be used to mask the back of the video dome in case it is a 180 degree movie.
-         */
-        private _halfDomeMask;
-        /**
-         * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-         * Also see the options.resolution property.
-         */
-        get fovMultiplier(): number;
-        set fovMultiplier(value: number);
-        private _videoMode;
-        /**
-         * Gets or set the current video mode for the video. It can be:
-         * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-         * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-         * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+         * Get the video mode of this dome
          */
         get videoMode(): number;
-        set videoMode(value: number);
         /**
-         * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-         *
-         */
-        get halfDome(): boolean;
-        /**
-         * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
-         */
-        set halfDome(enabled: boolean);
-        /**
-         * Oberserver used in Stereoscopic VR Mode.
-         */
-        private _onBeforeCameraRenderObserver;
-        /**
-         * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
-         * @param name Element's name, child elements will append suffixes for their own names.
-         * @param urlsOrVideo defines the url(s) or the video element to use
-         * @param options An object containing optional or exposed sub element properties
+         * Set the video mode of this dome.
+         * @see textureMode
          */
-        constructor(name: string, urlsOrVideo: string | string[] | HTMLVideoElement, options: {
-            resolution?: number;
-            clickToPlay?: boolean;
-            autoPlay?: boolean;
-            loop?: boolean;
-            size?: number;
-            poster?: string;
-            faceForward?: boolean;
-            useDirectMapping?: boolean;
-            halfDomeMode?: boolean;
-        }, scene: Scene);
-        private _changeVideoMode;
-        /**
-         * Releases resources associated with this node.
-         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
-         */
-        dispose(doNotRecurse?: boolean, disposeMaterialAndTextures?: boolean): void;
+        set videoMode(value: number);
+        protected _initTexture(urlsOrElement: string | string[] | HTMLVideoElement, scene: Scene, options: any): VideoTexture;
     }
 }
 declare module BABYLON {

Разлика између датотеке није приказан због своје велике величине
+ 72 - 68
dist/preview release/viewer/babylon.viewer.js


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -18,7 +18,7 @@
 ### General
 
 - Added support for postproces serialization ([Deltakosh](https://github.com/deltakosh))
-- Added support for querystrings on KTX file URLs ([abogartz](https://github.com/abogartz)
+- Added support for querystrings on KTX file URLs ([abogartz](https://github.com/abogartz))
 - Refactored React refs from old string API to React.createRef() API ([belfortk](https://github.com/belfortk))
 - Scale on one axis for `BoundingBoxGizmo` ([cedricguillemet](https://github.com/cedricguillemet))
 - Camera gizmo ([cedricguillemet](https://github.com/cedricguillemet))
@@ -42,6 +42,7 @@
 - Allow 180 monoscopic videos on top of the video dome ([#8575](https://github.com/BabylonJS/Babylon.js/issues/8575)) ([RaananW](https://github.com/RaananW))
 - Added `AssetContainerTask` support to `AssetsManager` class ([MackeyK24](https://github.com/MackeyK24))
 - Changed DeviceSourceManager getInput contract to no longer return nullable values ([Drigax](https://github.com/drigax))
+- Photo Dome and Video Dome now use the same abstract class and support the same parameters ([#8771](https://github.com/BabylonJS/Babylon.js/issues/8771)) ([RaananW](https://github.com/RaananW))
 
 ### Engine
 
@@ -83,7 +84,7 @@
 - Popup Window available (To be used in Curve Editor) ([pixelspace](https://github.com/devpixelspace))
 - Add support to update inspector when switching to a new scene ([belfortk](https://github.com/belfortk))
 - Hex Component for Hex inputs on layer masks. ([msDestiny14](https://github.com/msDestiny14))
-- View & edit textures in pop out inspector using canvas and postprocesses. Supports region selection and individual channel editing. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
+- View & edit textures in pop out inspector using 5 tools. Supports region selection and individual channel editing. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
 
 ### Cameras
 
@@ -129,6 +130,7 @@
 
 - Added support for KHR_materials_unlit to glTF serializer ([Popov72](https://github.com/Popov72))
 - Added support for glTF Skins to glTF serializer ([Drigax](https://github.com/Drigax))
+- Added support for glTF Morph Target serialization ([Drigax](https://github.com/Drigax))
 
 ### Navigation
 

+ 2 - 0
gui/src/2D/controls/image.ts

@@ -384,6 +384,7 @@ export class Image extends Control {
     }
 
     private _onImageLoaded(): void {
+        this._imageDataCache.data = null;
         this._imageWidth = this._domImage.width;
         this._imageHeight = this._domImage.height;
         this._loaded = true;
@@ -652,6 +653,7 @@ export class Image extends Control {
             const context = canvas.getContext("2d")!;
 
             this._imageDataCache.data = imageData = context.getImageData(0, 0, width, height).data;
+            this._imageDataCache.key = key;
         }
 
         x = (x - this._currentMeasure.left) | 0;

+ 13 - 2
inspector/src/components/actionTabs/lines/numericInputComponent.tsx

@@ -23,7 +23,6 @@ export class NumericInputComponent extends React.Component<INumericInputComponen
 
     shouldComponentUpdate(nextProps: INumericInputComponentProps, nextState: { value: string }) {
         if (this._localChange) {
-            this._localChange = false;
             return true;
         }
 
@@ -53,6 +52,18 @@ export class NumericInputComponent extends React.Component<INumericInputComponen
         this.props.onChange(valueAsNumber);
     }
 
+    onBlur() {
+        this._localChange = false;
+        let valueAsNumber = parseFloat(this.state.value);
+
+        if (isNaN(valueAsNumber)) {
+            this.props.onChange(this.props.value);
+            return;
+        }
+
+        this.props.onChange(valueAsNumber);
+    }
+
     render() {
         return (
             <div className="numeric">
@@ -62,7 +73,7 @@ export class NumericInputComponent extends React.Component<INumericInputComponen
                         {`${this.props.label}: `}
                     </div>
                 }
-                <input type="number" step={this.props.step} className="numeric-input" value={this.state.value} onChange={evt => this.updateValue(evt)} />
+                <input type="number" step={this.props.step} className="numeric-input" value={this.state.value} onChange={evt => this.updateValue(evt)} onBlur={() => this.onBlur()}/>
             </div>
         )
     }

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

@@ -48,6 +48,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
     private popoutWindowRef : React.RefObject<PopupComponent>;
     private textureLineRef: React.RefObject<TextureLineComponent>;
 
+    private _textureInspectorSize = {width: 1024, height: 490};
+
 
     constructor(props: ITexturePropertyGridComponentProps) {
         super(props);
@@ -70,6 +72,9 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         this._adtInstrumentation = new AdvancedDynamicTextureInstrumentation(adt);
         this._adtInstrumentation!.captureRenderTime = true;
         this._adtInstrumentation!.captureLayoutTime = true;
+        
+        this.onOpenTextureEditor.bind(this);
+        this.onCloseTextureEditor.bind(this);
     }
 
     componentWillUnmount() {
@@ -106,9 +111,9 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         }, undefined, true);
     }
 
-    onOpenTextureEditor() {
+    openTextureEditor() {
         if (this.state.isTextureEditorOpen && this.state.textureEditing !== this.props.texture) {
-            this.onCloseTextureEditor(null, () => this.onOpenTextureEditor());
+            this.onCloseTextureEditor(null, () => this.openTextureEditor());
             return;
         }
         this.setState({
@@ -116,6 +121,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
             textureEditing: this.props.texture
         });
     }
+
+    onOpenTextureEditor(window: Window) {}
     
     onCloseTextureEditor(window: Window | null, callback?: {() : void}) {
         this.setState({
@@ -169,7 +176,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                 <LineContainerComponent globalState={this.props.globalState} title="PREVIEW">
                     <TextureLineComponent ref={this.textureLineRef} texture={texture} width={256} height={256} globalState={this.props.globalState} />
                     <FileButtonLineComponent label="Load texture from file" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
-                    <ButtonLineComponent label="Edit" onClick={() => this.onOpenTextureEditor()} />
+                    <ButtonLineComponent label="Edit" onClick={() => this.openTextureEditor()} />
                     <TextInputLineComponent label="URL" value={textureUrl} lockObject={this.props.lockObject} onChange={url => {
                         (texture as Texture).updateURL(url);
                         this.forceRefresh();
@@ -179,15 +186,12 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                 <PopupComponent
                   id='texture-editor'
                   title='Texture Inspector'
-                  size={{ width: 1024, height: 490 }}
-                  onOpen={(window: Window) => {}}
-                  onClose={(window: Window) =>
-                    this.onCloseTextureEditor(window)
-                  }
+                  size={this._textureInspectorSize}
+                  onOpen={this.onOpenTextureEditor}
+                  onClose={this.onCloseTextureEditor}
                   ref={this.popoutWindowRef}
                 >
                     <TextureEditorComponent
-                        globalState={this.props.globalState}
                         texture={this.props.texture}
                         url={textureUrl}
                         window={this.popoutWindowRef}

+ 2 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/bottomBar.tsx

@@ -1,12 +1,12 @@
 import * as React from 'react';
 
-interface BottomBarProps {
+interface IBottomBarProps {
     name: string;
     mipLevel: number;
     hasMips: boolean;
 }
 
-export class BottomBar extends React.Component<BottomBarProps> {
+export class BottomBar extends React.PureComponent<IBottomBarProps> {
     render() {
         return <div id='bottom-bar'>
             <span id='file-url'>{this.props.name}</span>

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar.tsx

@@ -16,7 +16,7 @@ interface IChannelsBarProps {
 const eyeOpen = require('./assets/eyeOpen.svg');
 const eyeClosed = require('./assets/eyeClosed.svg');
 
-export class ChannelsBar extends React.Component<IChannelsBarProps> {
+export class ChannelsBar extends React.PureComponent<IChannelsBarProps> {
     render() {
         return <div id='channels-bar'>
             {this.props.channels.map(

+ 0 - 108
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.ts

@@ -1,108 +0,0 @@
-import { IToolData, IToolParameters, IToolType } from '../textureEditorComponent';
-import { TextBlock } from 'babylonjs-gui/2D/controls/textBlock';
-import { Slider } from 'babylonjs-gui/2D/controls/sliders/slider';
-
-export const Contrast : IToolData = {
-    name: 'Contrast/Exposure',
-    type: class implements IToolType {
-        getParameters: () => IToolParameters;
-        contrast : number = 1.0;
-        exposure : number = 1.0;
-        GUI: {
-            contrastLabel : TextBlock;
-            contrastSlider : Slider;
-            exposureLabel : TextBlock;
-            exposureSlider : Slider;
-        }
-        constructor(getParameters: () => IToolParameters) {
-            this.getParameters = getParameters;
-        }
-        setExposure(exposure : number) {
-            this.exposure = exposure;
-            this.GUI.exposureLabel.text = `Exposure: ${this.exposure}`;
-            const {scene3D, updateTexture} = this.getParameters();
-            scene3D.imageProcessingConfiguration.isEnabled = true;
-            scene3D.imageProcessingConfiguration.exposure = this.computeExposure(this.exposure);
-            updateTexture();
-        }
-        setContrast(contrast : number) {
-            this.contrast = contrast;
-            this.GUI.contrastLabel.text = `Contrast: ${this.contrast}`;
-            const {scene3D, updateTexture} = this.getParameters();
-            scene3D.imageProcessingConfiguration.isEnabled = true;
-            scene3D.imageProcessingConfiguration.contrast = this.computeContrast(contrast);
-            updateTexture();
-        }
-        /** Maps slider values to post processing values using an exponential regression */
-        computeExposure(sliderValue : number) {
-            if (sliderValue <= 0) {
-                return 1 - (-sliderValue / 100);
-            } else {
-                return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
-            }
-        }
-        /** Maps slider values to post processing values using an exponential regression */
-        computeContrast(sliderValue : number) {
-            if (sliderValue <= 0) {
-                return 1 - (-sliderValue / 100);
-            } else {
-                return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
-            }
-        }
-        setup() {
-            this.contrast = 0;
-            this.exposure = 0;
-            const {GUI} = this.getParameters();
-            const contrastLabel = new TextBlock();
-            contrastLabel.style = GUI.style;
-            contrastLabel.height = '20px';
-            contrastLabel.color = '#ffffff';
-            const contrastSlider = new Slider();
-            contrastSlider.value = this.contrast;
-            contrastSlider.minimum = -100;
-            contrastSlider.maximum = 100;
-            contrastSlider.height = '20px';
-            contrastSlider.isThumbCircle = true;
-            contrastSlider.background = '#a3a3a3';
-            contrastSlider.color = '#33648f';
-            contrastSlider.borderColor = '#33648f';
-            contrastSlider.onValueChangedObservable.add(evt => this.setContrast(evt.valueOf()));
-            const exposureLabel = new TextBlock();
-            exposureLabel.style = GUI.style;
-            exposureLabel.height = '20px';
-            exposureLabel.color = '#ffffff';
-            const exposureSlider = new Slider();
-            exposureSlider.value = this.exposure;
-            exposureSlider.minimum = -100;
-            exposureSlider.maximum = 100;
-            exposureSlider.height = '20px';
-            exposureSlider.isThumbCircle = true;
-            exposureSlider.background = '#a3a3a3';
-            exposureSlider.color = '#33648f';
-            exposureSlider.borderColor = '#33648f';
-            exposureSlider.onValueChangedObservable.add(evt => this.setExposure(evt.valueOf()));
-            GUI.toolWindow.addControl(contrastLabel);
-            GUI.toolWindow.addControl(contrastSlider);
-            GUI.toolWindow.addControl(exposureLabel);
-            GUI.toolWindow.addControl(exposureSlider);
-            this.GUI = {contrastLabel, contrastSlider, exposureLabel, exposureSlider};
-            this.setExposure(this.exposure);
-            this.setContrast(this.contrast);
-        }
-        cleanup() {
-            Object.entries(this.GUI).forEach(([key, value]) => value.dispose());
-        }
-        onReset() {
-            this.GUI.contrastSlider.value = 0;
-            this.GUI.exposureSlider.value = 0;
-        }
-    },
-    usesWindow: true,
-    is3D: true,
-    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0i
-    NDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTcuNTUsMjYuNTVsOC41OS0zLjIxQTYuODYsNi44NiwwLDAsMSwyNCwyNS43NWwtMy4x
-    OSwxLjE5QTcsNywwLDAsMSwxNy41NSwyNi41NVpNMjAsMTEuNUE4LjUsOC41LDAsMSwwLDI4LjUsMjAsOC41MSw4LjUxLDAsMCwwLDIwLDExLjVNMjAsMTBBMTAsMTAs
-    MCwxLDEsMTAsMjAsMTAsMTAsMCwwLDEsMjAsMTBabS0yLjQ1LDUuMzQsNS0xLjg2QTcsNywwLDAsMCwxOS40NCwxM2wtMS44OS43MVptMCwzLjIsNy44OC0yLjk0YTYu
-    ODgsNi44OCwwLDAsMC0xLjE5LTEuMTZsLTYuNjksMi41Wm0wLDMuMiw5LjIzLTMuNDRhNy42OCw3LjY4LDAsMCwwLS41Mi0xLjQxbC04LjcxLDMuMjVabTAsMS42djEu
-    Nmw5LjI4LTMuNDZBNi42Nyw2LjY3LDAsMCwwLDI3LDE5LjgyWiIgc3R5bGU9ImZpbGw6I2ZmZiIvPjwvc3ZnPg==`
-}

+ 99 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.tsx

@@ -0,0 +1,99 @@
+import * as React from 'react';
+import { IToolData, IToolParameters, IToolType, IToolGUIProps } from '../textureEditorComponent';
+
+class contrastTool implements IToolType {
+    getParameters: () => IToolParameters;
+    contrast : number = 1.0;
+    exposure : number = 1.0;
+    constructor(getParameters: () => IToolParameters) {
+        this.getParameters = getParameters;
+    }
+    setExposure(exposure : number) {
+        this.exposure = exposure;
+        const {scene3D, updateTexture} = this.getParameters();
+        scene3D.imageProcessingConfiguration.isEnabled = true;
+        scene3D.imageProcessingConfiguration.exposure = this.computeExposure(this.exposure);
+        updateTexture();
+        
+    }
+    setContrast(contrast : number) {
+        this.contrast = contrast;
+        const {scene3D, updateTexture} = this.getParameters();
+        scene3D.imageProcessingConfiguration.isEnabled = true;
+        scene3D.imageProcessingConfiguration.contrast = this.computeContrast(contrast);
+        updateTexture();
+    }
+    /** Maps slider values to post processing values using an exponential regression */
+    computeExposure(sliderValue : number) {
+        if (sliderValue <= 0) {
+            return 1 - (-sliderValue / 100);
+        } else {
+            return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
+        }
+    }
+    /** Maps slider values to post processing values using an exponential regression */
+    computeContrast(sliderValue : number) {
+        if (sliderValue <= 0) {
+            return 1 - (-sliderValue / 100);
+        } else {
+            return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
+        }
+    }
+    setup() {
+        this.contrast = 0;
+        this.exposure = 0;
+        this.setExposure(this.exposure);
+        this.setContrast(this.contrast);
+    }
+    cleanup() {
+    }
+    onReset() {
+        this.contrast = 0;
+        this.exposure = 0;
+    }
+};
+
+class Settings extends React.Component<IToolGUIProps> {
+    render() {
+        const instance = this.props.instance as contrastTool;
+        return (
+            <div>
+                <div>
+                <label className='tool-slider-input'>
+                    <span>Contrast: {instance.contrast}</span>
+                    <input id='contrast-slider'
+                        type='range'
+                        min={-100}
+                        max={100}
+                        value={instance.contrast}
+                        onChange={evt => {instance.setContrast(evt.target.valueAsNumber); this.forceUpdate();}}/>
+                </label>
+                </div>
+                <div>
+                <label className='tool-slider-input'>
+                    <span>Exposure: {instance.exposure}</span>
+                    <input
+                        type='range'
+                        min={-100}
+                        max={100}
+                        value={instance.exposure}
+                        onChange={evt => {instance.setExposure(evt.target.valueAsNumber); this.forceUpdate();}}/>
+                </label>
+                </div>
+            </div>
+        )
+    }
+}
+
+export const Contrast : IToolData = {
+    name: 'Contrast/Exposure',
+    type: contrastTool,
+    is3D: true,
+    settingsComponent: Settings,
+    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0i
+    NDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTcuNTUsMjYuNTVsOC41OS0zLjIxQTYuODYsNi44NiwwLDAsMSwyNCwyNS43NWwtMy4x
+    OSwxLjE5QTcsNywwLDAsMSwxNy41NSwyNi41NVpNMjAsMTEuNUE4LjUsOC41LDAsMSwwLDI4LjUsMjAsOC41MSw4LjUxLDAsMCwwLDIwLDExLjVNMjAsMTBBMTAsMTAs
+    MCwxLDEsMTAsMjAsMTAsMTAsMCwwLDEsMjAsMTBabS0yLjQ1LDUuMzQsNS0xLjg2QTcsNywwLDAsMCwxOS40NCwxM2wtMS44OS43MVptMCwzLjIsNy44OC0yLjk0YTYu
+    ODgsNi44OCwwLDAsMC0xLjE5LTEuMTZsLTYuNjksMi41Wm0wLDMuMiw5LjIzLTMuNDRhNy42OCw3LjY4LDAsMCwwLS41Mi0xLjQxbC04LjcxLDMuMjVabTAsMS42djEu
+    Nmw5LjI4LTMuNDZBNi42Nyw2LjY3LDAsMCwwLDI3LDE5LjgyWiIgc3R5bGU9ImZpbGw6I2ZmZiIvPjwvc3ZnPg==`
+}

Разлика између датотеке није приказан због своје велике величине
+ 0 - 149
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.ts


Разлика између датотеке није приказан због своје велике величине
+ 144 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.tsx


+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar.tsx

@@ -25,7 +25,7 @@ interface IPixelDataProps {
     data: number | undefined;
 }
 
-export class PropertiesBar extends React.Component<IPropertiesBarProps,IPropertiesBarState> {
+export class PropertiesBar extends React.PureComponent<IPropertiesBarProps,IPropertiesBarState> {
     private _resetButton = require('./assets/reset.svg');
     private _uploadButton = require('./assets/upload.svg');
     private _saveButton = require('./assets/save.svg');

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent.tsx

@@ -8,7 +8,7 @@ interface ITextureCanvasComponentProps {
     texture : BaseTexture;
 }
 
-export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+export class TextureCanvasComponent extends React.PureComponent<ITextureCanvasComponentProps> {
     shouldComponentUpdate(nextProps : ITextureCanvasComponentProps) {
         return (nextProps.texture !== this.props.texture);
     }

+ 0 - 75
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts

@@ -28,12 +28,6 @@ import { TextureHelper } from '../../../../../../textureHelper';
 
 import { ITool } from './toolBar';
 import { IChannel } from './channelsBar';
-import { TextBlock } from 'babylonjs-gui/2D/controls/textBlock';
-import { Rectangle } from 'babylonjs-gui/2D/controls/rectangle';
-import { StackPanel } from 'babylonjs-gui/2D/controls/stackPanel';
-import { Control } from 'babylonjs-gui/2D/controls/control';
-import { Style } from 'babylonjs-gui/2D/style';
-import { AdvancedDynamicTexture } from 'babylonjs-gui/2D/advancedDynamicTexture';
 import { IMetadata } from './textureEditorComponent';
 
 
@@ -46,14 +40,6 @@ export interface IPixelData {
     a? : number;
 }
 
-export interface IToolGUI {
-    adt: AdvancedDynamicTexture;
-    toolWindow: StackPanel;
-    isDragging: boolean;
-    dragCoords: Nullable<Vector2>;
-    style: Style;
-}
-
 export class TextureCanvasManager {
     private _engine: Engine;
     private _scene: Scene;
@@ -118,8 +104,6 @@ export class TextureCanvasManager {
 
     private _setPixelData : (pixelData : IPixelData) => void;
 
-    private _GUI : IToolGUI;
-
     private _window : Window;
 
     private _metadata : IMetadata;
@@ -314,53 +298,6 @@ export class TextureCanvasManager {
         this._planeMaterial.setInt('time', 0);
         this._plane.material = this._planeMaterial;
         
-        const adt = AdvancedDynamicTexture.CreateFullscreenUI('gui', true, this._scene);
-        const style = adt.createStyle();
-        style.fontFamily = 'acumin-pro-condensed';
-        style.fontSize = '15px';
-
-        const toolWindow = new StackPanel();
-        toolWindow.background = '#333333';
-        toolWindow.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT;
-        toolWindow.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM;
-        toolWindow.left = '0px';
-        toolWindow.top = '-30px';
-        toolWindow.width = '200px';
-        toolWindow.isVisible = false;
-        toolWindow.isPointerBlocker = true;
-        adt.addControl(toolWindow);
-
-        this._GUI = {adt, style, toolWindow, isDragging: false, dragCoords: null};
-
-        const topBar = new Rectangle();
-        topBar.width = '100%';
-        topBar.height = '20px';
-        topBar.background = '#666666';
-        topBar.thickness = 0;
-        topBar.hoverCursor = 'grab';
-        topBar.onPointerDownObservable.add(() => {this._GUI.isDragging = true; topBar.hoverCursor = 'grabbing';});
-        topBar.onPointerUpObservable.add(() => {this._GUI.isDragging = false; this._GUI.dragCoords = null; topBar.hoverCursor = 'grab';});
-
-        const title = new TextBlock();
-        title.text = 'Tool Settings';
-        title.color = 'white';
-        title.height = '20px';
-        title.style = this._GUI.style;
-        topBar.addControl(title);
-        this._GUI.toolWindow.addControl(topBar);
-
-        this._window.addEventListener('pointermove', evt => {
-            if (!this._GUI.isDragging) return;
-            if (!this._GUI.dragCoords) {
-                this._GUI.dragCoords = new Vector2(evt.x, evt.y);
-                return;
-            }
-            this._GUI.toolWindow.leftInPixels += evt.x - this._GUI.dragCoords.x;
-            this._GUI.toolWindow.topInPixels += evt.y - this._GUI.dragCoords.y;
-            this._GUI.dragCoords.x = evt.x;
-            this._GUI.dragCoords.y = evt.y;
-        });
-
         this._window.addEventListener('keydown', evt => {
             this._keyMap[evt.code] = true;
             if (evt.code === TextureCanvasManager.SELECT_ALL_KEY && evt.ctrlKey) {
@@ -392,8 +329,6 @@ export class TextureCanvasManager {
 
         this._engine.runRenderLoop(() => {
             this._engine.resize();
-            this.GUI.toolWindow.left = Math.min(Math.max(this._GUI.toolWindow.leftInPixels, -this._UICanvas.width + this._GUI.toolWindow.widthInPixels), 0);
-            this.GUI.toolWindow.top = Math.min(Math.max(this._GUI.toolWindow.topInPixels, -this._UICanvas.height + this._GUI.toolWindow.heightInPixels), 0);
             this._scene.render();
             this._planeMaterial.setInt('time', new Date().getTime());
         });
@@ -667,11 +602,6 @@ export class TextureCanvasManager {
         this._tool = tool;
         if (this._tool) {
             this._tool.instance.setup();
-            if (this._tool.usesWindow) {
-                this._GUI.toolWindow.isVisible = true;
-            } else {
-                this._GUI.toolWindow.isVisible = false;
-            }
             if (this._editing3D && !this._tool.is3D) {
                 this._editing3D = false;
                 this._2DCanvas.getContext('2d')?.drawImage(this._3DCanvas, 0, 0);
@@ -701,11 +631,6 @@ export class TextureCanvasManager {
         this.grabOriginalTexture(false);
     }
 
-    /** Returns the tool GUI object, allowing tools to access the GUI */
-    public get GUI() {
-        return this._GUI;
-    }
-
     /** Returns the 3D scene used for postprocesses */
     public get scene3D() {
         return this._3DScene;

+ 17 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss

@@ -231,6 +231,23 @@
         height: calc(100% - 70px);
         outline: none;
     }
+
+    #tool-ui {
+        background-color: #333;
+        position: absolute;
+        right: 0;
+        bottom: 30px;
+
+        label {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+        }
+
+        input[type='range'] {
+            background: #d3d3d3;
+        }
+    }
     
     #bottom-bar {
         height: 30px;

+ 8 - 6
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx

@@ -1,6 +1,5 @@
 import * as React from 'react';
-import { GlobalState } from '../../../../../globalState';
-import { TextureCanvasManager, IPixelData, IToolGUI } from './textureCanvasManager';
+import { TextureCanvasManager, IPixelData } from './textureCanvasManager';
 import { ITool, ToolBar } from './toolBar';
 import { PropertiesBar } from './propertiesBar';
 import { IChannel, ChannelsBar } from './channelsBar';
@@ -16,11 +15,11 @@ import { Vector2 } from 'babylonjs/Maths/math.vector';
 import { PointerInfo } from 'babylonjs/Events/pointerEvents';
 
 import { PopupComponent } from '../../../../../popupComponent';
+import { ToolSettings } from './toolSettings';
 
 require('./textureEditor.scss');
 
 interface ITextureEditorComponentProps {
-    globalState: GlobalState;
     texture: BaseTexture;
     url: string;
     window: React.RefObject<PopupComponent>;
@@ -54,8 +53,6 @@ export interface IToolParameters {
     setMetadata: (data : any) => void;
     /** Returns the texture coordinates under the cursor */
     getMouseCoordinates: (pointerInfo : PointerInfo) => Vector2;
-    /** An object which holds the GUI's ADT as well as the tool window. */
-    GUI: IToolGUI;
     /** Provides access to the BABYLON namespace */
     BABYLON: any;
     /** Provides a canvas that you can use the canvas API to paint on. */
@@ -66,6 +63,9 @@ export interface IToolParameters {
     stopPainting: () => void;
 }
 
+export interface IToolGUIProps {
+    instance: IToolType
+}
 
 /** An interface representing the definition of a tool */
 export interface IToolData {
@@ -79,6 +79,7 @@ export interface IToolData {
     usesWindow? : boolean;
     /** Whether the tool uses postprocesses */
     is3D? : boolean;
+    settingsComponent? : React.ComponentType<IToolGUIProps>;
 }
 
 export interface IToolType {
@@ -229,7 +230,6 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
             metadata: this.state.metadata,
             setMetadata: (data : any) => this.setMetadata(data),
             getMouseCoordinates: (pointerInfo : PointerInfo) => this._textureCanvasManager.getMouseCoordinates(pointerInfo),
-            GUI: this._textureCanvasManager.GUI,
             BABYLON: BABYLON,
         };
     }
@@ -271,6 +271,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
     }
 
     render() {
+        const currentTool : ITool | undefined = this.state.tools[this.state.activeToolIndex];
         return <div id="texture-editor">
             <PropertiesBar
                 texture={this.props.texture}
@@ -294,6 +295,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
             />}
             <ChannelsBar channels={this.state.channels} setChannels={(channels) => {this.setState({channels})}}/>
             <TextureCanvasComponent canvas2D={this._2DCanvas} canvas3D={this._3DCanvas} canvasUI={this._UICanvas} texture={this.props.texture}/>
+            <ToolSettings tool={currentTool} />
             <BottomBar name={this.props.url} mipLevel={this.state.mipLevel} hasMips={!this.props.texture.noMipmap}/>
         </div>
     }

+ 15 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/toolSettings.tsx

@@ -0,0 +1,15 @@
+import * as React from 'react';
+import { ITool } from './toolBar';
+
+interface IToolSettingsProps {
+    tool: ITool | undefined;
+}
+
+export class ToolSettings extends React.Component<IToolSettingsProps> {
+    render() {
+        if (!this.props.tool || !this.props.tool.settingsComponent) return <></>;
+        return <div id='tool-ui'>
+            {<this.props.tool.settingsComponent instance={this.props.tool.instance}/>}
+        </div>;
+    }
+}

+ 10 - 0
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -25,6 +25,7 @@ import { SSAO2RenderingPipeline } from 'babylonjs/PostProcesses/RenderPipeline/P
 import { StandardMaterial } from 'babylonjs/Materials/standardMaterial';
 import { PBRMaterial } from 'babylonjs/Materials/PBR/pbrMaterial';
 import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
+import { TargetCamera } from 'babylonjs/Cameras/targetCamera';
 
 require("./sceneExplorer.scss");
 
@@ -320,6 +321,15 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             label: "Add new free camera",
             action: () => {
                 let newFreeCamera = new FreeCamera("free camera", scene.activeCamera ? scene.activeCamera.globalPosition : new Vector3(0, 0, -5), scene);
+
+                if (scene.activeCamera) {
+                    newFreeCamera.minZ = scene.activeCamera.minZ;
+                    newFreeCamera.maxZ = scene.activeCamera.maxZ;
+                    if ((scene.activeCamera as any).getTarget) {
+                        newFreeCamera.setTarget((scene.activeCamera as TargetCamera).getTarget());
+                    }
+                }
+    
                 this.props.globalState.onSelectionChangedObservable.notifyObservers(newFreeCamera);
             }
         });

+ 4 - 1
inspector/src/inspector.ts

@@ -382,7 +382,10 @@ export class Inspector {
             if (options.popup) {
                 this._CreateEmbedHost(scene, options, this._CreatePopup("INSPECTOR", "_EmbedHostWindow"), Inspector.OnSelectionChangeObservable);
             } else {
-                let parentControl = (options.globalRoot ? options.globalRoot : rootElement!.parentElement) as HTMLElement;
+                if (!rootElement) {
+                    return;
+                }
+                let parentControl = (options.globalRoot ? options.globalRoot : rootElement.parentElement) as HTMLElement;
 
                 if (!options.overlay && !this._NewCanvasContainer) {
                     this._CreateCanvasContainer(parentControl);

+ 233 - 35
serializers/src/glTF/2.0/glTFAnimation.ts

@@ -6,6 +6,8 @@ import { Tools } from "babylonjs/Misc/tools";
 import { Animation } from "babylonjs/Animations/animation";
 import { TransformNode } from "babylonjs/Meshes/transformNode";
 import { Scene } from "babylonjs/scene";
+import { MorphTarget } from "babylonjs/Morph/morphTarget";
+import { Mesh } from "babylonjs/Meshes/mesh";
 
 import { _BinaryWriter } from "./glTFExporter";
 import { _GLTFUtilities } from "./glTFUtilities";
@@ -49,7 +51,7 @@ export interface _IAnimationInfo {
     /**
      * The glTF accessor type for the data.
      */
-    dataAccessorType: AccessorType.VEC3 | AccessorType.VEC4;
+    dataAccessorType: AccessorType.VEC3 | AccessorType.VEC4 | AccessorType.SCALAR;
     /**
      * Specifies if quaternions should be used.
      */
@@ -153,6 +155,11 @@ export class _GLTFAnimation {
                 animationChannelTargetPath = AnimationChannelTargetPath.ROTATION;
                 break;
             }
+            case 'influence': {
+                dataAccessorType = AccessorType.SCALAR;
+                animationChannelTargetPath = AnimationChannelTargetPath.WEIGHTS;
+                break;
+            }
             default: {
                 Tools.Error(`Unsupported animatable property ${property[0]}`);
             }
@@ -216,8 +223,83 @@ export class _GLTFAnimation {
     }
 
     /**
+ * @ignore
+ * Create individual morph animations from the mesh's morph target animation tracks
+ * @param babylonNode
+ * @param runtimeGLTFAnimation
+ * @param idleGLTFAnimations
+ * @param nodeMap
+ * @param nodes
+ * @param binaryWriter
+ * @param bufferViews
+ * @param accessors
+ * @param convertToRightHandedSystem
+ * @param animationSampleRate
+ */
+    public static _CreateMorphTargetAnimationFromMorphTargets(babylonNode: Node, runtimeGLTFAnimation: IAnimation, idleGLTFAnimations: IAnimation[], nodeMap: { [key: number]: number }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number) {
+        let glTFAnimation: IAnimation;
+        if (babylonNode instanceof Mesh) {
+            let morphTargetManager = babylonNode.morphTargetManager;
+            if (morphTargetManager) {
+                for (let i = 0; i < morphTargetManager.numTargets; ++i) {
+                    let morphTarget = morphTargetManager.getTarget(i);
+                    for (let j = 0; j < morphTarget.animations.length; ++j) {
+                        let animation = morphTarget.animations[j];
+                        let combinedAnimation = new Animation(`${animation.name}`,
+                            "influence",
+                            animation.framePerSecond,
+                            animation.dataType,
+                            animation.loopMode,
+                            animation.enableBlending);
+                        let combinedAnimationKeys: IAnimationKey[] = [];
+                        let animationKeys = animation.getKeys();
+
+                        for (let k = 0; k < animationKeys.length; ++k) {
+                            let animationKey = animationKeys[k];
+                            for (let l = 0; l < morphTargetManager.numTargets; ++l) {
+                                if (l == i) {
+                                    combinedAnimationKeys.push(animationKey);
+                                } else {
+                                    combinedAnimationKeys.push({ frame: animationKey.frame, value: morphTargetManager.getTarget(l).influence });
+                                }
+                            }
+                        }
+                        combinedAnimation.setKeys(combinedAnimationKeys);
+                        let animationInfo = _GLTFAnimation._DeduceAnimationInfo(combinedAnimation);
+                        if (animationInfo) {
+                            glTFAnimation = {
+                                name: combinedAnimation.name,
+                                samplers: [],
+                                channels: []
+                            };
+                            _GLTFAnimation.AddAnimation(`${animation.name}`,
+                                animation.hasRunningRuntimeAnimations ? runtimeGLTFAnimation : glTFAnimation,
+                                babylonNode,
+                                animation,
+                                animationInfo.dataAccessorType,
+                                animationInfo.animationChannelTargetPath,
+                                nodeMap,
+                                binaryWriter,
+                                bufferViews,
+                                accessors,
+                                convertToRightHandedSystem,
+                                animationInfo.useQuaternion,
+                                animationSampleRate,
+                                morphTargetManager.numTargets
+                            );
+                            if (glTFAnimation.samplers.length && glTFAnimation.channels.length) {
+                                idleGLTFAnimations.push(glTFAnimation);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * @ignore
-     * Create node animations from the animation groups
+     * Create node and morph animations from the animation groups
      * @param babylonScene
      * @param glTFAnimations
      * @param nodeMap
@@ -232,14 +314,18 @@ export class _GLTFAnimation {
         let glTFAnimation: IAnimation;
         if (babylonScene.animationGroups) {
             let animationGroups = babylonScene.animationGroups;
-
             for (let animationGroup of animationGroups) {
+                let morphAnimations: Map<Mesh, Map<MorphTarget, Animation>> = new Map();
+                let sampleAnimations: Map<Mesh, Animation> = new Map();
+                let morphAnimationMeshes: Set<Mesh> = new Set();
+                let animationGroupFrameDiff = animationGroup.to - animationGroup.from;
                 glTFAnimation = {
                     name: animationGroup.name,
                     channels: [],
                     samplers: []
                 };
-                for (let targetAnimation of animationGroup.targetedAnimations) {
+                for (let i = 0; i < animationGroup.targetedAnimations.length; ++i) {
+                    let targetAnimation = animationGroup.targetedAnimations[i];
                     let target = targetAnimation.target;
                     let animation = targetAnimation.animation;
                     if (target instanceof TransformNode || target.length === 1 && target[0] instanceof TransformNode) {
@@ -262,8 +348,90 @@ export class _GLTFAnimation {
                                 animationSampleRate
                             );
                         }
+                    } else if (target instanceof MorphTarget || target.length === 1 && target[0] instanceof MorphTarget) {
+                        let animationInfo = _GLTFAnimation._DeduceAnimationInfo(targetAnimation.animation);
+                        if (animationInfo) {
+                            let babylonMorphTarget = target instanceof MorphTarget ? target as MorphTarget : target[0] as MorphTarget;
+                            if (babylonMorphTarget) {
+                                let babylonMorphTargetManager = babylonScene.morphTargetManagers.find((morphTargetManager) => {
+                                    for (let j = 0; j < morphTargetManager.numTargets; ++j) {
+                                        if (morphTargetManager.getTarget(j) === babylonMorphTarget) {
+                                            return true;
+                                        }
+                                    }
+                                    return false;
+                                });
+                                if (babylonMorphTargetManager) {
+                                    let babylonMesh = <Mesh>babylonScene.meshes.find((mesh) => {
+                                        return (mesh as Mesh).morphTargetManager === babylonMorphTargetManager;
+                                    });
+                                    if (babylonMesh) {
+                                        if (!morphAnimations.has(babylonMesh)) {
+                                            morphAnimations.set(babylonMesh, new Map());
+                                        }
+                                        morphAnimations.get(babylonMesh)?.set(babylonMorphTarget, animation);
+                                        morphAnimationMeshes.add(babylonMesh);
+                                        sampleAnimations.set(babylonMesh, animation);
+                                    }
+                                }
+                            }
+                        }
                     }
                 }
+                morphAnimationMeshes.forEach((mesh) => {
+                    // for each mesh that we want to export a Morph target animation track for...
+                    let morphTargetManager = mesh.morphTargetManager;
+                    let combinedAnimationGroup: Nullable<Animation> = null;
+                    let animationKeys: IAnimationKey[] = [];
+                    let sampleAnimation = sampleAnimations.get(mesh)!;
+                    let numAnimationKeys = sampleAnimation.getKeys().length;
+                    // for each frame of this mesh's animation group track
+                    for (let i = 0; i < numAnimationKeys; ++i) {
+                        // construct a new Animation track by interlacing the frames of each morph target animation track
+                        if (morphTargetManager) {
+                            for (let j = 0; j < morphTargetManager.numTargets; ++j) {
+                                let morphTarget = morphTargetManager.getTarget(j);
+                                let animationsByMorphTarget = morphAnimations.get(mesh);
+                                if (animationsByMorphTarget) {
+                                    let morphTargetAnimation = animationsByMorphTarget.get(morphTarget);
+                                    if (morphTargetAnimation) {
+                                        if (!combinedAnimationGroup) {
+                                            combinedAnimationGroup = new Animation(`${animationGroup.name}_${mesh.name}_MorphWeightAnimation`,
+                                                "influence",
+                                                morphTargetAnimation.framePerSecond,
+                                                Animation.ANIMATIONTYPE_FLOAT,
+                                                morphTargetAnimation.loopMode,
+                                                morphTargetAnimation.enableBlending);
+                                        }
+                                        animationKeys.push(morphTargetAnimation.getKeys()[i]);
+                                    }
+                                    else {
+                                        animationKeys.push({ frame: animationGroup.from + (animationGroupFrameDiff / numAnimationKeys) * i, value: morphTarget.influence });
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    combinedAnimationGroup!.setKeys(animationKeys);
+                    let animationInfo = _GLTFAnimation._DeduceAnimationInfo(combinedAnimationGroup!);
+                    if (animationInfo) {
+                        _GLTFAnimation.AddAnimation(`${animationGroup.name}_${mesh.name}_MorphWeightAnimation`,
+                            glTFAnimation,
+                            mesh,
+                            combinedAnimationGroup!,
+                            animationInfo.dataAccessorType,
+                            animationInfo.animationChannelTargetPath,
+                            nodeMap,
+                            binaryWriter,
+                            bufferViews,
+                            accessors,
+                            false,
+                            animationInfo.useQuaternion,
+                            animationSampleRate,
+                            morphTargetManager?.numTargets
+                        );
+                    }
+                });
                 if (glTFAnimation.channels.length && glTFAnimation.samplers.length) {
                     glTFAnimations.push(glTFAnimation);
                 }
@@ -271,8 +439,10 @@ export class _GLTFAnimation {
         }
     }
 
-    private static AddAnimation(name: string, glTFAnimation: IAnimation, babylonTransformNode: TransformNode, animation: Animation, dataAccessorType: AccessorType, animationChannelTargetPath: AnimationChannelTargetPath, nodeMap: { [key: number]: number }, binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, useQuaternion: boolean, animationSampleRate: number) {
-        let animationData = _GLTFAnimation._CreateNodeAnimation(babylonTransformNode, animation, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion, animationSampleRate);
+    private static AddAnimation(name: string, glTFAnimation: IAnimation, babylonTransformNode: TransformNode, animation: Animation, dataAccessorType: AccessorType, animationChannelTargetPath: AnimationChannelTargetPath, nodeMap: { [key: number]: number }, binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, useQuaternion: boolean, animationSampleRate: number, morphAnimationChannels?: number) {
+        let animationData;
+        animationData = _GLTFAnimation._CreateNodeAnimation(babylonTransformNode, animation, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion, animationSampleRate);
+
         let bufferView: IBufferView;
         let accessor: IAccessor;
         let keyframeAccessorIndex: number;
@@ -282,13 +452,27 @@ export class _GLTFAnimation {
         let animationChannel: IAnimationChannel;
 
         if (animationData) {
+            if (morphAnimationChannels) {
+                let index = 0;
+                let currentInput: number = 0;
+                let newInputs: number[] = [];
+                while (animationData.inputs.length > 0) {
+                    currentInput = animationData.inputs.shift()!;
+                    if (index % morphAnimationChannels == 0) {
+                        newInputs.push(currentInput);
+                    }
+                    index++;
+                }
+                animationData.inputs = newInputs;
+            }
+
             let nodeIndex = nodeMap[babylonTransformNode.uniqueId];
 
             // Creates buffer view and accessor for key frames.
             let byteLength = animationData.inputs.length * 4;
             bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name}  keyframe data view`);
             bufferViews.push(bufferView);
-            animationData.inputs.forEach(function(input) {
+            animationData.inputs.forEach(function (input) {
                 binaryWriter.setFloat32(input);
             });
 
@@ -298,14 +482,14 @@ export class _GLTFAnimation {
 
             // create bufferview and accessor for keyed values.
             outputLength = animationData.outputs.length;
-            byteLength = dataAccessorType === AccessorType.VEC3 ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
+            byteLength = _GLTFUtilities._GetDataAccessorElementCount(dataAccessorType) * 4 * animationData.outputs.length;
 
             // check for in and out tangents
             bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name}  data view`);
             bufferViews.push(bufferView);
 
-            animationData.outputs.forEach(function(output) {
-                output.forEach(function(entry) {
+            animationData.outputs.forEach(function (output) {
+                output.forEach(function (entry) {
                     binaryWriter.setFloat32(entry);
                 });
             });
@@ -451,9 +635,9 @@ export class _GLTFAnimation {
 
     private static _SetInterpolatedValue(babylonTransformNode: TransformNode, value: Nullable<number | Vector3 | Quaternion>, time: number, animation: Animation, animationChannelTargetPath: AnimationChannelTargetPath, quaternionCache: Quaternion, inputs: number[], outputs: number[][], convertToRightHandedSystem: boolean, useQuaternion: boolean) {
         const animationType = animation.dataType;
-        let cacheValue: Vector3 | Quaternion;
+        let cacheValue: Vector3 | Quaternion | number;
         inputs.push(time);
-        if (typeof value === "number") {
+        if (typeof value === "number" && babylonTransformNode instanceof TransformNode) {
             value = this._ConvertFactorToVector3OrQuaternion(value as number, babylonTransformNode, animation, animationType, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion);
         }
         if (value) {
@@ -474,7 +658,10 @@ export class _GLTFAnimation {
                 }
                 outputs.push(quaternionCache.asArray());
             }
-            else {
+            else if (animationChannelTargetPath === AnimationChannelTargetPath.WEIGHTS) {
+                outputs.push([value as number]);
+            }
+            else { // scaling and position animation
                 cacheValue = value as Vector3;
                 if (convertToRightHandedSystem && (animationChannelTargetPath !== AnimationChannelTargetPath.SCALE)) {
                     _GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
@@ -519,7 +706,7 @@ export class _GLTFAnimation {
      * @param useQuaternion Specifies if quaternions are used in the animation
      */
     private static _CreateCubicSplineAnimation(babylonTransformNode: TransformNode, animation: Animation, animationChannelTargetPath: AnimationChannelTargetPath, frameDelta: number, inputs: number[], outputs: number[][], convertToRightHandedSystem: boolean, useQuaternion: boolean) {
-        animation.getKeys().forEach(function(keyFrame) {
+        animation.getKeys().forEach(function (keyFrame) {
             inputs.push(keyFrame.frame / animation.framePerSecond); // keyframes in seconds.
             _GLTFAnimation.AddSplineTangent(
                 babylonTransformNode,
@@ -594,7 +781,7 @@ export class _GLTFAnimation {
      */
     private static _AddKeyframeValue(keyFrame: IAnimationKey, animation: Animation, outputs: number[][], animationChannelTargetPath: AnimationChannelTargetPath, babylonTransformNode: TransformNode, convertToRightHandedSystem: boolean, useQuaternion: boolean) {
         let value: number[];
-        let newPositionRotationOrScale: Nullable<Vector3 | Quaternion>;
+        let newPositionRotationOrScale: Nullable<Vector3 | Quaternion | number>;
         const animationType = animation.dataType;
         if (animationType === Animation.ANIMATIONTYPE_VECTOR3) {
             value = keyFrame.value.asArray();
@@ -622,31 +809,35 @@ export class _GLTFAnimation {
             outputs.push(value); // scale  vector.
 
         }
-        else if (animationType === Animation.ANIMATIONTYPE_FLOAT) { // handles single component x, y, z or w component animation by using a base property and animating over a component.
-            newPositionRotationOrScale = this._ConvertFactorToVector3OrQuaternion(keyFrame.value as number, babylonTransformNode, animation, animationType, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion);
-            if (newPositionRotationOrScale) {
-                if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
-                    let posRotScale = useQuaternion ? newPositionRotationOrScale as Quaternion : Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
-                    if (convertToRightHandedSystem) {
-                        _GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
+        else if (animationType === Animation.ANIMATIONTYPE_FLOAT) {
+            if (animationChannelTargetPath === AnimationChannelTargetPath.WEIGHTS) {
+                outputs.push([keyFrame.value]);
+            } else {  // handles single component x, y, z or w component animation by using a base property and animating over a component.
+                newPositionRotationOrScale = this._ConvertFactorToVector3OrQuaternion(keyFrame.value as number, babylonTransformNode, animation, animationType, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion);
+                if (newPositionRotationOrScale) {
+                    if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
+                        let posRotScale = useQuaternion ? newPositionRotationOrScale as Quaternion : Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
+                        if (convertToRightHandedSystem) {
+                            _GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
 
-                        if (!babylonTransformNode.parent) {
-                            posRotScale = Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
+                            if (!babylonTransformNode.parent) {
+                                posRotScale = Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
+                            }
                         }
+                        outputs.push(posRotScale.asArray());
                     }
-                    outputs.push(posRotScale.asArray());
-                }
-                else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
-                    if (convertToRightHandedSystem) {
-                        _GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale as Vector3);
+                    else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
+                        if (convertToRightHandedSystem) {
+                            _GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale as Vector3);
 
-                        if (!babylonTransformNode.parent) {
-                            newPositionRotationOrScale.x *= -1;
-                            newPositionRotationOrScale.z *= -1;
+                            if (!babylonTransformNode.parent) {
+                                newPositionRotationOrScale.x *= -1;
+                                newPositionRotationOrScale.z *= -1;
+                            }
                         }
                     }
+                    outputs.push(newPositionRotationOrScale.asArray());
                 }
-                outputs.push(newPositionRotationOrScale.asArray());
             }
         }
         else if (animationType === Animation.ANIMATIONTYPE_QUATERNION) {
@@ -736,7 +927,7 @@ export class _GLTFAnimation {
      */
     private static AddSplineTangent(babylonTransformNode: TransformNode, tangentType: _TangentType, outputs: number[][], animationChannelTargetPath: AnimationChannelTargetPath, interpolation: AnimationSamplerInterpolation, keyFrame: IAnimationKey, frameDelta: number, useQuaternion: boolean, convertToRightHandedSystem: boolean) {
         let tangent: number[];
-        let tangentValue: Vector3 | Quaternion = tangentType === _TangentType.INTANGENT ? keyFrame.inTangent : keyFrame.outTangent;
+        let tangentValue: Vector3 | Quaternion | number = tangentType === _TangentType.INTANGENT ? keyFrame.inTangent : keyFrame.outTangent;
         if (interpolation === AnimationSamplerInterpolation.CUBICSPLINE) {
             if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
                 if (tangentValue) {
@@ -759,6 +950,13 @@ export class _GLTFAnimation {
                     tangent = [0, 0, 0, 0];
                 }
             }
+            else if (animationChannelTargetPath === AnimationChannelTargetPath.WEIGHTS) {
+                if (tangentValue) {
+                    tangent = [tangentValue as number];
+                } else {
+                    tangent = [0];
+                }
+            }
             else {
                 if (tangentValue) {
                     tangent = (tangentValue as Vector3).asArray();
@@ -789,7 +987,7 @@ export class _GLTFAnimation {
     private static calculateMinMaxKeyFrames(keyFrames: IAnimationKey[]): { min: number, max: number } {
         let min: number = Infinity;
         let max: number = -Infinity;
-        keyFrames.forEach(function(keyFrame) {
+        keyFrames.forEach(function (keyFrame) {
             min = Math.min(min, keyFrame.frame);
             max = Math.max(max, keyFrame.frame);
         });

+ 230 - 14
serializers/src/glTF/2.0/glTFExporter.ts

@@ -10,6 +10,7 @@ import { TransformNode } from "babylonjs/Meshes/transformNode";
 import { AbstractMesh } from "babylonjs/Meshes/abstractMesh";
 import { SubMesh } from "babylonjs/Meshes/subMesh";
 import { Mesh } from "babylonjs/Meshes/mesh";
+import { MorphTarget } from "babylonjs/Morph/morphTarget";
 import { LinesMesh } from "babylonjs/Meshes/linesMesh";
 import { InstancedMesh } from "babylonjs/Meshes/instancedMesh";
 import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
@@ -195,7 +196,7 @@ export class _Exporter {
         return this._applyExtensions(meshPrimitive, (extension, node) => extension.postExportMeshPrimitiveAsync && extension.postExportMeshPrimitiveAsync(context, node, babylonSubMesh, binaryWriter));
     }
 
-    public _extensionsPostExportNodeAsync(context: string, node: Nullable<INode>, babylonNode: Node, nodeMap?: {[key: number]: number}): Promise<Nullable<INode>> {
+    public _extensionsPostExportNodeAsync(context: string, node: Nullable<INode>, babylonNode: Node, nodeMap?: { [key: number]: number }): Promise<Nullable<INode>> {
         return this._applyExtensions(node, (extension, node) => extension.postExportNodeAsync && extension.postExportNodeAsync(context, node, babylonNode, nodeMap));
     }
 
@@ -763,7 +764,106 @@ export class _Exporter {
         }
 
         let writeBinaryFunc;
-        switch (attributeComponentKind){
+        switch (attributeComponentKind) {
+            case AccessorComponentType.UNSIGNED_BYTE: {
+                writeBinaryFunc = binaryWriter.setUInt8.bind(binaryWriter);
+                break;
+            }
+            case AccessorComponentType.UNSIGNED_SHORT: {
+                writeBinaryFunc = binaryWriter.setUInt16.bind(binaryWriter);
+                break;
+            }
+            case AccessorComponentType.UNSIGNED_INT: {
+                writeBinaryFunc = binaryWriter.setUInt32.bind(binaryWriter);
+            }
+            case AccessorComponentType.FLOAT: {
+                writeBinaryFunc = binaryWriter.setFloat32.bind(binaryWriter);
+                break;
+            }
+            default: {
+                Tools.Warn("Unsupported Attribute Component kind: " + attributeComponentKind);
+                return;
+            }
+        }
+
+        for (let vertexAttribute of vertexAttributes) {
+            for (let component of vertexAttribute) {
+                writeBinaryFunc(component);
+            }
+        }
+    }
+
+    /**
+     * Writes mesh attribute data to a data buffer
+     * Returns the bytelength of the data
+     * @param vertexBufferKind Indicates what kind of vertex data is being passed in
+     * @param meshAttributeArray Array containing the attribute data
+     * @param byteStride Specifies the space between data
+     * @param binaryWriter The buffer to write the binary data to
+     * @param convertToRightHandedSystem Converts the values to right-handed
+     */
+    public writeMorphTargetAttributeData(vertexBufferKind: string, attributeComponentKind: AccessorComponentType, meshPrimitive: SubMesh, morphTarget: MorphTarget, meshAttributeArray: FloatArray, morphTargetAttributeArray: FloatArray, stride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean, minMax?: any) {
+        let vertexAttributes: number[][] = [];
+        let index: number;
+        let difference: Vector3 = new Vector3();
+        let difference4: Vector4 = new Vector4(0, 0, 0, 0);
+
+        switch (vertexBufferKind) {
+            case VertexBuffer.PositionKind: {
+                for (let k = meshPrimitive.verticesStart; k < meshPrimitive.verticesCount; ++k) {
+                    index = meshPrimitive.indexStart + k * stride;
+                    const vertexData = Vector3.FromArray(meshAttributeArray, index);
+                    const morphData = Vector3.FromArray(morphTargetAttributeArray, index);
+                    difference = morphData.subtractToRef(vertexData, difference);
+                    if (convertToRightHandedSystem) {
+                        _GLTFUtilities._GetRightHandedPositionVector3FromRef(difference);
+                    }
+                    if (minMax) {
+                        minMax.min.copyFromFloats(Math.min(difference.x, minMax.min.x), Math.min(difference.y, minMax.min.y), Math.min(difference.z, minMax.min.z));
+                        minMax.max.copyFromFloats(Math.max(difference.x, minMax.max.x), Math.max(difference.y, minMax.max.y), Math.max(difference.z, minMax.max.z));
+                    }
+                    vertexAttributes.push(difference.asArray());
+                }
+                break;
+            }
+            case VertexBuffer.NormalKind: {
+                for (let k = meshPrimitive.verticesStart; k < meshPrimitive.verticesCount; ++k) {
+                    index = meshPrimitive.indexStart + k * stride;
+                    const vertexData = Vector3.FromArray(meshAttributeArray, index);
+                    vertexData.normalize();
+                    const morphData = Vector3.FromArray(morphTargetAttributeArray, index);
+                    morphData.normalize();
+                    difference = morphData.subtractToRef(vertexData, difference);
+                    if (convertToRightHandedSystem) {
+                        _GLTFUtilities._GetRightHandedNormalVector3FromRef(difference);
+                    }
+                    vertexAttributes.push(difference.asArray());
+                }
+                break;
+            }
+            case VertexBuffer.TangentKind: {
+                for (let k = meshPrimitive.verticesStart; k < meshPrimitive.verticesCount; ++k) {
+                    index = meshPrimitive.indexStart + k * (stride + 1);
+                    const vertexData = Vector4.FromArray(meshAttributeArray, index);
+                    _GLTFUtilities._NormalizeTangentFromRef(vertexData);
+                    const morphData = Vector4.FromArray(morphTargetAttributeArray, index);
+                    _GLTFUtilities._NormalizeTangentFromRef(morphData);
+                    difference4 = morphData.subtractToRef(vertexData, difference4);
+                    if (convertToRightHandedSystem) {
+                        _GLTFUtilities._GetRightHandedVector4FromRef(difference4);
+                    }
+                    vertexAttributes.push([difference4.x, difference4.y, difference4.z]);
+                }
+                break;
+            }
+            default: {
+                Tools.Warn("Unsupported Vertex Buffer Type: " + vertexBufferKind);
+                vertexAttributes = [];
+            }
+        }
+
+        let writeBinaryFunc;
+        switch (attributeComponentKind) {
             case AccessorComponentType.UNSIGNED_BYTE: {
                 writeBinaryFunc = binaryWriter.setUInt8.bind(binaryWriter);
                 break;
@@ -1110,6 +1210,105 @@ export class _Exporter {
     }
 
     /**
+ * Creates a bufferview based on the vertices type for the Babylon mesh
+ * @param babylonSubMesh The Babylon submesh that the morph target is applied to
+ * @param babylonMorphTarget the morph target to be exported
+ * @param binaryWriter The buffer to write the bufferview data to
+ * @param convertToRightHandedSystem Converts the values to right-handed
+ */
+    private setMorphTargetAttributes(babylonSubMesh: SubMesh, meshPrimitive: IMeshPrimitive, babylonMorphTarget: MorphTarget, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean) {
+        if (babylonMorphTarget) {
+            if (!meshPrimitive.targets) {
+                meshPrimitive.targets = [];
+            }
+            let target: { [attribute: string]: number } = {};
+            if (babylonMorphTarget.hasNormals) {
+                const vertexNormals = babylonSubMesh.getMesh().getVerticesData(VertexBuffer.NormalKind)!;
+                const morphNormals = babylonMorphTarget.getNormals()!;
+                const count = babylonSubMesh.verticesCount;
+                const byteStride = 12; // 3 x 4 byte floats
+                const byteLength = count * byteStride;
+                const bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, babylonMorphTarget.name + "_NORMAL");
+                this._bufferViews.push(bufferView);
+
+                let bufferViewIndex = this._bufferViews.length - 1;
+                const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, babylonMorphTarget.name + " - " + "NORMAL", AccessorType.VEC3, AccessorComponentType.FLOAT, count, 0, null, null);
+                this._accessors.push(accessor);
+                target.NORMAL = this._accessors.length - 1;
+
+                this.writeMorphTargetAttributeData(
+                    VertexBuffer.NormalKind,
+                    AccessorComponentType.FLOAT,
+                    babylonSubMesh,
+                    babylonMorphTarget,
+                    vertexNormals,
+                    morphNormals,
+                    byteStride / 4,
+                    binaryWriter,
+                    convertToRightHandedSystem
+                );
+            }
+            if (babylonMorphTarget.hasPositions) {
+                const vertexPositions = babylonSubMesh.getMesh().getVerticesData(VertexBuffer.PositionKind)!;
+                const morphPositions = babylonMorphTarget.getPositions()!;
+                const count = babylonSubMesh.verticesCount;
+                const byteStride = 12; // 3 x 4 byte floats
+                const byteLength = count * byteStride;
+                const bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, babylonMorphTarget.name + "_POSITION");
+                this._bufferViews.push(bufferView);
+
+                let bufferViewIndex = this._bufferViews.length - 1;
+                let minMax = { min: new Vector3(Infinity, Infinity, Infinity), max: new Vector3(-Infinity, -Infinity, -Infinity) };
+                const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, babylonMorphTarget.name + " - " + "POSITION", AccessorType.VEC3, AccessorComponentType.FLOAT, count, 0, null, null);
+                this._accessors.push(accessor);
+                target.POSITION = this._accessors.length - 1;
+
+                this.writeMorphTargetAttributeData(
+                    VertexBuffer.PositionKind,
+                    AccessorComponentType.FLOAT,
+                    babylonSubMesh,
+                    babylonMorphTarget,
+                    vertexPositions,
+                    morphPositions,
+                    byteStride / 4,
+                    binaryWriter,
+                    convertToRightHandedSystem,
+                    minMax
+                );
+                accessor.min = minMax.min!.asArray();
+                accessor.max = minMax.max!.asArray();
+            }
+            if (babylonMorphTarget.hasTangents) {
+                const vertexTangents = babylonSubMesh.getMesh().getVerticesData(VertexBuffer.TangentKind)!;
+                const morphTangents = babylonMorphTarget.getTangents()!;
+                const count = babylonSubMesh.verticesCount;
+                const byteStride = 12; // 3 x 4 byte floats
+                const byteLength = count * byteStride;
+                const bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, babylonMorphTarget.name + "_NORMAL");
+                this._bufferViews.push(bufferView);
+
+                let bufferViewIndex = this._bufferViews.length - 1;
+                const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, babylonMorphTarget.name + " - " + "TANGENT", AccessorType.VEC3, AccessorComponentType.FLOAT, count, 0, null, null);
+                this._accessors.push(accessor);
+                target.TANGENT = this._accessors.length - 1;
+
+                this.writeMorphTargetAttributeData(
+                    VertexBuffer.TangentKind,
+                    AccessorComponentType.FLOAT,
+                    babylonSubMesh,
+                    babylonMorphTarget,
+                    vertexTangents,
+                    morphTangents,
+                    byteStride / 4,
+                    binaryWriter,
+                    convertToRightHandedSystem,
+                );
+            }
+            meshPrimitive.targets.push(target);
+        }
+    }
+
+    /**
      * The primitive mode of the Babylon mesh
      * @param babylonMesh The BabylonJS mesh
      */
@@ -1239,8 +1438,8 @@ export class _Exporter {
             { kind: VertexBuffer.NormalKind, accessorType: AccessorType.VEC3, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 12 },
             { kind: VertexBuffer.ColorKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 16 },
             { kind: VertexBuffer.TangentKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 16 },
-            { kind: VertexBuffer.UVKind, accessorType: AccessorType.VEC2, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 8  },
-            { kind: VertexBuffer.UV2Kind, accessorType: AccessorType.VEC2, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 8  },
+            { kind: VertexBuffer.UVKind, accessorType: AccessorType.VEC2, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 8 },
+            { kind: VertexBuffer.UV2Kind, accessorType: AccessorType.VEC2, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 8 },
             { kind: VertexBuffer.MatricesIndicesKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.UNSIGNED_SHORT, byteStride: 8 },
             { kind: VertexBuffer.MatricesIndicesExtraKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.UNSIGNED_SHORT, byteStride: 8 },
             { kind: VertexBuffer.MatricesWeightsKind, accessorType: AccessorType.VEC4, accessorComponentType: AccessorComponentType.FLOAT, byteStride: 16 },
@@ -1251,6 +1450,7 @@ export class _Exporter {
             let indexBufferViewIndex: Nullable<number> = null;
             const primitiveMode = this.getMeshPrimitiveMode(bufferMesh);
             let vertexAttributeBufferViews: { [attributeKind: string]: number } = {};
+            let morphTargetManager = bufferMesh.morphTargetManager;
 
             // For each BabylonMesh, create bufferviews for each 'kind'
             for (const attribute of attributeData) {
@@ -1382,6 +1582,14 @@ export class _Exporter {
                         meshPrimitive.material = materialIndex;
 
                     }
+                    if (morphTargetManager) {
+                        let target;
+                        for (let i = 0; i < morphTargetManager.numTargets; ++i) {
+                            target = morphTargetManager.getTarget(i);
+                            this.setMorphTargetAttributes(submesh, meshPrimitive, target, binaryWriter, convertToRightHandedSystem);
+                        }
+                    }
+
                     mesh.primitives.push(meshPrimitive);
 
                     const promise = this._extensionsPostExportMeshPrimitiveAsync("postExport", meshPrimitive, submesh, binaryWriter);
@@ -1402,8 +1610,7 @@ export class _Exporter {
      * @returns True if the node is used to convert its descendants from right-handed to left-handed. False otherwise
      */
     private isBabylonCoordinateSystemConvertingNode(node: Node): boolean {
-        if (node instanceof TransformNode)
-        {
+        if (node instanceof TransformNode) {
             if (node.name !== "__root__") {
                 return false;
             }
@@ -1519,7 +1726,7 @@ export class _Exporter {
                             }
 
                             if (babylonNode instanceof Mesh) {
-                                let babylonMesh : Mesh = babylonNode;
+                                let babylonMesh: Mesh = babylonNode;
                                 if (babylonMesh.skeleton) {
                                     glTFNode.skin = skinMap[babylonMesh.skeleton.uniqueId];
                                 }
@@ -1586,6 +1793,7 @@ export class _Exporter {
 
                                 if (!babylonScene.animationGroups.length && babylonNode.animations.length) {
                                     _GLTFAnimation._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, convertToRightHandedSystem, this._animationSampleRate);
+                                    _GLTFAnimation._CreateMorphTargetAnimationFromMorphTargets(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, convertToRightHandedSystem, this._animationSampleRate);
                                 }
                             });
                         }
@@ -1623,7 +1831,7 @@ export class _Exporter {
      * @param nodeMap Node mapping of unique id to glTF node index
      * @returns glTF node
      */
-    private createNodeAsync(babylonNode: Node, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean, nodeMap?: {[key: number]: number}): Promise<INode> {
+    private createNodeAsync(babylonNode: Node, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean, nodeMap?: { [key: number]: number }): Promise<INode> {
         return Promise.resolve().then(() => {
             // create node to hold translation/rotation/scale and the mesh
             const node: INode = {};
@@ -1637,7 +1845,15 @@ export class _Exporter {
             if (babylonNode instanceof TransformNode) {
                 // Set transformation
                 this.setNodeTransformation(node, babylonNode, convertToRightHandedSystem);
-
+                if (babylonNode instanceof Mesh) {
+                    let morphTargetManager = babylonNode.morphTargetManager;
+                    if (morphTargetManager && morphTargetManager.numTargets > 0) {
+                        mesh.weights = [];
+                        for (let i = 0; i < morphTargetManager.numTargets; ++i) {
+                            mesh.weights.push(morphTargetManager.getTarget(i).influence);
+                        }
+                    }
+                }
                 return this.setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter, convertToRightHandedSystem).then(() => {
                     if (mesh.primitives.length) {
                         this._meshes.push(mesh);
@@ -1664,9 +1880,9 @@ export class _Exporter {
         const skinMap: { [key: number]: number } = {};
         for (let skeleton of babylonScene.skeletons) {
             // create skin
-            const skin: ISkin = { joints: []};
-            let inverseBindMatrices : Matrix[] = [];
-            let skeletonMesh = babylonScene.meshes.find((mesh) => {mesh.skeleton === skeleton; });
+            const skin: ISkin = { joints: [] };
+            let inverseBindMatrices: Matrix[] = [];
+            let skeletonMesh = babylonScene.meshes.find((mesh) => { mesh.skeleton === skeleton; });
             skin.skeleton = skeleton.overrideMesh === null ? (skeletonMesh ? nodeMap[skeletonMesh.uniqueId] : undefined) : nodeMap[skeleton.overrideMesh.uniqueId];
             for (let bone of skeleton.bones) {
                 if (bone._index != -1) {
@@ -1687,7 +1903,7 @@ export class _Exporter {
             let bufferViewOffset = binaryWriter.getByteOffset();
             let bufferView = _GLTFUtilities._CreateBufferView(0, bufferViewOffset, byteLength, byteStride, "InverseBindMatrices" + " - " + skeleton.name);
             this._bufferViews.push(bufferView);
-            let bufferViewIndex =  this._bufferViews.length - 1;
+            let bufferViewIndex = this._bufferViews.length - 1;
             let bindMatrixAccessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, "InverseBindMatrices" + " - " + skeleton.name, AccessorType.MAT4, AccessorComponentType.FLOAT, inverseBindMatrices.length, null, null, null);
             let inverseBindAccessorIndex = this._accessors.push(bindMatrixAccessor) - 1;
             skin.inverseBindMatrices = inverseBindAccessorIndex;
@@ -1916,4 +2132,4 @@ export class _BinaryWriter {
             this._byteOffset += 4;
         }
     }
-}
+}

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

@@ -204,4 +204,23 @@ export class _GLTFUtilities {
             matrix
         );
     }
+
+    public static _GetDataAccessorElementCount(accessorType: AccessorType) {
+        switch (accessorType) {
+            case AccessorType.MAT2:
+                return 4;
+            case AccessorType.MAT3:
+                return 9;
+            case AccessorType.MAT4:
+                return 16;
+            case AccessorType.SCALAR:
+                return 1;
+            case AccessorType.VEC2:
+                return 2;
+            case AccessorType.VEC3:
+                return 3;
+            case AccessorType.VEC4:
+                return 4;
+        }
+    }
 }

+ 12 - 8
src/Culling/ray.ts

@@ -692,11 +692,15 @@ Scene.prototype._internalPick = function (rayFunction: (world: Matrix) => Ray, p
             continue;
         }
 
+        let world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
+
         if (mesh.hasThinInstances && (mesh as Mesh).thinInstanceEnablePicking) {
+            const tmpMatrix = TmpVectors.Matrix[0];
             let thinMatrices = (mesh as Mesh).thinInstanceGetWorldMatrices();
             for (let index = 0; index < thinMatrices.length; index++) {
-                let world = thinMatrices[index];
-                let result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate);
+                let thinMatrix = thinMatrices[index];
+                thinMatrix.multiplyToRef(world, tmpMatrix);
+                let result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, tmpMatrix, fastCheck, onlyBoundingInfo, trianglePredicate);
 
                 if (result) {
                     pickingInfo = result;
@@ -708,8 +712,6 @@ Scene.prototype._internalPick = function (rayFunction: (world: Matrix) => Ray, p
                 }
             }
         } else {
-            let world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
-
             let result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate);
 
             if (result) {
@@ -742,11 +744,15 @@ Scene.prototype._internalMultiPick = function (rayFunction: (world: Matrix) => R
             continue;
         }
 
+        let world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
+
         if (mesh.hasThinInstances && (mesh as Mesh).thinInstanceEnablePicking) {
+            const tmpMatrix = TmpVectors.Matrix[0];
             let thinMatrices = (mesh as Mesh).thinInstanceGetWorldMatrices();
             for (let index = 0; index < thinMatrices.length; index++) {
-                let world = thinMatrices[index];
-                let result = this._internalPickForMesh(null, rayFunction, mesh, world, false, false, trianglePredicate);
+                let thinMatrix = thinMatrices[index];
+                thinMatrix.multiplyToRef(world, tmpMatrix);
+                let result = this._internalPickForMesh(null, rayFunction, mesh, tmpMatrix, false, false, trianglePredicate);
 
                 if (result) {
                     result.thinInstanceIndex = index;
@@ -754,8 +760,6 @@ Scene.prototype._internalMultiPick = function (rayFunction: (world: Matrix) => R
                 }
             }
         } else {
-            let world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
-
             let result = this._internalPickForMesh(null, rayFunction, mesh, world, false, false, trianglePredicate);
 
             if (result) {

+ 24 - 184
src/Helpers/photoDome.ts

@@ -1,13 +1,6 @@
-import { Observable, Observer } from "../Misc/observable";
-import { Nullable } from "../types";
 import { Scene } from "../scene";
-import { TransformNode } from "../Meshes/transformNode";
-import { Mesh } from "../Meshes/mesh";
 import { Texture } from "../Materials/Textures/texture";
-import { BackgroundMaterial } from "../Materials/Background/backgroundMaterial";
-import "../Meshes/Builders/sphereBuilder";
-import { Vector3 } from '../Maths/math.vector';
-import { Camera } from '../Cameras/camera';
+import { TextureDome } from "./textureDome";
 
 /**
  * Display a 360 degree photo on an approximately spherical surface, useful for VR applications or skyboxes.
@@ -15,212 +8,59 @@ import { Camera } from '../Cameras/camera';
  * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
  * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
  */
-export class PhotoDome extends TransformNode {
+export class PhotoDome extends TextureDome<Texture> {
     /**
      * Define the image as a Monoscopic panoramic 360 image.
      */
-    public static readonly MODE_MONOSCOPIC = 0;
+    public static readonly MODE_MONOSCOPIC = TextureDome.MODE_MONOSCOPIC;
     /**
      * Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
      */
-    public static readonly MODE_TOPBOTTOM = 1;
+    public static readonly MODE_TOPBOTTOM = TextureDome.MODE_TOPBOTTOM;
     /**
      * Define the image as a Stereoscopic Side by Side panoramic 360 image.
      */
-    public static readonly MODE_SIDEBYSIDE = 2;
-
-    private _useDirectMapping = false;
-
-    /**
-     * The texture being displayed on the sphere
-     */
-    protected _photoTexture: Texture;
-
+    public static readonly MODE_SIDEBYSIDE = TextureDome.MODE_SIDEBYSIDE;
     /**
      * Gets or sets the texture being displayed on the sphere
      */
     public get photoTexture(): Texture {
-        return this._photoTexture;
+        return this.texture;
     }
 
-    public set photoTexture(value: Texture) {
-        if (this._photoTexture === value) {
-            return;
-        }
-        this._photoTexture = value;
-        if (this._useDirectMapping) {
-            this._photoTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
-            this._photoTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this._material.diffuseTexture = this._photoTexture;
-        } else {
-            this._photoTexture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
-            this._photoTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this._material.reflectionTexture = this._photoTexture;
-        }
-    }
-
-    /**
-     * Observable raised when an error occured while loading the 360 image
-     */
-    public onLoadErrorObservable = new Observable<string>();
-
     /**
-     * The skybox material
+     * sets the texture being displayed on the sphere
      */
-    protected _material: BackgroundMaterial;
-
-    /**
-     * The surface used for the skybox
-     */
-    protected _mesh: Mesh;
-    /**
-     * Gets the mesh used for the skybox.
-     */
-    public get mesh(): Mesh {
-        return this._mesh;
+    public set photoTexture(value: Texture) {
+        this.texture = value;
     }
 
     /**
-     * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-     * Also see the options.resolution property.
+     * Gets the current video mode for the video. It can be:
+     * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+     * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+     * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
      */
-    public get fovMultiplier(): number {
-        return this._material.fovMultiplier;
-    }
-    public set fovMultiplier(value: number) {
-        this._material.fovMultiplier = value;
+    public get imageMode(): number {
+        return this.textureMode;
     }
-
-    private _imageMode = PhotoDome.MODE_MONOSCOPIC;
     /**
-     * Gets or set the current video mode for the video. It can be:
-     * * PhotoDome.MODE_MONOSCOPIC : Define the image as a Monoscopic panoramic 360 image.
-     * * PhotoDome.MODE_TOPBOTTOM  : Define the image as a Stereoscopic TopBottom/OverUnder panoramic 360 image.
-     * * PhotoDome.MODE_SIDEBYSIDE : Define the image as a Stereoscopic Side by Side panoramic 360 image.
+     * Sets the current video mode for the video. It can be:
+     * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+     * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+     * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
      */
-    public get imageMode(): number {
-        return this._imageMode;
-    }
     public set imageMode(value: number) {
-        if (this._imageMode === value) {
-            return;
-        }
-
-        this._changeImageMode(value);
+        this.textureMode = value;
     }
 
-    /**
-     * Create an instance of this class and pass through the parameters to the relevant classes, Texture, StandardMaterial, and Mesh.
-     * @param name Element's name, child elements will append suffixes for their own names.
-     * @param urlsOfPhoto defines the url of the photo to display
-     * @param options defines an object containing optional or exposed sub element properties
-     * @param onError defines a callback called when an error occured while loading the texture
-     */
-    constructor(name: string, urlOfPhoto: string, options: {
-        resolution?: number,
-        size?: number,
-        useDirectMapping?: boolean,
-        faceForward?: boolean
-    }, scene: Scene, onError: Nullable<(message?: string, exception?: any) => void> = null) {
-        super(name, scene);
-
-        // set defaults and manage values
-        name = name || "photoDome";
-        options.resolution = (Math.abs(options.resolution as any) | 0) || 32;
-        options.size = Math.abs(options.size as any) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
-
-        if (options.useDirectMapping === undefined) {
-            this._useDirectMapping = true;
-        } else {
-            this._useDirectMapping = options.useDirectMapping;
-        }
-
-        if (options.faceForward === undefined) {
-            options.faceForward = true;
-        }
-
-        this._setReady(false);
-
-        // create
-        let material = this._material = new BackgroundMaterial(name + "_material", scene);
-        this._mesh = Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, Mesh.BACKSIDE);
-
-        // configure material
-        material.opacityFresnel = false;
-        material.useEquirectangularFOV = true;
-        material.fovMultiplier = 1.0;
-
-        this.photoTexture = new Texture(urlOfPhoto, scene, true, !this._useDirectMapping, undefined, undefined, (message, exception) => {
+    protected _initTexture(urlsOrElement: string, scene: Scene, options: any): Texture {
+        return new Texture(urlsOrElement, scene, true, !this._useDirectMapping, undefined, undefined, (message, exception) => {
             this.onLoadErrorObservable.notifyObservers(message || "Unknown error occured");
 
-            if (onError) {
-                onError(message, exception);
+            if (this.onError) {
+                this.onError(message, exception);
             }
         });
-        this.photoTexture.anisotropicFilteringLevel = 1;
-
-        this.photoTexture.onLoadObservable.addOnce(() => {
-            this._setReady(true);
-        });
-
-        // configure mesh
-        this._mesh.material = material;
-        this._mesh.parent = this;
-
-        // Initial rotation
-        if (options.faceForward && scene.activeCamera) {
-            let camera = scene.activeCamera;
-
-            let forward = Vector3.Forward();
-            var direction = Vector3.TransformNormal(forward, camera.getViewMatrix());
-            direction.normalize();
-
-            this.rotation.y = Math.acos(Vector3.Dot(forward, direction));
-        }
-    }
-
-    private _onBeforeCameraRenderObserver: Nullable<Observer<Camera>> = null;
-
-    private _changeImageMode(value: number): void {
-        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
-        this._imageMode = value;
-
-        // Default Setup and Reset.
-        this._photoTexture.uScale = 1;
-        this._photoTexture.vScale = 1;
-        this._photoTexture.uOffset = 0;
-        this._photoTexture.vOffset = 0;
-
-        switch (value) {
-            case PhotoDome.MODE_SIDEBYSIDE:
-                this._photoTexture.uScale = 0.5;
-                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
-                    this._photoTexture.uOffset = camera.isRightCamera ? 0.5 : 0.0;
-                });
-                break;
-            case PhotoDome.MODE_TOPBOTTOM:
-                this._photoTexture.vScale = 0.5;
-                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
-                    this._photoTexture.vOffset = camera.isRightCamera ? 0.5 : 0.0;
-                });
-                break;
-        }
-    }
-
-    /**
-     * Releases resources associated with this node.
-     * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-     * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
-     */
-    public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
-        this._photoTexture.dispose();
-        this._mesh.dispose();
-        this._material.dispose();
-
-        this.onLoadErrorObservable.clear();
-
-        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
-
-        super.dispose(doNotRecurse, disposeMaterialAndTextures);
     }
 }

+ 294 - 0
src/Helpers/textureDome.ts

@@ -0,0 +1,294 @@
+import { Scene } from "../scene";
+import { TransformNode } from "../Meshes/transformNode";
+import { Mesh } from "../Meshes/mesh";
+import { Texture } from "../Materials/Textures/texture";
+import { BackgroundMaterial } from "../Materials/Background/backgroundMaterial";
+import "../Meshes/Builders/sphereBuilder";
+import { Nullable } from "../types";
+import { Observer, Observable } from "../Misc/observable";
+import { Vector3 } from "../Maths/math.vector";
+import { Axis } from "../Maths/math";
+import { SphereBuilder } from "../Meshes/Builders/sphereBuilder";
+
+declare type Camera = import("../Cameras/camera").Camera;
+
+/**
+ * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
+ * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
+ * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
+ * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
+ */
+export abstract class TextureDome<T extends Texture> extends TransformNode {
+    /**
+     * Define the source as a Monoscopic panoramic 360/180.
+     */
+    public static readonly MODE_MONOSCOPIC = 0;
+    /**
+     * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
+     */
+    public static readonly MODE_TOPBOTTOM = 1;
+    /**
+     * Define the source as a Stereoscopic Side by Side panoramic 360/180.
+     */
+    public static readonly MODE_SIDEBYSIDE = 2;
+
+    private _halfDome: boolean = false;
+
+    protected _useDirectMapping = false;
+
+    /**
+     * The texture being displayed on the sphere
+     */
+    protected _texture: T;
+
+    /**
+     * Gets the texture being displayed on the sphere
+     */
+    public get texture(): T {
+        return this._texture;
+    }
+
+    /**
+     * Sets the texture being displayed on the sphere
+     */
+    public set texture(newTexture: T) {
+        if (this._texture === newTexture) {
+            return;
+        }
+        this._texture = newTexture;
+        if (this._useDirectMapping) {
+            this._texture.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._texture.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._material.diffuseTexture = this._texture;
+        } else {
+            this._texture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
+            this._texture.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._material.reflectionTexture = this._texture;
+        }
+    }
+
+    /**
+     * The skybox material
+     */
+    protected _material: BackgroundMaterial;
+
+    /**
+     * The surface used for the dome
+     */
+    protected _mesh: Mesh;
+    /**
+     * Gets the mesh used for the dome.
+     */
+    public get mesh(): Mesh {
+        return this._mesh;
+    }
+
+    /**
+     * A mesh that will be used to mask the back of the dome in case it is a 180 degree movie.
+     */
+    private _halfDomeMask: Mesh;
+
+    /**
+     * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
+     * Also see the options.resolution property.
+     */
+    public get fovMultiplier(): number {
+        return this._material.fovMultiplier;
+    }
+    public set fovMultiplier(value: number) {
+        this._material.fovMultiplier = value;
+    }
+
+    protected _textureMode = TextureDome.MODE_MONOSCOPIC;
+    /**
+     * Gets or set the current texture mode for the texture. It can be:
+     * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+     * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+     * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+     */
+    public get textureMode(): number {
+        return this._textureMode;
+    }
+    /**
+     * Sets the current texture mode for the texture. It can be:
+      * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
+     * * TextureDome.MODE_TOPBOTTOM  : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
+     * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
+     */
+    public set textureMode(value: number) {
+        if (this._textureMode === value) {
+            return;
+        }
+
+        this._changeTextureMode(value);
+    }
+
+    /**
+     * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
+     */
+    public get halfDome(): boolean {
+        return this._halfDome;
+    }
+
+    /**
+     * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+     */
+    public set halfDome(enabled: boolean) {
+        this._halfDome = enabled;
+        this._halfDomeMask.setEnabled(enabled);
+    }
+
+    /**
+     * Oberserver used in Stereoscopic VR Mode.
+     */
+    private _onBeforeCameraRenderObserver: Nullable<Observer<Camera>> = null;
+    /**
+     * Observable raised when an error occured while loading the 360 image
+     */
+    public onLoadErrorObservable = new Observable<string>();
+
+    /**
+     * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
+     * @param name Element's name, child elements will append suffixes for their own names.
+     * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
+     * @param options An object containing optional or exposed sub element properties
+     */
+    constructor(
+        name: string,
+        textureUrlOrElement: string | string[] | HTMLVideoElement,
+        options: {
+            resolution?: number;
+            clickToPlay?: boolean;
+            autoPlay?: boolean;
+            loop?: boolean;
+            size?: number;
+            poster?: string;
+            faceForward?: boolean;
+            useDirectMapping?: boolean;
+            halfDomeMode?: boolean;
+        },
+        scene: Scene,
+        protected onError: Nullable<(message?: string, exception?: any) => void> = null
+    ) {
+        super(name, scene);
+
+        scene = this.getScene();
+
+        // set defaults and manage values
+        name = name || "textureDome";
+        options.resolution = Math.abs(options.resolution as any) | 0 || 32;
+        options.clickToPlay = Boolean(options.clickToPlay);
+        options.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
+        options.loop = options.loop === undefined ? true : Boolean(options.loop);
+        options.size = Math.abs(options.size as any) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
+
+        if (options.useDirectMapping === undefined) {
+            this._useDirectMapping = true;
+        } else {
+            this._useDirectMapping = options.useDirectMapping;
+        }
+
+        if (options.faceForward === undefined) {
+            options.faceForward = true;
+        }
+
+        this._setReady(false);
+        this._mesh = Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, Mesh.BACKSIDE);
+        // configure material
+        let material = (this._material = new BackgroundMaterial(name + "_material", scene));
+        material.useEquirectangularFOV = true;
+        material.fovMultiplier = 1.0;
+        material.opacityFresnel = false;
+
+        const texture = this._initTexture(textureUrlOrElement, scene, options);
+        this.texture = texture;
+
+        // configure mesh
+        this._mesh.material = material;
+        this._mesh.parent = this;
+
+        // create a (disabled until needed) mask to cover unneeded segments of 180 texture.
+        this._halfDomeMask = SphereBuilder.CreateSphere("", { slice: 0.5, diameter: options.size * 0.98, segments: options.resolution * 2, sideOrientation: Mesh.BACKSIDE }, scene);
+        this._halfDomeMask.rotate(Axis.X, -Math.PI / 2);
+        // set the parent, so it will always be positioned correctly AND will be disposed when the main sphere is disposed
+        this._halfDomeMask.parent = this._mesh;
+        this._halfDome = !!options.halfDomeMode;
+        // enable or disable according to the settings
+        this._halfDomeMask.setEnabled(this._halfDome);
+
+        // create
+        this._texture.anisotropicFilteringLevel = 1;
+        this._texture.onLoadObservable.addOnce(() => {
+            this._setReady(true);
+        });
+
+        // Initial rotation
+        if (options.faceForward && scene.activeCamera) {
+            let camera = scene.activeCamera;
+
+            let forward = Vector3.Forward();
+            var direction = Vector3.TransformNormal(forward, camera.getViewMatrix());
+            direction.normalize();
+
+            this.rotation.y = Math.acos(Vector3.Dot(forward, direction));
+        }
+
+        this._changeTextureMode(this._textureMode);
+    }
+
+    protected abstract _initTexture(urlsOrElement: string | string[] | HTMLElement, scene: Scene, options: any): T;
+
+    protected _changeTextureMode(value: number): void {
+        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
+        this._textureMode = value;
+
+        // Default Setup and Reset.
+        this._texture.uScale = 1;
+        this._texture.vScale = 1;
+        this._texture.uOffset = 0;
+        this._texture.vOffset = 0;
+
+        switch (value) {
+            case TextureDome.MODE_MONOSCOPIC:
+                if (this._halfDome) {
+                    this._texture.uScale = 2;
+                    this._texture.uOffset = -1;
+                }
+                break;
+            case TextureDome.MODE_SIDEBYSIDE:
+                // in half-dome mode the uScale should be double of 360 texture
+                // Use 0.99999 to boost perf by not switching program
+                this._texture.uScale = this._halfDome ? 0.99999 : 0.5;
+                const rightOffset = this._halfDome ? 0.0 : 0.5;
+                const leftOffset = this._halfDome ? 0.5 : 0.0;
+                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
+                    this._texture.uOffset = camera.isRightCamera ? rightOffset : leftOffset;
+                });
+                break;
+            case TextureDome.MODE_TOPBOTTOM:
+                // in half-dome mode the vScale should be double of 360 texture
+                // Use 0.99999 to boost perf by not switching program
+                this._texture.vScale = this._halfDome ? 0.99999 : 0.5;
+                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
+                    this._texture.vOffset = camera.isRightCamera ? 0.5 : 0.0;
+                });
+                break;
+        }
+    }
+
+    /**
+     * Releases resources associated with this node.
+     * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+     * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
+     */
+    public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
+        this._texture.dispose();
+        this._mesh.dispose();
+        this._material.dispose();
+
+        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
+        this.onLoadErrorObservable.clear();
+
+        super.dispose(doNotRecurse, disposeMaterialAndTextures);
+    }
+}

+ 18 - 238
src/Helpers/videoDome.ts

@@ -1,15 +1,7 @@
 import { Scene } from "../scene";
-import { TransformNode } from "../Meshes/transformNode";
-import { Mesh } from "../Meshes/mesh";
 import { Texture } from "../Materials/Textures/texture";
 import { VideoTexture, VideoTextureSettings } from "../Materials/Textures/videoTexture";
-import { BackgroundMaterial } from "../Materials/Background/backgroundMaterial";
-import "../Meshes/Builders/sphereBuilder";
-import { Nullable } from "../types";
-import { Observer } from "../Misc/observable";
-import { Vector3 } from "../Maths/math.vector";
-import { Axis } from "../Maths/math";
-import { SphereBuilder } from "../Meshes/Builders/sphereBuilder";
+import { TextureDome } from "./textureDome";
 
 declare type Camera = import("../Cameras/camera").Camera;
 
@@ -19,261 +11,49 @@ declare type Camera = import("../Cameras/camera").Camera;
  * This class achieves its effect with a VideoTexture and a correctly configured BackgroundMaterial on an inverted sphere.
  * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
  */
-export class VideoDome extends TransformNode {
+export class VideoDome extends TextureDome<VideoTexture> {
     /**
      * Define the video source as a Monoscopic panoramic 360 video.
      */
-    public static readonly MODE_MONOSCOPIC = 0;
+    public static readonly MODE_MONOSCOPIC = TextureDome.MODE_MONOSCOPIC;
     /**
      * Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
      */
-    public static readonly MODE_TOPBOTTOM = 1;
+    public static readonly MODE_TOPBOTTOM = TextureDome.MODE_TOPBOTTOM;
     /**
      * Define the video source as a Stereoscopic Side by Side panoramic 360 video.
      */
-    public static readonly MODE_SIDEBYSIDE = 2;
-
-    private _halfDome: boolean = false;
-
-    private _useDirectMapping = false;
-
-    /**
-     * The video texture being displayed on the sphere
-     */
-    protected _videoTexture: VideoTexture;
+    public static readonly MODE_SIDEBYSIDE = TextureDome.MODE_SIDEBYSIDE;
 
     /**
-     * Gets the video texture being displayed on the sphere
+     * Get the video texture associated with this video dome
      */
     public get videoTexture(): VideoTexture {
-        return this._videoTexture;
-    }
-
-    /**
-     * The skybox material
-     */
-    protected _material: BackgroundMaterial;
-
-    /**
-     * The surface used for the video dome
-     */
-    protected _mesh: Mesh;
-    /**
-     * Gets the mesh used for the video dome.
-     */
-    public get mesh(): Mesh {
-        return this._mesh;
-    }
-
-    /**
-     * A mesh that will be used to mask the back of the video dome in case it is a 180 degree movie.
-     */
-    private _halfDomeMask: Mesh;
-
-    /**
-     * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
-     * Also see the options.resolution property.
-     */
-    public get fovMultiplier(): number {
-        return this._material.fovMultiplier;
-    }
-    public set fovMultiplier(value: number) {
-        this._material.fovMultiplier = value;
+        return this._texture;
     }
-
-    private _videoMode = VideoDome.MODE_MONOSCOPIC;
     /**
-     * Gets or set the current video mode for the video. It can be:
-     * * VideoDome.MODE_MONOSCOPIC : Define the video source as a Monoscopic panoramic 360 video.
-     * * VideoDome.MODE_TOPBOTTOM  : Define the video source as a Stereoscopic TopBottom/OverUnder panoramic 360 video.
-     * * VideoDome.MODE_SIDEBYSIDE : Define the video source as a Stereoscopic Side by Side panoramic 360 video.
+     * Get the video mode of this dome
      */
     public get videoMode(): number {
-        return this._videoMode;
+        return this.textureMode;
     }
-    public set videoMode(value: number) {
-        if (this._videoMode === value) {
-            return;
-        }
-
-        this._changeVideoMode(value);
-    }
-
-    /**
-     * Is the video a 180 degrees video (half dome) or 360 video (full dome)
-     *
-     */
-    public get halfDome(): boolean {
-        return this._halfDome;
-    }
-
     /**
-     * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
+     * Set the video mode of this dome.
+     * @see textureMode
      */
-    public set halfDome(enabled: boolean) {
-        this._halfDome = enabled;
-        this._halfDomeMask.setEnabled(enabled);
+    public set videoMode(value: number) {
+        this.textureMode = value;
     }
 
-    /**
-     * Oberserver used in Stereoscopic VR Mode.
-     */
-    private _onBeforeCameraRenderObserver: Nullable<Observer<Camera>> = null;
-
-    /**
-     * Create an instance of this class and pass through the parameters to the relevant classes, VideoTexture, StandardMaterial, and Mesh.
-     * @param name Element's name, child elements will append suffixes for their own names.
-     * @param urlsOrVideo defines the url(s) or the video element to use
-     * @param options An object containing optional or exposed sub element properties
-     */
-    constructor(
-        name: string,
-        urlsOrVideo: string | string[] | HTMLVideoElement,
-        options: {
-            resolution?: number;
-            clickToPlay?: boolean;
-            autoPlay?: boolean;
-            loop?: boolean;
-            size?: number;
-            poster?: string;
-            faceForward?: boolean;
-            useDirectMapping?: boolean;
-            halfDomeMode?: boolean;
-        },
-        scene: Scene
-    ) {
-        super(name, scene);
-
-        scene = this.getScene();
-
-        // set defaults and manage values
-        name = name || "videoDome";
-        options.resolution = Math.abs(options.resolution as any) | 0 || 32;
-        options.clickToPlay = Boolean(options.clickToPlay);
-        options.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
-        options.loop = options.loop === undefined ? true : Boolean(options.loop);
-        options.size = Math.abs(options.size as any) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
-
-        if (options.useDirectMapping === undefined) {
-            this._useDirectMapping = true;
-        } else {
-            this._useDirectMapping = options.useDirectMapping;
-        }
-
-        if (options.faceForward === undefined) {
-            options.faceForward = true;
-        }
-
-        this._setReady(false);
-
-        // create
-        let tempOptions: VideoTextureSettings = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
-        let material = (this._material = new BackgroundMaterial(name + "_material", scene));
-        let texture = (this._videoTexture = new VideoTexture(name + "_texture", urlsOrVideo, scene, false, this._useDirectMapping, Texture.TRILINEAR_SAMPLINGMODE, tempOptions));
-        this._mesh = Mesh.CreateSphere(name + "_mesh", options.resolution, options.size, scene, false, Mesh.BACKSIDE);
-        texture.anisotropicFilteringLevel = 1;
-        texture.onLoadObservable.addOnce(() => {
-            this._setReady(true);
-        });
-
-        // configure material
-        material.useEquirectangularFOV = true;
-        material.fovMultiplier = 1.0;
-        material.opacityFresnel = false;
-
-        if (this._useDirectMapping) {
-            texture.wrapU = Texture.CLAMP_ADDRESSMODE;
-            texture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            material.diffuseTexture = texture;
-        } else {
-            texture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
-            texture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            material.reflectionTexture = texture;
-        }
-
-        // configure mesh
-        this._mesh.material = material;
-        this._mesh.parent = this;
-
-        // create a (disabled until needed) mask to cover unneeded segments of 180 videos.
-        this._halfDomeMask = SphereBuilder.CreateSphere("", { slice: 0.5, diameter: options.size * 0.99, segments: options.resolution, sideOrientation: Mesh.BACKSIDE }, scene);
-        this._halfDomeMask.rotate(Axis.X, -Math.PI / 2);
-        // set the parent, so it will always be positioned correctly AND will be disposed when the main sphere is disposed
-        this._halfDomeMask.parent = this._mesh;
-        this._halfDome = !!options.halfDomeMode;
-        // enable or disable according to the settings
-        this._halfDomeMask.setEnabled(this._halfDome);
-
+    protected _initTexture(urlsOrElement: string | string[] | HTMLVideoElement, scene: Scene, options: any): VideoTexture {
+        const tempOptions: VideoTextureSettings = { loop: options.loop, autoPlay: options.autoPlay, autoUpdateTexture: true, poster: options.poster };
+        const texture = new VideoTexture((this.name || "videoDome") + "_texture", urlsOrElement, scene, false, this._useDirectMapping, Texture.TRILINEAR_SAMPLINGMODE, tempOptions);
         // optional configuration
         if (options.clickToPlay) {
             scene.onPointerUp = () => {
-                this._videoTexture.video.play();
+                this._texture.video.play();
             };
         }
-
-        // Initial rotation
-        if (options.faceForward && scene.activeCamera) {
-            let camera = scene.activeCamera;
-
-            let forward = Vector3.Forward();
-            var direction = Vector3.TransformNormal(forward, camera.getViewMatrix());
-            direction.normalize();
-
-            this.rotation.y = Math.acos(Vector3.Dot(forward, direction));
-        }
-
-        this._changeVideoMode(this._videoMode);
-    }
-
-    private _changeVideoMode(value: number): void {
-        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
-        this._videoMode = value;
-
-        // Default Setup and Reset.
-        this._videoTexture.uScale = 1;
-        this._videoTexture.vScale = 1;
-        this._videoTexture.uOffset = 0;
-        this._videoTexture.vOffset = 0;
-
-        switch (value) {
-            case VideoDome.MODE_MONOSCOPIC:
-                if (this._halfDome) {
-                    this._videoTexture.uScale = 2;
-                }
-                break;
-            case VideoDome.MODE_SIDEBYSIDE:
-                // in half-dome mode the uScale should be double of 360 videos
-                // Use 0.99999 to boost perf by not switching program
-                this._videoTexture.uScale = this._halfDome ? 0.99999 : 0.5;
-                const rightOffset = this._halfDome ? 0.0 : 0.5;
-                const leftOffset = this._halfDome ? 0.5 : 0.0;
-                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
-                    this._videoTexture.uOffset = camera.isRightCamera ? rightOffset : leftOffset;
-                });
-                break;
-            case VideoDome.MODE_TOPBOTTOM:
-                // in half-dome mode the vScale should be double of 360 videos
-                // Use 0.99999 to boost perf by not switching program
-                this._videoTexture.vScale = this._halfDome ? 0.99999 : 0.5;
-                this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
-                    this._videoTexture.vOffset = camera.isRightCamera ? 0.5 : 0.0;
-                });
-                break;
-        }
-    }
-
-    /**
-     * Releases resources associated with this node.
-     * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
-     * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
-     */
-    public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
-        this._videoTexture.dispose();
-        this._mesh.dispose();
-        this._material.dispose();
-
-        this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
-
-        super.dispose(doNotRecurse, disposeMaterialAndTextures);
+        return texture;
     }
 }

+ 3 - 3
src/Rendering/utilityLayerRenderer.ts

@@ -174,7 +174,7 @@ export class UtilityLayerRenderer implements IDisposable {
                 // allow every non pointer down event to flow to the utility layer
                 if (this.onlyCheckPointerDownEvents && prePointerInfo.type != PointerEventTypes.POINTERDOWN) {
                     if (!prePointerInfo.skipOnPointerObservable) {
-                        this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                        this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick), prePointerInfo.type);
                     }
                     if (prePointerInfo.type === PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
                         this._pointerCaptures[pointerEvent.pointerId] = false;
@@ -187,7 +187,7 @@ export class UtilityLayerRenderer implements IDisposable {
                     if (utilityScenePick && utilityScenePick.hit) {
 
                         if (!prePointerInfo.skipOnPointerObservable) {
-                            this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                            this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick), prePointerInfo.type);
                         }
                         prePointerInfo.skipOnPointerObservable = true;
                     }
@@ -264,7 +264,7 @@ export class UtilityLayerRenderer implements IDisposable {
 
     private _notifyObservers(prePointerInfo: PointerInfoPre, pickInfo: PickingInfo, pointerEvent: PointerEvent) {
         if (!prePointerInfo.skipOnPointerObservable) {
-            this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo));
+            this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo), prePointerInfo.type);
             this._lastPointerEvents[pointerEvent.pointerId] = true;
         }
     }

BIN
tests/validation/ReferenceImages/gltfSerializerMorphTargetAnimation.png


+ 5 - 0
tests/validation/config.json

@@ -603,6 +603,11 @@
             "referenceImage": "gltfSerializerSkinningAndAnimation.png"
         },
         {
+            "title": "GLTF Serializer Morph Target Animation",
+            "playgroundId": "#T087A8#27",
+            "referenceImage": "gltfSerializerMorphTargetAnimation.png"
+        },
+        {
             "title": "GLTF Buggy with Draco Mesh Compression",
             "playgroundId": "#JNW207#1",
             "referenceImage": "gltfBuggyDraco.png"