瀏覽代碼

Merge pull request #7631 from BabylonJS/master

Nightly
mergify[bot] 5 年之前
父節點
當前提交
91897a6076
共有 31 個文件被更改,包括 821 次插入254 次删除
  1. 17 6
      dist/preview release/babylon.d.ts
  2. 2 2
      dist/preview release/babylon.js
  3. 85 27
      dist/preview release/babylon.max.js
  4. 1 1
      dist/preview release/babylon.max.js.map
  5. 34 12
      dist/preview release/babylon.module.d.ts
  6. 52 10
      dist/preview release/documentation.d.ts
  7. 3 2
      dist/preview release/gui/babylon.gui.js
  8. 1 1
      dist/preview release/gui/babylon.gui.js.map
  9. 1 1
      dist/preview release/gui/babylon.gui.min.js
  10. 126 48
      dist/preview release/serializers/babylon.glTF2Serializer.js
  11. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  12. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  13. 35 4
      dist/preview release/serializers/babylonjs.serializers.d.ts
  14. 126 48
      dist/preview release/serializers/babylonjs.serializers.js
  15. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  16. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  17. 70 8
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  18. 34 12
      dist/preview release/viewer/babylon.module.d.ts
  19. 9 9
      dist/preview release/viewer/babylon.viewer.js
  20. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  21. 3 1
      dist/preview release/what's new.md
  22. 4 3
      gui/src/2D/controls/image.ts
  23. 3 2
      serializers/src/glTF/2.0/Extensions/KHR_lights_punctual.ts
  24. 5 2
      serializers/src/glTF/2.0/glTFAnimation.ts
  25. 130 40
      serializers/src/glTF/2.0/glTFExporter.ts
  26. 5 0
      serializers/src/glTF/2.0/glTFSerializer.ts
  27. 52 0
      src/Engines/engine.ts
  28. 3 7
      src/Loading/Plugins/babylonFileLoader.ts
  29. 1 0
      src/Materials/Textures/hdrCubeTexture.ts
  30. 14 3
      src/Sprites/spriteManager.ts
  31. 二進制
      tests/validation/ReferenceImages/glTFSerializerNegativeWorldMatrix.png

+ 17 - 6
dist/preview release/babylon.d.ts

@@ -9957,12 +9957,12 @@ declare module BABYLON {
          */
         intersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
         /**
-     * Intersects the sprites with a ray
-     * @param ray defines the ray to intersect with
-     * @param camera defines the current active camera
-     * @param predicate defines a predicate used to select candidate sprites
-     * @returns null if no hit or a PickingInfo array
-     */
+         * Intersects the sprites with a ray
+         * @param ray defines the ray to intersect with
+         * @param camera defines the current active camera
+         * @param predicate defines a predicate used to select candidate sprites
+         * @returns null if no hit or a PickingInfo array
+         */
         multiIntersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean): Nullable<PickingInfo[]>;
         /**
          * Renders the list of sprites on screen.
@@ -10021,6 +10021,14 @@ declare module BABYLON {
          */
         get texture(): Texture;
         set texture(value: Texture);
+        private _blendMode;
+        /**
+         * Blend mode use to render the particle, it can be any of
+         * the static Constants.ALPHA_x properties provided in this class.
+         * Default value is Constants.ALPHA_COMBINE
+         */
+        get blendMode(): number;
+        set blendMode(blendMode: number);
         /**
          * Creates a new sprite manager
          * @param name defines the manager's name
@@ -33822,6 +33830,9 @@ declare module BABYLON {
          * @param buffer defines the webGL buffer to delete
          */
         deleteInstancesBuffer(buffer: WebGLBuffer): void;
+        private _clientWaitAsync;
+        /** @hidden */
+        _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView): Promise<ArrayBufferView> | null;
         /** @hidden */
         _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex?: number, level?: number, buffer?: Nullable<ArrayBufferView>): ArrayBufferView;
         dispose(): void;

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


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


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


+ 34 - 12
dist/preview release/babylon.module.d.ts

@@ -10148,12 +10148,12 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         intersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
         /**
-     * Intersects the sprites with a ray
-     * @param ray defines the ray to intersect with
-     * @param camera defines the current active camera
-     * @param predicate defines a predicate used to select candidate sprites
-     * @returns null if no hit or a PickingInfo array
-     */
+         * Intersects the sprites with a ray
+         * @param ray defines the ray to intersect with
+         * @param camera defines the current active camera
+         * @param predicate defines a predicate used to select candidate sprites
+         * @returns null if no hit or a PickingInfo array
+         */
         multiIntersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean): Nullable<PickingInfo[]>;
         /**
          * Renders the list of sprites on screen.
@@ -10212,6 +10212,14 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         get texture(): Texture;
         set texture(value: Texture);
+        private _blendMode;
+        /**
+         * Blend mode use to render the particle, it can be any of
+         * the static Constants.ALPHA_x properties provided in this class.
+         * Default value is Constants.ALPHA_COMBINE
+         */
+        get blendMode(): number;
+        set blendMode(blendMode: number);
         /**
          * Creates a new sprite manager
          * @param name defines the manager's name
@@ -34832,6 +34840,9 @@ declare module "babylonjs/Engines/engine" {
          * @param buffer defines the webGL buffer to delete
          */
         deleteInstancesBuffer(buffer: WebGLBuffer): void;
+        private _clientWaitAsync;
+        /** @hidden */
+        _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView): Promise<ArrayBufferView> | null;
         /** @hidden */
         _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex?: number, level?: number, buffer?: Nullable<ArrayBufferView>): ArrayBufferView;
         dispose(): void;
@@ -83285,12 +83296,12 @@ declare module BABYLON {
          */
         intersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
         /**
-     * Intersects the sprites with a ray
-     * @param ray defines the ray to intersect with
-     * @param camera defines the current active camera
-     * @param predicate defines a predicate used to select candidate sprites
-     * @returns null if no hit or a PickingInfo array
-     */
+         * Intersects the sprites with a ray
+         * @param ray defines the ray to intersect with
+         * @param camera defines the current active camera
+         * @param predicate defines a predicate used to select candidate sprites
+         * @returns null if no hit or a PickingInfo array
+         */
         multiIntersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean): Nullable<PickingInfo[]>;
         /**
          * Renders the list of sprites on screen.
@@ -83349,6 +83360,14 @@ declare module BABYLON {
          */
         get texture(): Texture;
         set texture(value: Texture);
+        private _blendMode;
+        /**
+         * Blend mode use to render the particle, it can be any of
+         * the static Constants.ALPHA_x properties provided in this class.
+         * Default value is Constants.ALPHA_COMBINE
+         */
+        get blendMode(): number;
+        set blendMode(blendMode: number);
         /**
          * Creates a new sprite manager
          * @param name defines the manager's name
@@ -107150,6 +107169,9 @@ declare module BABYLON {
          * @param buffer defines the webGL buffer to delete
          */
         deleteInstancesBuffer(buffer: WebGLBuffer): void;
+        private _clientWaitAsync;
+        /** @hidden */
+        _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView): Promise<ArrayBufferView> | null;
         /** @hidden */
         _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex?: number, level?: number, buffer?: Nullable<ArrayBufferView>): ArrayBufferView;
         dispose(): void;

+ 52 - 10
dist/preview release/documentation.d.ts

@@ -9957,12 +9957,12 @@ declare module BABYLON {
          */
         intersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
         /**
-     * Intersects the sprites with a ray
-     * @param ray defines the ray to intersect with
-     * @param camera defines the current active camera
-     * @param predicate defines a predicate used to select candidate sprites
-     * @returns null if no hit or a PickingInfo array
-     */
+         * Intersects the sprites with a ray
+         * @param ray defines the ray to intersect with
+         * @param camera defines the current active camera
+         * @param predicate defines a predicate used to select candidate sprites
+         * @returns null if no hit or a PickingInfo array
+         */
         multiIntersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean): Nullable<PickingInfo[]>;
         /**
          * Renders the list of sprites on screen.
@@ -10021,6 +10021,14 @@ declare module BABYLON {
          */
         get texture(): Texture;
         set texture(value: Texture);
+        private _blendMode;
+        /**
+         * Blend mode use to render the particle, it can be any of
+         * the static Constants.ALPHA_x properties provided in this class.
+         * Default value is Constants.ALPHA_COMBINE
+         */
+        get blendMode(): number;
+        set blendMode(blendMode: number);
         /**
          * Creates a new sprite manager
          * @param name defines the manager's name
@@ -33822,6 +33830,9 @@ declare module BABYLON {
          * @param buffer defines the webGL buffer to delete
          */
         deleteInstancesBuffer(buffer: WebGLBuffer): void;
+        private _clientWaitAsync;
+        /** @hidden */
+        _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView): Promise<ArrayBufferView> | null;
         /** @hidden */
         _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex?: number, level?: number, buffer?: Nullable<ArrayBufferView>): ArrayBufferView;
         dispose(): void;
@@ -76915,6 +76926,10 @@ declare module BABYLON {
          * Begin serialization without waiting for the scene to be ready
          */
         exportWithoutWaitingForScene?: boolean;
+        /**
+         * Indicates if coordinate system swapping root nodes should be included in export
+         */
+        includeCoordinateSystemConversionNodes?: boolean;
     }
     /**
      * Class for generating glTF data from a Babylon scene.
@@ -77114,10 +77129,17 @@ declare module BABYLON.GLTF2.Exporter {
          */
         private _nodeMap;
         /**
-         * Specifies if the Babylon scene should be converted to right-handed on export
+         * Specifies if the source Babylon scene was left handed, and needed conversion.
          */
         _convertToRightHandedSystem: boolean;
         /**
+         * Specifies if a Babylon node should be converted to right-handed on export
+         */
+        _convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        };
+        _includeCoordinateSystemConversionNodes: boolean;
+        /**
          * Baked animation sample rate
          */
         private _animationSampleRate;
@@ -77175,6 +77197,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderVertexAttributeDataBasedOnPrimitiveMode;
         /**
@@ -77187,6 +77210,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFillMode;
         /**
@@ -77199,6 +77223,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleStripDrawMode;
         /**
@@ -77211,6 +77236,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFanMode;
         /**
@@ -77220,6 +77246,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param vertexAttributeKind The vertex attribute type
          * @param meshAttributeArray The vertex attribute data
          * @param binaryWriter The writer containing the binary data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private writeVertexAttributeData;
         /**
@@ -77229,8 +77256,9 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray Array containing the attribute data
          * @param binaryWriter The buffer to write the binary data to
          * @param indices Used to specify the order of the vertex data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
-        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter): void;
+        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void;
         /**
          * Generates glTF json data
          * @param shouldUseGlb Indicates whether the json should be written for a glb file
@@ -77265,6 +77293,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Sets the TRS for each node
          * @param node glTF Node for storing the transformation data
          * @param babylonTransformNode Babylon mesh used as the source for the transformation data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setNodeTransformation;
         private getVertexBufferFromMesh;
@@ -77273,6 +77302,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param kind Indicates the type of vertices data
          * @param babylonTransformNode The Babylon mesh to get the vertices data from
          * @param binaryWriter The buffer to write the bufferview data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private createBufferViewKind;
         /**
@@ -77298,9 +77328,16 @@ declare module BABYLON.GLTF2.Exporter {
          * @param mesh glTF Mesh object to store the primitive attribute information
          * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
          * @param binaryWriter Buffer to write the attribute data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setPrimitiveAttributesAsync;
         /**
+         * Check if the node is used to convert its descendants from a right handed coordinate system to the Babylon scene's coordinate system.
+         * @param node The node to check
+         * @returns True if the node is used to convert its descendants from right-handed to left-handed. False otherwise
+         */
+        private isBabylonCoordinateSystemConvertingNode;
+        /**
          * Creates a glTF scene based on the array of meshes
          * Returns the the total byte offset
          * @param babylonScene Babylon scene to get the mesh data from
@@ -77319,6 +77356,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Creates a glTF node from a Babylon mesh
          * @param babylonMesh Source Babylon mesh
          * @param binaryWriter Buffer for storing geometry data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          * @returns glTF node
          */
         private createNodeAsync;
@@ -77464,6 +77502,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param bufferViews
          * @param accessors
          * @param convertToRightHandedSystem
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromNodeAnimations(babylonNode: Node, runtimeGLTFAnimation: IAnimation, idleGLTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
@@ -77478,11 +77517,14 @@ declare module BABYLON.GLTF2.Exporter {
          * @param binaryWriter
          * @param bufferViews
          * @param accessors
-         * @param convertToRightHandedSystem
+         * @param convertToRightHandedSystemMap
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromAnimationGroups(babylonScene: Scene, glTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
-        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number): void;
+        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        }, animationSampleRate: number): void;
         private static AddAnimation;
         /**
          * Create a baked animation

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

@@ -6826,7 +6826,8 @@ var Image = /** @class */ (function (_super) {
                     var vb = svgDoc.documentElement.getAttribute("viewBox");
                     var docwidth = Number(svgDoc.documentElement.getAttribute("width"));
                     var docheight = Number(svgDoc.documentElement.getAttribute("height"));
-                    if (vb && docwidth && docheight) {
+                    var elem = svgDoc.getElementById(elemid);
+                    if (elem && vb && docwidth && docheight) {
                         this._getSVGAttribs(svgExist, elemid);
                         return value;
                     }
@@ -6871,7 +6872,7 @@ var Image = /** @class */ (function (_super) {
             var docheight = Number(svgDoc.documentElement.getAttribute("height"));
             // get element bbox and matrix transform
             var elem = svgDoc.getElementById(elemid);
-            if (vb && docwidth && docheight) {
+            if (vb && docwidth && docheight && elem) {
                 var vb_width = Number(vb.split(" ")[2]);
                 var vb_height = Number(vb.split(" ")[3]);
                 var elem_bbox = elem.getBBox();

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


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


+ 126 - 48
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -432,8 +432,9 @@ var KHR_lights_punctual = /** @class */ (function () {
                 }
                 else {
                     var lightPosition = babylonLight.position.clone();
+                    var convertToRightHandedSystem = _this._exporter._convertToRightHandedSystemMap[babylonNode.uniqueId];
                     if (!lightPosition.equals(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Vector3"].Zero())) {
-                        if (_this._exporter._convertToRightHandedSystem) {
+                        if (convertToRightHandedSystem) {
                             _glTFUtilities__WEBPACK_IMPORTED_MODULE_2__["_GLTFUtilities"]._GetRightHandedPositionVector3FromRef(lightPosition);
                         }
                         node.translation = lightPosition.asArray();
@@ -444,7 +445,7 @@ var KHR_lights_punctual = /** @class */ (function () {
                         var len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
                         var pitch = -Math.atan2(localAxis.y, len);
                         var lightRotationQuaternion = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].RotationYawPitchRoll(yaw, pitch, 0);
-                        if (_this._exporter._convertToRightHandedSystem) {
+                        if (convertToRightHandedSystem) {
                             _glTFUtilities__WEBPACK_IMPORTED_MODULE_2__["_GLTFUtilities"]._GetRightHandedQuaternionFromRef(lightRotationQuaternion);
                         }
                         if (!lightRotationQuaternion.equals(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].Identity())) {
@@ -928,6 +929,7 @@ var _GLTFAnimation = /** @class */ (function () {
      * @param bufferViews
      * @param accessors
      * @param convertToRightHandedSystem
+     * @param animationSampleRate
      */
     _GLTFAnimation._CreateNodeAnimationFromNodeAnimations = function (babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, nodes, binaryWriter, bufferViews, accessors, convertToRightHandedSystem, animationSampleRate) {
         var glTFAnimation;
@@ -961,9 +963,10 @@ var _GLTFAnimation = /** @class */ (function () {
      * @param binaryWriter
      * @param bufferViews
      * @param accessors
-     * @param convertToRightHandedSystem
+     * @param convertToRightHandedSystemMap
+     * @param animationSampleRate
      */
-    _GLTFAnimation._CreateNodeAnimationFromAnimationGroups = function (babylonScene, glTFAnimations, nodeMap, nodes, binaryWriter, bufferViews, accessors, convertToRightHandedSystem, animationSampleRate) {
+    _GLTFAnimation._CreateNodeAnimationFromAnimationGroups = function (babylonScene, glTFAnimations, nodeMap, nodes, binaryWriter, bufferViews, accessors, convertToRightHandedSystemMap, animationSampleRate) {
         var glTFAnimation;
         if (babylonScene.animationGroups) {
             var animationGroups = babylonScene.animationGroups;
@@ -982,6 +985,7 @@ var _GLTFAnimation = /** @class */ (function () {
                         var animationInfo = _GLTFAnimation._DeduceAnimationInfo(targetAnimation.animation);
                         if (animationInfo) {
                             var babylonTransformNode = target instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["TransformNode"] ? target : target[0];
+                            var convertToRightHandedSystem = convertToRightHandedSystemMap[babylonTransformNode.uniqueId];
                             _GLTFAnimation.AddAnimation("" + animation.name, glTFAnimation, babylonTransformNode, animation, animationInfo.dataAccessorType, animationInfo.animationChannelTargetPath, nodeMap, binaryWriter, bufferViews, accessors, convertToRightHandedSystem, animationInfo.useQuaternion, animationSampleRate);
                         }
                     }
@@ -1574,6 +1578,10 @@ var _Exporter = /** @class */ (function () {
      * @param options Options to modify the behavior of the exporter
      */
     function _Exporter(babylonScene, options) {
+        /*
+        * Specifies if root Babylon empty nodes that act as a coordinate space transform should be included in export
+        */
+        this._includeCoordinateSystemConversionNodes = false;
         this._extensions = {};
         this._glTF = {
             asset: { generator: "BabylonJS", version: "2.0" }
@@ -1591,9 +1599,9 @@ var _Exporter = /** @class */ (function () {
         this._samplers = [];
         this._animations = [];
         this._imageData = {};
-        this._convertToRightHandedSystem = !this._babylonScene.useRightHandedSystem;
         this._options = options || {};
         this._animationSampleRate = options && options.animationSampleRate ? options.animationSampleRate : 1 / 60;
+        this._includeCoordinateSystemConversionNodes = options && options.includeCoordinateSystemConversionNodes ? true : false;
         this._glTFMaterialExporter = new _glTFMaterialExporter__WEBPACK_IMPORTED_MODULE_2__["_GLTFMaterialExporter"](this);
         this._loadExtensions();
     }
@@ -1784,20 +1792,21 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderVertexAttributeDataBasedOnPrimitiveMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
-        if (this._convertToRightHandedSystem && sideOrientation === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation) {
+    _Exporter.prototype.reorderVertexAttributeDataBasedOnPrimitiveMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
+        if (convertToRightHandedSystem && sideOrientation === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation) {
             switch (primitiveMode) {
                 case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].TriangleFillMode: {
-                    this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
                 case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].TriangleStripDrawMode: {
-                    this.reorderTriangleStripDrawMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleStripDrawMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
                 case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].TriangleFanDrawMode: {
-                    this.reorderTriangleFanMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleFanMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
             }
@@ -1813,8 +1822,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderTriangleFillMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
+    _Exporter.prototype.reorderTriangleFillMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
         var vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
         if (vertexBuffer) {
             var stride = vertexBuffer.byteStride / babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(vertexBuffer.type);
@@ -1875,7 +1885,7 @@ var _Exporter = /** @class */ (function () {
                         babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error("Unsupported Vertex Buffer type: " + vertexBufferKind);
                     }
                 }
-                this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter);
+                this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
             }
         }
         else {
@@ -1892,8 +1902,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderTriangleStripDrawMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
+    _Exporter.prototype.reorderTriangleStripDrawMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
         var vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
         if (vertexBuffer) {
             var stride = vertexBuffer.byteStride / babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(vertexBuffer.type);
@@ -1933,7 +1944,7 @@ var _Exporter = /** @class */ (function () {
                     babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error("Unsupported Vertex Buffer type: " + vertexBufferKind);
                 }
             }
-            this.writeVertexAttributeData(vertexData, byteOffset + 12, vertexBufferKind, meshAttributeArray, binaryWriter);
+            this.writeVertexAttributeData(vertexData, byteOffset + 12, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
         }
         else {
             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("reorderTriangleStripDrawMode: Vertex buffer kind " + vertexBufferKind + " not present!");
@@ -1949,8 +1960,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderTriangleFanMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
+    _Exporter.prototype.reorderTriangleFanMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
         var vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
         if (vertexBuffer) {
             var stride = vertexBuffer.byteStride / babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(vertexBuffer.type);
@@ -1992,7 +2004,7 @@ var _Exporter = /** @class */ (function () {
                     babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error("Unsupported Vertex Buffer type: " + vertexBufferKind);
                 }
             }
-            this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter);
+            this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
         }
         else {
             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("reorderTriangleFanMode: Vertex buffer kind " + vertexBufferKind + " not present!");
@@ -2005,11 +2017,12 @@ var _Exporter = /** @class */ (function () {
      * @param vertexAttributeKind The vertex attribute type
      * @param meshAttributeArray The vertex attribute data
      * @param binaryWriter The writer containing the binary data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.writeVertexAttributeData = function (vertices, byteOffset, vertexAttributeKind, meshAttributeArray, binaryWriter) {
+    _Exporter.prototype.writeVertexAttributeData = function (vertices, byteOffset, vertexAttributeKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem) {
         for (var _i = 0, vertices_1 = vertices; _i < vertices_1.length; _i++) {
             var vertex = vertices_1[_i];
-            if (this._convertToRightHandedSystem && !(vertexAttributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind) && !(vertex instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector2"])) {
+            if (convertToRightHandedSystem && !(vertexAttributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind) && !(vertex instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector2"])) {
                 if (vertex instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"]) {
                     if (vertexAttributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].NormalKind) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedNormalVector3FromRef(vertex);
@@ -2045,8 +2058,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray Array containing the attribute data
      * @param binaryWriter The buffer to write the binary data to
      * @param indices Used to specify the order of the vertex data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, byteStride, binaryWriter) {
+    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, byteStride, binaryWriter, convertToRightHandedSystem) {
         var stride = byteStride / 4;
         var vertexAttributes = [];
         var index;
@@ -2055,7 +2069,7 @@ var _Exporter = /** @class */ (function () {
                 for (var k = 0, length_2 = meshAttributeArray.length / stride; k < length_2; ++k) {
                     index = k * stride;
                     var vertexData = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedPositionVector3FromRef(vertexData);
                     }
                     vertexAttributes.push(vertexData.asArray());
@@ -2066,7 +2080,7 @@ var _Exporter = /** @class */ (function () {
                 for (var k = 0, length_3 = meshAttributeArray.length / stride; k < length_3; ++k) {
                     index = k * stride;
                     var vertexData = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedNormalVector3FromRef(vertexData);
                     }
                     vertexData.normalize();
@@ -2078,7 +2092,7 @@ var _Exporter = /** @class */ (function () {
                 for (var k = 0, length_4 = meshAttributeArray.length / stride; k < length_4; ++k) {
                     index = k * stride;
                     var vertexData = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedVector4FromRef(vertexData);
                     }
                     _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._NormalizeTangentFromRef(vertexData);
@@ -2098,7 +2112,7 @@ var _Exporter = /** @class */ (function () {
             case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind: {
                 for (var k = 0, length_6 = meshAttributeArray.length / stride; k < length_6; ++k) {
                     index = k * stride;
-                    vertexAttributes.push(this._convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
+                    vertexAttributes.push(convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
                 }
                 break;
             }
@@ -2327,13 +2341,14 @@ var _Exporter = /** @class */ (function () {
      * Sets the TRS for each node
      * @param node glTF Node for storing the transformation data
      * @param babylonTransformNode Babylon mesh used as the source for the transformation data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
+    _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode, convertToRightHandedSystem) {
         if (!babylonTransformNode.getPivotPoint().equalsToFloats(0, 0, 0)) {
             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Pivot points are not supported in the glTF serializer");
         }
         if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-            node.translation = this._convertToRightHandedSystem ? _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+            node.translation = convertToRightHandedSystem ? _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
         }
         if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
             node.scale = babylonTransformNode.scaling.asArray();
@@ -2343,7 +2358,7 @@ var _Exporter = /** @class */ (function () {
             rotationQuaternion.multiplyInPlace(babylonTransformNode.rotationQuaternion);
         }
         if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
-            if (this._convertToRightHandedSystem) {
+            if (convertToRightHandedSystem) {
                 _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedQuaternionFromRef(rotationQuaternion);
             }
             node.rotation = rotationQuaternion.normalize().asArray();
@@ -2363,8 +2378,9 @@ var _Exporter = /** @class */ (function () {
      * @param kind Indicates the type of vertices data
      * @param babylonTransformNode The Babylon mesh to get the vertices data from
      * @param binaryWriter The buffer to write the bufferview data to
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.createBufferViewKind = function (kind, babylonTransformNode, binaryWriter, byteStride) {
+    _Exporter.prototype.createBufferViewKind = function (kind, babylonTransformNode, binaryWriter, byteStride, convertToRightHandedSystem) {
         var bufferMesh = babylonTransformNode instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Mesh"] ?
             babylonTransformNode : babylonTransformNode instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["InstancedMesh"] ?
             babylonTransformNode.sourceMesh : null;
@@ -2374,7 +2390,7 @@ var _Exporter = /** @class */ (function () {
                 var byteLength = vertexData.length * 4;
                 var bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                 this._bufferViews.push(bufferView);
-                this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
+                this.writeAttributeData(kind, vertexData, byteStride, binaryWriter, convertToRightHandedSystem);
             }
         }
     };
@@ -2470,8 +2486,10 @@ var _Exporter = /** @class */ (function () {
      * @param mesh glTF Mesh object to store the primitive attribute information
      * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
      * @param binaryWriter Buffer to write the attribute data to
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.setPrimitiveAttributesAsync = function (mesh, babylonTransformNode, binaryWriter) {
+    _Exporter.prototype.setPrimitiveAttributesAsync = function (mesh, babylonTransformNode, binaryWriter, convertToRightHandedSystem) {
+        var _a;
         var promises = [];
         var bufferMesh = null;
         var bufferView;
@@ -2504,7 +2522,7 @@ var _Exporter = /** @class */ (function () {
                     if (attribute.byteStride === 12) {
                         attribute.accessorType = "VEC3" /* VEC3 */;
                     }
-                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride);
+                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
                     attribute.bufferViewIndex = this._bufferViews.length - 1;
                     vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                 }
@@ -2523,8 +2541,8 @@ var _Exporter = /** @class */ (function () {
             }
             if (bufferMesh.subMeshes) {
                 // go through all mesh primitives (submeshes)
-                for (var _a = 0, _b = bufferMesh.subMeshes; _a < _b.length; _a++) {
-                    var submesh = _b[_a];
+                for (var _b = 0, _c = bufferMesh.subMeshes; _b < _c.length; _b++) {
+                    var submesh = _c[_b];
                     var babylonMaterial = submesh.getMaterial() || bufferMesh.getScene().defaultMaterial;
                     var materialIndex = null;
                     if (babylonMaterial) {
@@ -2555,8 +2573,8 @@ var _Exporter = /** @class */ (function () {
                     var glTFMaterial = materialIndex != null ? this._materials[materialIndex] : null;
                     var meshPrimitive = { attributes: {} };
                     this.setPrimitiveMode(meshPrimitive, primitiveMode);
-                    for (var _c = 0, attributeData_2 = attributeData; _c < attributeData_2.length; _c++) {
-                        var attribute = attributeData_2[_c];
+                    for (var _d = 0, attributeData_2 = attributeData; _d < attributeData_2.length; _d++) {
+                        var attribute = attributeData_2[_d];
                         var attributeKind = attribute.kind;
                         if (attributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UVKind || attributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind) {
                             if (glTFMaterial && !this._glTFMaterialExporter._hasTexturesPresent(glTFMaterial)) {
@@ -2572,7 +2590,7 @@ var _Exporter = /** @class */ (function () {
                                 if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                     minMax = { min: null, max: null };
                                     if (attributeKind == babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind) {
-                                        minMax = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                        minMax = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, convertToRightHandedSystem);
                                     }
                                     var accessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                     this._accessors.push(accessor);
@@ -2589,8 +2607,8 @@ var _Exporter = /** @class */ (function () {
                     }
                     if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
                         var sideOrientation = bufferMesh.overrideMaterialSideOrientation !== null ? bufferMesh.overrideMaterialSideOrientation : babylonMaterial.sideOrientation;
-                        // Only reverse the winding if we have a clockwise winding
-                        if (sideOrientation === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation) {
+                        if ((sideOrientation == babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation && this._babylonScene.useRightHandedSystem)
+                            || (sideOrientation == babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation && convertToRightHandedSystem && bufferMesh.overrideMaterialSideOrientation !== ((_a = bufferMesh.material) === null || _a === void 0 ? void 0 : _a.sideOrientation))) {
                             var byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
                             if (byteOffset == null) {
                                 byteOffset = 0;
@@ -2603,15 +2621,15 @@ var _Exporter = /** @class */ (function () {
                                 this.reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
                             }
                             else {
-                                for (var _d = 0, attributeData_3 = attributeData; _d < attributeData_3.length; _d++) {
-                                    var attribute = attributeData_3[_d];
+                                for (var _e = 0, attributeData_3 = attributeData; _e < attributeData_3.length; _e++) {
+                                    var attribute = attributeData_3[_e];
                                     var vertexData = bufferMesh.getVerticesData(attribute.kind);
                                     if (vertexData) {
                                         var byteOffset_1 = this._bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset;
                                         if (!byteOffset_1) {
                                             byteOffset_1 = 0;
                                         }
-                                        this.reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, sideOrientation, attribute.kind, vertexData, byteOffset_1, binaryWriter);
+                                        this.reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, sideOrientation, attribute.kind, vertexData, byteOffset_1, binaryWriter, convertToRightHandedSystem);
                                     }
                                 }
                             }
@@ -2631,6 +2649,38 @@ var _Exporter = /** @class */ (function () {
         });
     };
     /**
+     * Check if the node is used to convert its descendants from a right handed coordinate system to the Babylon scene's coordinate system.
+     * @param node The node to check
+     * @returns True if the node is used to convert its descendants from right-handed to left-handed. False otherwise
+     */
+    _Exporter.prototype.isBabylonCoordinateSystemConvertingNode = function (node) {
+        if (node instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["TransformNode"]) {
+            if (node.name !== "__root__") {
+                return false;
+            }
+            // Transform
+            var matrix = node.getWorldMatrix();
+            var matrixToLeftHanded = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Matrix"].Compose(this._convertToRightHandedSystem ? new babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"](-1, 1, 1) : babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].One(), babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].Identity(), babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].Zero());
+            var matrixProduct = matrix.multiply(matrixToLeftHanded);
+            var matrixIdentity = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Matrix"].IdentityReadOnly;
+            for (var i = 0; i < 16; i++) {
+                if (Math.abs(matrixProduct.m[i] - matrixIdentity.m[i]) > babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Epsilon"]) {
+                    return false;
+                }
+            }
+            // Geometry
+            if ((node instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Mesh"] && node.geometry !== null) ||
+                (node instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["InstancedMesh"] && node.sourceMesh.geometry !== null)) {
+                return false;
+            }
+            if (this._includeCoordinateSystemConversionNodes) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    };
+    /**
      * Creates a glTF scene based on the array of meshes
      * Returns the the total byte offset
      * @param babylonScene Babylon scene to get the mesh data from
@@ -2643,6 +2693,31 @@ var _Exporter = /** @class */ (function () {
         var glTFNode;
         var directDescendents;
         var nodes = Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spreadArrays"])(babylonScene.transformNodes, babylonScene.meshes, babylonScene.lights);
+        var rootNodesToLeftHanded = [];
+        this._convertToRightHandedSystem = !babylonScene.useRightHandedSystem;
+        this._convertToRightHandedSystemMap = {};
+        // Set default values for all nodes
+        babylonScene.rootNodes.forEach(function (rootNode) {
+            _this._convertToRightHandedSystemMap[rootNode.uniqueId] = _this._convertToRightHandedSystem;
+            rootNode.getDescendants(false).forEach(function (descendant) {
+                _this._convertToRightHandedSystemMap[descendant.uniqueId] = _this._convertToRightHandedSystem;
+            });
+        });
+        // Check if root nodes converting to left-handed are present
+        babylonScene.rootNodes.forEach(function (rootNode) {
+            if (_this.isBabylonCoordinateSystemConvertingNode(rootNode)) {
+                rootNodesToLeftHanded.push(rootNode);
+                // Exclude the node from list of nodes to export
+                var indexRootNode = nodes.indexOf(rootNode);
+                if (indexRootNode !== -1) { // should always be true
+                    nodes.splice(indexRootNode, 1);
+                }
+                // Cancel conversion to right handed system
+                rootNode.getDescendants(false).forEach(function (descendant) {
+                    _this._convertToRightHandedSystemMap[descendant.uniqueId] = false;
+                });
+            }
+        });
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, "image/png" /* PNG */, true).then(function () {
             return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then(function (nodeMap) {
                 _this._nodeMap = nodeMap;
@@ -2664,12 +2739,13 @@ var _Exporter = /** @class */ (function () {
                                 glTFNode.extras = babylonNode.metadata.gltf.extras;
                             }
                         }
-                        if (!babylonNode.parent) {
+                        if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
                             if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
                                 babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
                             }
                             else {
-                                if (_this._convertToRightHandedSystem) {
+                                var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                                if (convertToRightHandedSystem) {
                                     if (glTFNode.translation) {
                                         glTFNode.translation[2] *= -1;
                                         glTFNode.translation[0] *= -1;
@@ -2721,7 +2797,8 @@ var _Exporter = /** @class */ (function () {
         var _loop_1 = function (babylonNode) {
             if (!this_1._options.shouldExportNode || this_1._options.shouldExportNode(babylonNode)) {
                 promiseChain = promiseChain.then(function () {
-                    return _this.createNodeAsync(babylonNode, binaryWriter).then(function (node) {
+                    var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                    return _this.createNodeAsync(babylonNode, binaryWriter, convertToRightHandedSystem).then(function (node) {
                         var promise = _this._extensionsPostExportNodeAsync("createNodeAsync", node, babylonNode);
                         if (promise == null) {
                             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Not exporting node " + babylonNode.name);
@@ -2736,7 +2813,7 @@ var _Exporter = /** @class */ (function () {
                                 nodeIndex = _this._nodes.length - 1;
                                 nodeMap[babylonNode.uniqueId] = nodeIndex;
                                 if (!babylonScene.animationGroups.length && babylonNode.animations.length) {
-                                    _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, _this._convertToRightHandedSystem, _this._animationSampleRate);
+                                    _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, convertToRightHandedSystem, _this._animationSampleRate);
                                 }
                             });
                         }
@@ -2762,7 +2839,7 @@ var _Exporter = /** @class */ (function () {
                 }
             });
             if (babylonScene.animationGroups.length) {
-                _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromAnimationGroups(babylonScene, _this._animations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, _this._convertToRightHandedSystem, _this._animationSampleRate);
+                _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromAnimationGroups(babylonScene, _this._animations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, _this._convertToRightHandedSystemMap, _this._animationSampleRate);
             }
             return nodeMap;
         });
@@ -2771,9 +2848,10 @@ var _Exporter = /** @class */ (function () {
      * Creates a glTF node from a Babylon mesh
      * @param babylonMesh Source Babylon mesh
      * @param binaryWriter Buffer for storing geometry data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      * @returns glTF node
      */
-    _Exporter.prototype.createNodeAsync = function (babylonNode, binaryWriter) {
+    _Exporter.prototype.createNodeAsync = function (babylonNode, binaryWriter, convertToRightHandedSystem) {
         var _this = this;
         return Promise.resolve().then(function () {
             // create node to hold translation/rotation/scale and the mesh
@@ -2785,8 +2863,8 @@ var _Exporter = /** @class */ (function () {
             }
             if (babylonNode instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["TransformNode"]) {
                 // Set transformation
-                _this.setNodeTransformation(node, babylonNode);
-                return _this.setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter).then(function () {
+                _this.setNodeTransformation(node, babylonNode, convertToRightHandedSystem);
+                return _this.setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter, convertToRightHandedSystem).then(function () {
                     if (mesh.primitives.length) {
                         _this._meshes.push(mesh);
                         node.mesh = _this._meshes.length - 1;

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


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 35 - 4
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -374,6 +374,10 @@ declare module BABYLON {
          * Begin serialization without waiting for the scene to be ready
          */
         exportWithoutWaitingForScene?: boolean;
+        /**
+         * Indicates if coordinate system swapping root nodes should be included in export
+         */
+        includeCoordinateSystemConversionNodes?: boolean;
     }
     /**
      * Class for generating glTF data from a Babylon scene.
@@ -573,10 +577,17 @@ declare module BABYLON.GLTF2.Exporter {
          */
         private _nodeMap;
         /**
-         * Specifies if the Babylon scene should be converted to right-handed on export
+         * Specifies if the source Babylon scene was left handed, and needed conversion.
          */
         _convertToRightHandedSystem: boolean;
         /**
+         * Specifies if a Babylon node should be converted to right-handed on export
+         */
+        _convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        };
+        _includeCoordinateSystemConversionNodes: boolean;
+        /**
          * Baked animation sample rate
          */
         private _animationSampleRate;
@@ -634,6 +645,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderVertexAttributeDataBasedOnPrimitiveMode;
         /**
@@ -646,6 +658,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFillMode;
         /**
@@ -658,6 +671,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleStripDrawMode;
         /**
@@ -670,6 +684,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFanMode;
         /**
@@ -679,6 +694,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param vertexAttributeKind The vertex attribute type
          * @param meshAttributeArray The vertex attribute data
          * @param binaryWriter The writer containing the binary data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private writeVertexAttributeData;
         /**
@@ -688,8 +704,9 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray Array containing the attribute data
          * @param binaryWriter The buffer to write the binary data to
          * @param indices Used to specify the order of the vertex data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
-        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter): void;
+        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void;
         /**
          * Generates glTF json data
          * @param shouldUseGlb Indicates whether the json should be written for a glb file
@@ -724,6 +741,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Sets the TRS for each node
          * @param node glTF Node for storing the transformation data
          * @param babylonTransformNode Babylon mesh used as the source for the transformation data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setNodeTransformation;
         private getVertexBufferFromMesh;
@@ -732,6 +750,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param kind Indicates the type of vertices data
          * @param babylonTransformNode The Babylon mesh to get the vertices data from
          * @param binaryWriter The buffer to write the bufferview data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private createBufferViewKind;
         /**
@@ -757,9 +776,16 @@ declare module BABYLON.GLTF2.Exporter {
          * @param mesh glTF Mesh object to store the primitive attribute information
          * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
          * @param binaryWriter Buffer to write the attribute data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setPrimitiveAttributesAsync;
         /**
+         * Check if the node is used to convert its descendants from a right handed coordinate system to the Babylon scene's coordinate system.
+         * @param node The node to check
+         * @returns True if the node is used to convert its descendants from right-handed to left-handed. False otherwise
+         */
+        private isBabylonCoordinateSystemConvertingNode;
+        /**
          * Creates a glTF scene based on the array of meshes
          * Returns the the total byte offset
          * @param babylonScene Babylon scene to get the mesh data from
@@ -778,6 +804,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Creates a glTF node from a Babylon mesh
          * @param babylonMesh Source Babylon mesh
          * @param binaryWriter Buffer for storing geometry data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          * @returns glTF node
          */
         private createNodeAsync;
@@ -923,6 +950,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param bufferViews
          * @param accessors
          * @param convertToRightHandedSystem
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromNodeAnimations(babylonNode: Node, runtimeGLTFAnimation: IAnimation, idleGLTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
@@ -937,11 +965,14 @@ declare module BABYLON.GLTF2.Exporter {
          * @param binaryWriter
          * @param bufferViews
          * @param accessors
-         * @param convertToRightHandedSystem
+         * @param convertToRightHandedSystemMap
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromAnimationGroups(babylonScene: Scene, glTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
-        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number): void;
+        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        }, animationSampleRate: number): void;
         private static AddAnimation;
         /**
          * Create a baked animation

+ 126 - 48
dist/preview release/serializers/babylonjs.serializers.js

@@ -610,8 +610,9 @@ var KHR_lights_punctual = /** @class */ (function () {
                 }
                 else {
                     var lightPosition = babylonLight.position.clone();
+                    var convertToRightHandedSystem = _this._exporter._convertToRightHandedSystemMap[babylonNode.uniqueId];
                     if (!lightPosition.equals(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Vector3"].Zero())) {
-                        if (_this._exporter._convertToRightHandedSystem) {
+                        if (convertToRightHandedSystem) {
                             _glTFUtilities__WEBPACK_IMPORTED_MODULE_2__["_GLTFUtilities"]._GetRightHandedPositionVector3FromRef(lightPosition);
                         }
                         node.translation = lightPosition.asArray();
@@ -622,7 +623,7 @@ var KHR_lights_punctual = /** @class */ (function () {
                         var len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
                         var pitch = -Math.atan2(localAxis.y, len);
                         var lightRotationQuaternion = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].RotationYawPitchRoll(yaw, pitch, 0);
-                        if (_this._exporter._convertToRightHandedSystem) {
+                        if (convertToRightHandedSystem) {
                             _glTFUtilities__WEBPACK_IMPORTED_MODULE_2__["_GLTFUtilities"]._GetRightHandedQuaternionFromRef(lightRotationQuaternion);
                         }
                         if (!lightRotationQuaternion.equals(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].Identity())) {
@@ -1106,6 +1107,7 @@ var _GLTFAnimation = /** @class */ (function () {
      * @param bufferViews
      * @param accessors
      * @param convertToRightHandedSystem
+     * @param animationSampleRate
      */
     _GLTFAnimation._CreateNodeAnimationFromNodeAnimations = function (babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, nodes, binaryWriter, bufferViews, accessors, convertToRightHandedSystem, animationSampleRate) {
         var glTFAnimation;
@@ -1139,9 +1141,10 @@ var _GLTFAnimation = /** @class */ (function () {
      * @param binaryWriter
      * @param bufferViews
      * @param accessors
-     * @param convertToRightHandedSystem
+     * @param convertToRightHandedSystemMap
+     * @param animationSampleRate
      */
-    _GLTFAnimation._CreateNodeAnimationFromAnimationGroups = function (babylonScene, glTFAnimations, nodeMap, nodes, binaryWriter, bufferViews, accessors, convertToRightHandedSystem, animationSampleRate) {
+    _GLTFAnimation._CreateNodeAnimationFromAnimationGroups = function (babylonScene, glTFAnimations, nodeMap, nodes, binaryWriter, bufferViews, accessors, convertToRightHandedSystemMap, animationSampleRate) {
         var glTFAnimation;
         if (babylonScene.animationGroups) {
             var animationGroups = babylonScene.animationGroups;
@@ -1160,6 +1163,7 @@ var _GLTFAnimation = /** @class */ (function () {
                         var animationInfo = _GLTFAnimation._DeduceAnimationInfo(targetAnimation.animation);
                         if (animationInfo) {
                             var babylonTransformNode = target instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["TransformNode"] ? target : target[0];
+                            var convertToRightHandedSystem = convertToRightHandedSystemMap[babylonTransformNode.uniqueId];
                             _GLTFAnimation.AddAnimation("" + animation.name, glTFAnimation, babylonTransformNode, animation, animationInfo.dataAccessorType, animationInfo.animationChannelTargetPath, nodeMap, binaryWriter, bufferViews, accessors, convertToRightHandedSystem, animationInfo.useQuaternion, animationSampleRate);
                         }
                     }
@@ -1752,6 +1756,10 @@ var _Exporter = /** @class */ (function () {
      * @param options Options to modify the behavior of the exporter
      */
     function _Exporter(babylonScene, options) {
+        /*
+        * Specifies if root Babylon empty nodes that act as a coordinate space transform should be included in export
+        */
+        this._includeCoordinateSystemConversionNodes = false;
         this._extensions = {};
         this._glTF = {
             asset: { generator: "BabylonJS", version: "2.0" }
@@ -1769,9 +1777,9 @@ var _Exporter = /** @class */ (function () {
         this._samplers = [];
         this._animations = [];
         this._imageData = {};
-        this._convertToRightHandedSystem = !this._babylonScene.useRightHandedSystem;
         this._options = options || {};
         this._animationSampleRate = options && options.animationSampleRate ? options.animationSampleRate : 1 / 60;
+        this._includeCoordinateSystemConversionNodes = options && options.includeCoordinateSystemConversionNodes ? true : false;
         this._glTFMaterialExporter = new _glTFMaterialExporter__WEBPACK_IMPORTED_MODULE_2__["_GLTFMaterialExporter"](this);
         this._loadExtensions();
     }
@@ -1962,20 +1970,21 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderVertexAttributeDataBasedOnPrimitiveMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
-        if (this._convertToRightHandedSystem && sideOrientation === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation) {
+    _Exporter.prototype.reorderVertexAttributeDataBasedOnPrimitiveMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
+        if (convertToRightHandedSystem && sideOrientation === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation) {
             switch (primitiveMode) {
                 case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].TriangleFillMode: {
-                    this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
                 case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].TriangleStripDrawMode: {
-                    this.reorderTriangleStripDrawMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleStripDrawMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
                 case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].TriangleFanDrawMode: {
-                    this.reorderTriangleFanMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleFanMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
             }
@@ -1991,8 +2000,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderTriangleFillMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
+    _Exporter.prototype.reorderTriangleFillMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
         var vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
         if (vertexBuffer) {
             var stride = vertexBuffer.byteStride / babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(vertexBuffer.type);
@@ -2053,7 +2063,7 @@ var _Exporter = /** @class */ (function () {
                         babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error("Unsupported Vertex Buffer type: " + vertexBufferKind);
                     }
                 }
-                this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter);
+                this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
             }
         }
         else {
@@ -2070,8 +2080,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderTriangleStripDrawMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
+    _Exporter.prototype.reorderTriangleStripDrawMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
         var vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
         if (vertexBuffer) {
             var stride = vertexBuffer.byteStride / babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(vertexBuffer.type);
@@ -2111,7 +2122,7 @@ var _Exporter = /** @class */ (function () {
                     babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error("Unsupported Vertex Buffer type: " + vertexBufferKind);
                 }
             }
-            this.writeVertexAttributeData(vertexData, byteOffset + 12, vertexBufferKind, meshAttributeArray, binaryWriter);
+            this.writeVertexAttributeData(vertexData, byteOffset + 12, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
         }
         else {
             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("reorderTriangleStripDrawMode: Vertex buffer kind " + vertexBufferKind + " not present!");
@@ -2127,8 +2138,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.reorderTriangleFanMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
+    _Exporter.prototype.reorderTriangleFanMode = function (submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem) {
         var vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
         if (vertexBuffer) {
             var stride = vertexBuffer.byteStride / babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].GetTypeByteLength(vertexBuffer.type);
@@ -2170,7 +2182,7 @@ var _Exporter = /** @class */ (function () {
                     babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Error("Unsupported Vertex Buffer type: " + vertexBufferKind);
                 }
             }
-            this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter);
+            this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
         }
         else {
             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("reorderTriangleFanMode: Vertex buffer kind " + vertexBufferKind + " not present!");
@@ -2183,11 +2195,12 @@ var _Exporter = /** @class */ (function () {
      * @param vertexAttributeKind The vertex attribute type
      * @param meshAttributeArray The vertex attribute data
      * @param binaryWriter The writer containing the binary data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.writeVertexAttributeData = function (vertices, byteOffset, vertexAttributeKind, meshAttributeArray, binaryWriter) {
+    _Exporter.prototype.writeVertexAttributeData = function (vertices, byteOffset, vertexAttributeKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem) {
         for (var _i = 0, vertices_1 = vertices; _i < vertices_1.length; _i++) {
             var vertex = vertices_1[_i];
-            if (this._convertToRightHandedSystem && !(vertexAttributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind) && !(vertex instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector2"])) {
+            if (convertToRightHandedSystem && !(vertexAttributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind) && !(vertex instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector2"])) {
                 if (vertex instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"]) {
                     if (vertexAttributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].NormalKind) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedNormalVector3FromRef(vertex);
@@ -2223,8 +2236,9 @@ var _Exporter = /** @class */ (function () {
      * @param meshAttributeArray Array containing the attribute data
      * @param binaryWriter The buffer to write the binary data to
      * @param indices Used to specify the order of the vertex data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, byteStride, binaryWriter) {
+    _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, byteStride, binaryWriter, convertToRightHandedSystem) {
         var stride = byteStride / 4;
         var vertexAttributes = [];
         var index;
@@ -2233,7 +2247,7 @@ var _Exporter = /** @class */ (function () {
                 for (var k = 0, length_2 = meshAttributeArray.length / stride; k < length_2; ++k) {
                     index = k * stride;
                     var vertexData = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedPositionVector3FromRef(vertexData);
                     }
                     vertexAttributes.push(vertexData.asArray());
@@ -2244,7 +2258,7 @@ var _Exporter = /** @class */ (function () {
                 for (var k = 0, length_3 = meshAttributeArray.length / stride; k < length_3; ++k) {
                     index = k * stride;
                     var vertexData = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedNormalVector3FromRef(vertexData);
                     }
                     vertexData.normalize();
@@ -2256,7 +2270,7 @@ var _Exporter = /** @class */ (function () {
                 for (var k = 0, length_4 = meshAttributeArray.length / stride; k < length_4; ++k) {
                     index = k * stride;
                     var vertexData = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedVector4FromRef(vertexData);
                     }
                     _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._NormalizeTangentFromRef(vertexData);
@@ -2276,7 +2290,7 @@ var _Exporter = /** @class */ (function () {
             case babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind: {
                 for (var k = 0, length_6 = meshAttributeArray.length / stride; k < length_6; ++k) {
                     index = k * stride;
-                    vertexAttributes.push(this._convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
+                    vertexAttributes.push(convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
                 }
                 break;
             }
@@ -2505,13 +2519,14 @@ var _Exporter = /** @class */ (function () {
      * Sets the TRS for each node
      * @param node glTF Node for storing the transformation data
      * @param babylonTransformNode Babylon mesh used as the source for the transformation data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
+    _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode, convertToRightHandedSystem) {
         if (!babylonTransformNode.getPivotPoint().equalsToFloats(0, 0, 0)) {
             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Pivot points are not supported in the glTF serializer");
         }
         if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-            node.translation = this._convertToRightHandedSystem ? _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+            node.translation = convertToRightHandedSystem ? _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
         }
         if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
             node.scale = babylonTransformNode.scaling.asArray();
@@ -2521,7 +2536,7 @@ var _Exporter = /** @class */ (function () {
             rotationQuaternion.multiplyInPlace(babylonTransformNode.rotationQuaternion);
         }
         if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
-            if (this._convertToRightHandedSystem) {
+            if (convertToRightHandedSystem) {
                 _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._GetRightHandedQuaternionFromRef(rotationQuaternion);
             }
             node.rotation = rotationQuaternion.normalize().asArray();
@@ -2541,8 +2556,9 @@ var _Exporter = /** @class */ (function () {
      * @param kind Indicates the type of vertices data
      * @param babylonTransformNode The Babylon mesh to get the vertices data from
      * @param binaryWriter The buffer to write the bufferview data to
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.createBufferViewKind = function (kind, babylonTransformNode, binaryWriter, byteStride) {
+    _Exporter.prototype.createBufferViewKind = function (kind, babylonTransformNode, binaryWriter, byteStride, convertToRightHandedSystem) {
         var bufferMesh = babylonTransformNode instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Mesh"] ?
             babylonTransformNode : babylonTransformNode instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["InstancedMesh"] ?
             babylonTransformNode.sourceMesh : null;
@@ -2552,7 +2568,7 @@ var _Exporter = /** @class */ (function () {
                 var byteLength = vertexData.length * 4;
                 var bufferView = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                 this._bufferViews.push(bufferView);
-                this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
+                this.writeAttributeData(kind, vertexData, byteStride, binaryWriter, convertToRightHandedSystem);
             }
         }
     };
@@ -2648,8 +2664,10 @@ var _Exporter = /** @class */ (function () {
      * @param mesh glTF Mesh object to store the primitive attribute information
      * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
      * @param binaryWriter Buffer to write the attribute data to
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    _Exporter.prototype.setPrimitiveAttributesAsync = function (mesh, babylonTransformNode, binaryWriter) {
+    _Exporter.prototype.setPrimitiveAttributesAsync = function (mesh, babylonTransformNode, binaryWriter, convertToRightHandedSystem) {
+        var _a;
         var promises = [];
         var bufferMesh = null;
         var bufferView;
@@ -2682,7 +2700,7 @@ var _Exporter = /** @class */ (function () {
                     if (attribute.byteStride === 12) {
                         attribute.accessorType = "VEC3" /* VEC3 */;
                     }
-                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride);
+                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
                     attribute.bufferViewIndex = this._bufferViews.length - 1;
                     vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                 }
@@ -2701,8 +2719,8 @@ var _Exporter = /** @class */ (function () {
             }
             if (bufferMesh.subMeshes) {
                 // go through all mesh primitives (submeshes)
-                for (var _a = 0, _b = bufferMesh.subMeshes; _a < _b.length; _a++) {
-                    var submesh = _b[_a];
+                for (var _b = 0, _c = bufferMesh.subMeshes; _b < _c.length; _b++) {
+                    var submesh = _c[_b];
                     var babylonMaterial = submesh.getMaterial() || bufferMesh.getScene().defaultMaterial;
                     var materialIndex = null;
                     if (babylonMaterial) {
@@ -2733,8 +2751,8 @@ var _Exporter = /** @class */ (function () {
                     var glTFMaterial = materialIndex != null ? this._materials[materialIndex] : null;
                     var meshPrimitive = { attributes: {} };
                     this.setPrimitiveMode(meshPrimitive, primitiveMode);
-                    for (var _c = 0, attributeData_2 = attributeData; _c < attributeData_2.length; _c++) {
-                        var attribute = attributeData_2[_c];
+                    for (var _d = 0, attributeData_2 = attributeData; _d < attributeData_2.length; _d++) {
+                        var attribute = attributeData_2[_d];
                         var attributeKind = attribute.kind;
                         if (attributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UVKind || attributeKind === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].UV2Kind) {
                             if (glTFMaterial && !this._glTFMaterialExporter._hasTexturesPresent(glTFMaterial)) {
@@ -2750,7 +2768,7 @@ var _Exporter = /** @class */ (function () {
                                 if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                     minMax = { min: null, max: null };
                                     if (attributeKind == babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].PositionKind) {
-                                        minMax = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                        minMax = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, convertToRightHandedSystem);
                                     }
                                     var accessor = _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__["_GLTFUtilities"]._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                     this._accessors.push(accessor);
@@ -2767,8 +2785,8 @@ var _Exporter = /** @class */ (function () {
                     }
                     if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
                         var sideOrientation = bufferMesh.overrideMaterialSideOrientation !== null ? bufferMesh.overrideMaterialSideOrientation : babylonMaterial.sideOrientation;
-                        // Only reverse the winding if we have a clockwise winding
-                        if (sideOrientation === babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation) {
+                        if ((sideOrientation == babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation && this._babylonScene.useRightHandedSystem)
+                            || (sideOrientation == babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Material"].ClockWiseSideOrientation && convertToRightHandedSystem && bufferMesh.overrideMaterialSideOrientation !== ((_a = bufferMesh.material) === null || _a === void 0 ? void 0 : _a.sideOrientation))) {
                             var byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
                             if (byteOffset == null) {
                                 byteOffset = 0;
@@ -2781,15 +2799,15 @@ var _Exporter = /** @class */ (function () {
                                 this.reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
                             }
                             else {
-                                for (var _d = 0, attributeData_3 = attributeData; _d < attributeData_3.length; _d++) {
-                                    var attribute = attributeData_3[_d];
+                                for (var _e = 0, attributeData_3 = attributeData; _e < attributeData_3.length; _e++) {
+                                    var attribute = attributeData_3[_e];
                                     var vertexData = bufferMesh.getVerticesData(attribute.kind);
                                     if (vertexData) {
                                         var byteOffset_1 = this._bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset;
                                         if (!byteOffset_1) {
                                             byteOffset_1 = 0;
                                         }
-                                        this.reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, sideOrientation, attribute.kind, vertexData, byteOffset_1, binaryWriter);
+                                        this.reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, sideOrientation, attribute.kind, vertexData, byteOffset_1, binaryWriter, convertToRightHandedSystem);
                                     }
                                 }
                             }
@@ -2809,6 +2827,38 @@ var _Exporter = /** @class */ (function () {
         });
     };
     /**
+     * Check if the node is used to convert its descendants from a right handed coordinate system to the Babylon scene's coordinate system.
+     * @param node The node to check
+     * @returns True if the node is used to convert its descendants from right-handed to left-handed. False otherwise
+     */
+    _Exporter.prototype.isBabylonCoordinateSystemConvertingNode = function (node) {
+        if (node instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["TransformNode"]) {
+            if (node.name !== "__root__") {
+                return false;
+            }
+            // Transform
+            var matrix = node.getWorldMatrix();
+            var matrixToLeftHanded = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Matrix"].Compose(this._convertToRightHandedSystem ? new babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"](-1, 1, 1) : babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].One(), babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Quaternion"].Identity(), babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Vector3"].Zero());
+            var matrixProduct = matrix.multiply(matrixToLeftHanded);
+            var matrixIdentity = babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Matrix"].IdentityReadOnly;
+            for (var i = 0; i < 16; i++) {
+                if (Math.abs(matrixProduct.m[i] - matrixIdentity.m[i]) > babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Epsilon"]) {
+                    return false;
+                }
+            }
+            // Geometry
+            if ((node instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Mesh"] && node.geometry !== null) ||
+                (node instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["InstancedMesh"] && node.sourceMesh.geometry !== null)) {
+                return false;
+            }
+            if (this._includeCoordinateSystemConversionNodes) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    };
+    /**
      * Creates a glTF scene based on the array of meshes
      * Returns the the total byte offset
      * @param babylonScene Babylon scene to get the mesh data from
@@ -2821,6 +2871,31 @@ var _Exporter = /** @class */ (function () {
         var glTFNode;
         var directDescendents;
         var nodes = Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spreadArrays"])(babylonScene.transformNodes, babylonScene.meshes, babylonScene.lights);
+        var rootNodesToLeftHanded = [];
+        this._convertToRightHandedSystem = !babylonScene.useRightHandedSystem;
+        this._convertToRightHandedSystemMap = {};
+        // Set default values for all nodes
+        babylonScene.rootNodes.forEach(function (rootNode) {
+            _this._convertToRightHandedSystemMap[rootNode.uniqueId] = _this._convertToRightHandedSystem;
+            rootNode.getDescendants(false).forEach(function (descendant) {
+                _this._convertToRightHandedSystemMap[descendant.uniqueId] = _this._convertToRightHandedSystem;
+            });
+        });
+        // Check if root nodes converting to left-handed are present
+        babylonScene.rootNodes.forEach(function (rootNode) {
+            if (_this.isBabylonCoordinateSystemConvertingNode(rootNode)) {
+                rootNodesToLeftHanded.push(rootNode);
+                // Exclude the node from list of nodes to export
+                var indexRootNode = nodes.indexOf(rootNode);
+                if (indexRootNode !== -1) { // should always be true
+                    nodes.splice(indexRootNode, 1);
+                }
+                // Cancel conversion to right handed system
+                rootNode.getDescendants(false).forEach(function (descendant) {
+                    _this._convertToRightHandedSystemMap[descendant.uniqueId] = false;
+                });
+            }
+        });
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, "image/png" /* PNG */, true).then(function () {
             return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then(function (nodeMap) {
                 _this._nodeMap = nodeMap;
@@ -2842,12 +2917,13 @@ var _Exporter = /** @class */ (function () {
                                 glTFNode.extras = babylonNode.metadata.gltf.extras;
                             }
                         }
-                        if (!babylonNode.parent) {
+                        if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
                             if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
                                 babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
                             }
                             else {
-                                if (_this._convertToRightHandedSystem) {
+                                var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                                if (convertToRightHandedSystem) {
                                     if (glTFNode.translation) {
                                         glTFNode.translation[2] *= -1;
                                         glTFNode.translation[0] *= -1;
@@ -2899,7 +2975,8 @@ var _Exporter = /** @class */ (function () {
         var _loop_1 = function (babylonNode) {
             if (!this_1._options.shouldExportNode || this_1._options.shouldExportNode(babylonNode)) {
                 promiseChain = promiseChain.then(function () {
-                    return _this.createNodeAsync(babylonNode, binaryWriter).then(function (node) {
+                    var convertToRightHandedSystem = _this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                    return _this.createNodeAsync(babylonNode, binaryWriter, convertToRightHandedSystem).then(function (node) {
                         var promise = _this._extensionsPostExportNodeAsync("createNodeAsync", node, babylonNode);
                         if (promise == null) {
                             babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["Tools"].Warn("Not exporting node " + babylonNode.name);
@@ -2914,7 +2991,7 @@ var _Exporter = /** @class */ (function () {
                                 nodeIndex = _this._nodes.length - 1;
                                 nodeMap[babylonNode.uniqueId] = nodeIndex;
                                 if (!babylonScene.animationGroups.length && babylonNode.animations.length) {
-                                    _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, _this._convertToRightHandedSystem, _this._animationSampleRate);
+                                    _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, convertToRightHandedSystem, _this._animationSampleRate);
                                 }
                             });
                         }
@@ -2940,7 +3017,7 @@ var _Exporter = /** @class */ (function () {
                 }
             });
             if (babylonScene.animationGroups.length) {
-                _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromAnimationGroups(babylonScene, _this._animations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, _this._convertToRightHandedSystem, _this._animationSampleRate);
+                _glTFAnimation__WEBPACK_IMPORTED_MODULE_5__["_GLTFAnimation"]._CreateNodeAnimationFromAnimationGroups(babylonScene, _this._animations, nodeMap, _this._nodes, binaryWriter, _this._bufferViews, _this._accessors, _this._convertToRightHandedSystemMap, _this._animationSampleRate);
             }
             return nodeMap;
         });
@@ -2949,9 +3026,10 @@ var _Exporter = /** @class */ (function () {
      * Creates a glTF node from a Babylon mesh
      * @param babylonMesh Source Babylon mesh
      * @param binaryWriter Buffer for storing geometry data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      * @returns glTF node
      */
-    _Exporter.prototype.createNodeAsync = function (babylonNode, binaryWriter) {
+    _Exporter.prototype.createNodeAsync = function (babylonNode, binaryWriter, convertToRightHandedSystem) {
         var _this = this;
         return Promise.resolve().then(function () {
             // create node to hold translation/rotation/scale and the mesh
@@ -2963,8 +3041,8 @@ var _Exporter = /** @class */ (function () {
             }
             if (babylonNode instanceof babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__["TransformNode"]) {
                 // Set transformation
-                _this.setNodeTransformation(node, babylonNode);
-                return _this.setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter).then(function () {
+                _this.setNodeTransformation(node, babylonNode, convertToRightHandedSystem);
+                return _this.setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter, convertToRightHandedSystem).then(function () {
                     if (mesh.primitives.length) {
                         _this._meshes.push(mesh);
                         node.mesh = _this._meshes.length - 1;

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


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 70 - 8
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -398,6 +398,10 @@ declare module "babylonjs-serializers/glTF/2.0/glTFSerializer" {
          * Begin serialization without waiting for the scene to be ready
          */
         exportWithoutWaitingForScene?: boolean;
+        /**
+         * Indicates if coordinate system swapping root nodes should be included in export
+         */
+        includeCoordinateSystemConversionNodes?: boolean;
     }
     /**
      * Class for generating glTF data from a Babylon scene.
@@ -614,10 +618,17 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          */
         private _nodeMap;
         /**
-         * Specifies if the Babylon scene should be converted to right-handed on export
+         * Specifies if the source Babylon scene was left handed, and needed conversion.
          */
         _convertToRightHandedSystem: boolean;
         /**
+         * Specifies if a Babylon node should be converted to right-handed on export
+         */
+        _convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        };
+        _includeCoordinateSystemConversionNodes: boolean;
+        /**
          * Baked animation sample rate
          */
         private _animationSampleRate;
@@ -675,6 +686,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderVertexAttributeDataBasedOnPrimitiveMode;
         /**
@@ -687,6 +699,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFillMode;
         /**
@@ -699,6 +712,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleStripDrawMode;
         /**
@@ -711,6 +725,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFanMode;
         /**
@@ -720,6 +735,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param vertexAttributeKind The vertex attribute type
          * @param meshAttributeArray The vertex attribute data
          * @param binaryWriter The writer containing the binary data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private writeVertexAttributeData;
         /**
@@ -729,8 +745,9 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param meshAttributeArray Array containing the attribute data
          * @param binaryWriter The buffer to write the binary data to
          * @param indices Used to specify the order of the vertex data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
-        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter): void;
+        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void;
         /**
          * Generates glTF json data
          * @param shouldUseGlb Indicates whether the json should be written for a glb file
@@ -765,6 +782,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * Sets the TRS for each node
          * @param node glTF Node for storing the transformation data
          * @param babylonTransformNode Babylon mesh used as the source for the transformation data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setNodeTransformation;
         private getVertexBufferFromMesh;
@@ -773,6 +791,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param kind Indicates the type of vertices data
          * @param babylonTransformNode The Babylon mesh to get the vertices data from
          * @param binaryWriter The buffer to write the bufferview data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private createBufferViewKind;
         /**
@@ -798,9 +817,16 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * @param mesh glTF Mesh object to store the primitive attribute information
          * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
          * @param binaryWriter Buffer to write the attribute data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setPrimitiveAttributesAsync;
         /**
+         * Check if the node is used to convert its descendants from a right handed coordinate system to the Babylon scene's coordinate system.
+         * @param node The node to check
+         * @returns True if the node is used to convert its descendants from right-handed to left-handed. False otherwise
+         */
+        private isBabylonCoordinateSystemConvertingNode;
+        /**
          * Creates a glTF scene based on the array of meshes
          * Returns the the total byte offset
          * @param babylonScene Babylon scene to get the mesh data from
@@ -819,6 +845,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * Creates a glTF node from a Babylon mesh
          * @param babylonMesh Source Babylon mesh
          * @param binaryWriter Buffer for storing geometry data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          * @returns glTF node
          */
         private createNodeAsync;
@@ -971,6 +998,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFAnimation" {
          * @param bufferViews
          * @param accessors
          * @param convertToRightHandedSystem
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromNodeAnimations(babylonNode: Node, runtimeGLTFAnimation: IAnimation, idleGLTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
@@ -985,11 +1013,14 @@ declare module "babylonjs-serializers/glTF/2.0/glTFAnimation" {
          * @param binaryWriter
          * @param bufferViews
          * @param accessors
-         * @param convertToRightHandedSystem
+         * @param convertToRightHandedSystemMap
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromAnimationGroups(babylonScene: Scene, glTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
-        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number): void;
+        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        }, animationSampleRate: number): void;
         private static AddAnimation;
         /**
          * Create a baked animation
@@ -1621,6 +1652,10 @@ declare module BABYLON {
          * Begin serialization without waiting for the scene to be ready
          */
         exportWithoutWaitingForScene?: boolean;
+        /**
+         * Indicates if coordinate system swapping root nodes should be included in export
+         */
+        includeCoordinateSystemConversionNodes?: boolean;
     }
     /**
      * Class for generating glTF data from a Babylon scene.
@@ -1820,10 +1855,17 @@ declare module BABYLON.GLTF2.Exporter {
          */
         private _nodeMap;
         /**
-         * Specifies if the Babylon scene should be converted to right-handed on export
+         * Specifies if the source Babylon scene was left handed, and needed conversion.
          */
         _convertToRightHandedSystem: boolean;
         /**
+         * Specifies if a Babylon node should be converted to right-handed on export
+         */
+        _convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        };
+        _includeCoordinateSystemConversionNodes: boolean;
+        /**
          * Baked animation sample rate
          */
         private _animationSampleRate;
@@ -1881,6 +1923,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderVertexAttributeDataBasedOnPrimitiveMode;
         /**
@@ -1893,6 +1936,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFillMode;
         /**
@@ -1905,6 +1949,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleStripDrawMode;
         /**
@@ -1917,6 +1962,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray The vertex attribute data
          * @param byteOffset The offset to the binary data
          * @param binaryWriter The binary data for the glTF file
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private reorderTriangleFanMode;
         /**
@@ -1926,6 +1972,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param vertexAttributeKind The vertex attribute type
          * @param meshAttributeArray The vertex attribute data
          * @param binaryWriter The writer containing the binary data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private writeVertexAttributeData;
         /**
@@ -1935,8 +1982,9 @@ declare module BABYLON.GLTF2.Exporter {
          * @param meshAttributeArray Array containing the attribute data
          * @param binaryWriter The buffer to write the binary data to
          * @param indices Used to specify the order of the vertex data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
-        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter): void;
+        writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void;
         /**
          * Generates glTF json data
          * @param shouldUseGlb Indicates whether the json should be written for a glb file
@@ -1971,6 +2019,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Sets the TRS for each node
          * @param node glTF Node for storing the transformation data
          * @param babylonTransformNode Babylon mesh used as the source for the transformation data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setNodeTransformation;
         private getVertexBufferFromMesh;
@@ -1979,6 +2028,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param kind Indicates the type of vertices data
          * @param babylonTransformNode The Babylon mesh to get the vertices data from
          * @param binaryWriter The buffer to write the bufferview data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private createBufferViewKind;
         /**
@@ -2004,9 +2054,16 @@ declare module BABYLON.GLTF2.Exporter {
          * @param mesh glTF Mesh object to store the primitive attribute information
          * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
          * @param binaryWriter Buffer to write the attribute data to
+         * @param convertToRightHandedSystem Converts the values to right-handed
          */
         private setPrimitiveAttributesAsync;
         /**
+         * Check if the node is used to convert its descendants from a right handed coordinate system to the Babylon scene's coordinate system.
+         * @param node The node to check
+         * @returns True if the node is used to convert its descendants from right-handed to left-handed. False otherwise
+         */
+        private isBabylonCoordinateSystemConvertingNode;
+        /**
          * Creates a glTF scene based on the array of meshes
          * Returns the the total byte offset
          * @param babylonScene Babylon scene to get the mesh data from
@@ -2025,6 +2082,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Creates a glTF node from a Babylon mesh
          * @param babylonMesh Source Babylon mesh
          * @param binaryWriter Buffer for storing geometry data
+         * @param convertToRightHandedSystem Converts the values to right-handed
          * @returns glTF node
          */
         private createNodeAsync;
@@ -2170,6 +2228,7 @@ declare module BABYLON.GLTF2.Exporter {
          * @param bufferViews
          * @param accessors
          * @param convertToRightHandedSystem
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromNodeAnimations(babylonNode: Node, runtimeGLTFAnimation: IAnimation, idleGLTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
@@ -2184,11 +2243,14 @@ declare module BABYLON.GLTF2.Exporter {
          * @param binaryWriter
          * @param bufferViews
          * @param accessors
-         * @param convertToRightHandedSystem
+         * @param convertToRightHandedSystemMap
+         * @param animationSampleRate
          */
         static _CreateNodeAnimationFromAnimationGroups(babylonScene: Scene, glTFAnimations: IAnimation[], nodeMap: {
             [key: number]: number;
-        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number): void;
+        }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystemMap: {
+            [nodeId: number]: boolean;
+        }, animationSampleRate: number): void;
         private static AddAnimation;
         /**
          * Create a baked animation

+ 34 - 12
dist/preview release/viewer/babylon.module.d.ts

@@ -10148,12 +10148,12 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         intersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
         /**
-     * Intersects the sprites with a ray
-     * @param ray defines the ray to intersect with
-     * @param camera defines the current active camera
-     * @param predicate defines a predicate used to select candidate sprites
-     * @returns null if no hit or a PickingInfo array
-     */
+         * Intersects the sprites with a ray
+         * @param ray defines the ray to intersect with
+         * @param camera defines the current active camera
+         * @param predicate defines a predicate used to select candidate sprites
+         * @returns null if no hit or a PickingInfo array
+         */
         multiIntersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean): Nullable<PickingInfo[]>;
         /**
          * Renders the list of sprites on screen.
@@ -10212,6 +10212,14 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         get texture(): Texture;
         set texture(value: Texture);
+        private _blendMode;
+        /**
+         * Blend mode use to render the particle, it can be any of
+         * the static Constants.ALPHA_x properties provided in this class.
+         * Default value is Constants.ALPHA_COMBINE
+         */
+        get blendMode(): number;
+        set blendMode(blendMode: number);
         /**
          * Creates a new sprite manager
          * @param name defines the manager's name
@@ -34832,6 +34840,9 @@ declare module "babylonjs/Engines/engine" {
          * @param buffer defines the webGL buffer to delete
          */
         deleteInstancesBuffer(buffer: WebGLBuffer): void;
+        private _clientWaitAsync;
+        /** @hidden */
+        _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView): Promise<ArrayBufferView> | null;
         /** @hidden */
         _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex?: number, level?: number, buffer?: Nullable<ArrayBufferView>): ArrayBufferView;
         dispose(): void;
@@ -83285,12 +83296,12 @@ declare module BABYLON {
          */
         intersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
         /**
-     * Intersects the sprites with a ray
-     * @param ray defines the ray to intersect with
-     * @param camera defines the current active camera
-     * @param predicate defines a predicate used to select candidate sprites
-     * @returns null if no hit or a PickingInfo array
-     */
+         * Intersects the sprites with a ray
+         * @param ray defines the ray to intersect with
+         * @param camera defines the current active camera
+         * @param predicate defines a predicate used to select candidate sprites
+         * @returns null if no hit or a PickingInfo array
+         */
         multiIntersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean): Nullable<PickingInfo[]>;
         /**
          * Renders the list of sprites on screen.
@@ -83349,6 +83360,14 @@ declare module BABYLON {
          */
         get texture(): Texture;
         set texture(value: Texture);
+        private _blendMode;
+        /**
+         * Blend mode use to render the particle, it can be any of
+         * the static Constants.ALPHA_x properties provided in this class.
+         * Default value is Constants.ALPHA_COMBINE
+         */
+        get blendMode(): number;
+        set blendMode(blendMode: number);
         /**
          * Creates a new sprite manager
          * @param name defines the manager's name
@@ -107150,6 +107169,9 @@ declare module BABYLON {
          * @param buffer defines the webGL buffer to delete
          */
         deleteInstancesBuffer(buffer: WebGLBuffer): void;
+        private _clientWaitAsync;
+        /** @hidden */
+        _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView): Promise<ArrayBufferView> | null;
         /** @hidden */
         _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex?: number, level?: number, buffer?: Nullable<ArrayBufferView>): ArrayBufferView;
         dispose(): void;

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


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


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

@@ -226,7 +226,7 @@
 - Added `Button.delegatePickingToChildren` to let buttons delegate hit testing to embedded controls ([Deltakosh](https://github.com/deltakosh/))
 - Added `Container.maxLayoutCycle` and `Container.logLayoutCycleErrors` to get more control over layout cycles ([Deltakosh](https://github.com/deltakosh/))
 - Added `StackPanel.ignoreLayoutWarnings` to disable console warnings when controls with percentage size are added to a StackPanel ([Deltakosh](https://github.com/deltakosh/))
-- Added `_getSVGAttribs` functionality for loading multiple svg icons from an external svg file via icon id. Fixed bug for Chrome. Strip icon id from image url for firefox.([lockphase](https://github.com/lockphase/))
+- Added `_getSVGAttribs` functionality for loading multiple svg icons from an external svg file via icon id. Fixed bug for Chrome. Strip icon id from image url for firefox. ([lockphase](https://github.com/lockphase/))
 - Scroll Viewer extended to include the use of images in the scroll bars([JohnK](https://github.com/BabylonJSGuide/))
 - Added `ScrollViewer.freezeControls` property to speed up rendering ([Popov72](https://github.com/Popov72))
 - Added `ImageScrollBar.num90RotationInVerticalMode` property to let the user rotate the pictures when in vertical mode ([Popov72](https://github.com/Popov72))
@@ -334,6 +334,8 @@
 - Fix for bug where the light gizmo causes lights to flip orientation ([#7603](https://github.com/BabylonJS/Babylon.js/issues/7603)) ([drigax](https://github.com/drigax))
 - Fix for bug where directional lights are inverted when using a right handed scene coordinate system. ([drigax](https://github.com/drigax))
 - Fix subSurface parameters not copied in the PBR clone methods ([Popov72](https://github.com/Popov72))
+- Fix for bug where round-tripped glTF imported scenes are encapsulated in a second root node ([#6349](https://github.com/BabylonJS/Babylon.js/issues/6349))([drigax](https://github.com/drigax) & [noalak](https://github.com/noalak))
+- Fix `HDRCubeTexture` construction, `generateHarmonics` was not properly taken into account ([Popov72](https://github.com/Popov72))
 
 ## Breaking changes
 

+ 4 - 3
gui/src/2D/controls/image.ts

@@ -486,7 +486,8 @@ export class Image extends Control {
                     var vb = svgDoc.documentElement.getAttribute("viewBox");
                     var docwidth = Number(svgDoc.documentElement.getAttribute("width"));
                     var docheight = Number(svgDoc.documentElement.getAttribute("height"));
-                    if (vb && docwidth && docheight) {
+                    var elem = <SVGGraphicsElement> <unknown> svgDoc.getElementById(elemid);
+                    if (elem && vb && docwidth && docheight) {
                         this._getSVGAttribs(svgExist, elemid);
                         return value;
                     }
@@ -530,8 +531,8 @@ export class Image extends Control {
             var docwidth = Number(svgDoc.documentElement.getAttribute("width"));
             var docheight = Number(svgDoc.documentElement.getAttribute("height"));
             // get element bbox and matrix transform
-            var elem = <SVGGraphicsElement> <unknown> svgDoc.getElementById(elemid);
-            if (vb && docwidth && docheight) {
+            var elem = svgDoc.getElementById(elemid) as Nullable<SVGGraphicsElement>;
+            if (vb && docwidth && docheight && elem) {
                 var vb_width = Number(vb.split(" ")[2]);
                 var vb_height = Number(vb.split(" ")[3]);
                 var elem_bbox = elem.getBBox();

+ 3 - 2
serializers/src/glTF/2.0/Extensions/KHR_lights_punctual.ts

@@ -97,8 +97,9 @@ export class KHR_lights_punctual implements IGLTFExporterExtensionV2 {
                 }
                 else {
                     const lightPosition = babylonLight.position.clone();
+                    let convertToRightHandedSystem = this._exporter._convertToRightHandedSystemMap[babylonNode.uniqueId];
                     if (!lightPosition.equals(Vector3.Zero())) {
-                        if (this._exporter._convertToRightHandedSystem) {
+                        if (convertToRightHandedSystem) {
                             _GLTFUtilities._GetRightHandedPositionVector3FromRef(lightPosition);
                         }
                         node.translation = lightPosition.asArray();
@@ -109,7 +110,7 @@ export class KHR_lights_punctual implements IGLTFExporterExtensionV2 {
                         const len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
                         const pitch = -Math.atan2(localAxis.y, len);
                         const lightRotationQuaternion = Quaternion.RotationYawPitchRoll(yaw, pitch, 0);
-                        if (this._exporter._convertToRightHandedSystem) {
+                        if (convertToRightHandedSystem) {
                             _GLTFUtilities._GetRightHandedQuaternionFromRef(lightRotationQuaternion);
                         }
                         if (!lightRotationQuaternion.equals(Quaternion.Identity())) {

+ 5 - 2
serializers/src/glTF/2.0/glTFAnimation.ts

@@ -178,6 +178,7 @@ export class _GLTFAnimation {
      * @param bufferViews
      * @param accessors
      * @param convertToRightHandedSystem
+     * @param animationSampleRate
      */
     public static _CreateNodeAnimationFromNodeAnimations(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;
@@ -224,9 +225,10 @@ export class _GLTFAnimation {
      * @param binaryWriter
      * @param bufferViews
      * @param accessors
-     * @param convertToRightHandedSystem
+     * @param convertToRightHandedSystemMap
+     * @param animationSampleRate
      */
-    public static _CreateNodeAnimationFromAnimationGroups(babylonScene: Scene, glTFAnimations: IAnimation[], nodeMap: { [key: number]: number }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number) {
+    public static _CreateNodeAnimationFromAnimationGroups(babylonScene: Scene, glTFAnimations: IAnimation[], nodeMap: { [key: number]: number }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystemMap: { [nodeId: number]: boolean }, animationSampleRate: number) {
         let glTFAnimation: IAnimation;
         if (babylonScene.animationGroups) {
             let animationGroups = babylonScene.animationGroups;
@@ -244,6 +246,7 @@ export class _GLTFAnimation {
                         let animationInfo = _GLTFAnimation._DeduceAnimationInfo(targetAnimation.animation);
                         if (animationInfo) {
                             let babylonTransformNode = target instanceof TransformNode ? target as TransformNode : target[0] as TransformNode;
+                            let convertToRightHandedSystem = convertToRightHandedSystemMap[babylonTransformNode.uniqueId];
                             _GLTFAnimation.AddAnimation(`${animation.name}`,
                                 glTFAnimation,
                                 babylonTransformNode,

+ 130 - 40
serializers/src/glTF/2.0/glTFExporter.ts

@@ -1,7 +1,7 @@
 import { AccessorType, IBufferView, IAccessor, INode, IScene, IMesh, IMaterial, ITexture, IImage, ISampler, IAnimation, ImageMimeType, IMeshPrimitive, IBuffer, IGLTF, MeshPrimitiveMode, AccessorComponentType, ITextureInfo } from "babylonjs-gltf2interface";
 
 import { FloatArray, Nullable, IndicesArray } from "babylonjs/types";
-import { Viewport, Color3, Vector2, Vector3, Vector4, Quaternion } from "babylonjs/Maths/math";
+import { Viewport, Color3, Vector2, Vector3, Vector4, Quaternion, Epsilon, Matrix } from "babylonjs/Maths/math";
 import { Tools } from "babylonjs/Misc/tools";
 import { VertexBuffer } from "babylonjs/Meshes/buffer";
 import { Node } from "babylonjs/node";
@@ -122,11 +122,21 @@ export class _Exporter {
     private _nodeMap: { [key: number]: number };
 
     /**
-     * Specifies if the Babylon scene should be converted to right-handed on export
+     * Specifies if the source Babylon scene was left handed, and needed conversion.
      */
     public _convertToRightHandedSystem: boolean;
 
     /**
+     * Specifies if a Babylon node should be converted to right-handed on export
+     */
+    public _convertToRightHandedSystemMap: { [nodeId: number]: boolean };
+
+    /*
+    * Specifies if root Babylon empty nodes that act as a coordinate space transform should be included in export
+    */
+    public _includeCoordinateSystemConversionNodes: boolean = false;
+
+    /**
      * Baked animation sample rate
      */
     private _animationSampleRate: number;
@@ -277,9 +287,9 @@ export class _Exporter {
         this._samplers = [];
         this._animations = [];
         this._imageData = {};
-        this._convertToRightHandedSystem = !this._babylonScene.useRightHandedSystem;
         this._options = options || {};
         this._animationSampleRate = options && options.animationSampleRate ? options.animationSampleRate : 1 / 60;
+        this._includeCoordinateSystemConversionNodes = options && options.includeCoordinateSystemConversionNodes ? true : false;
 
         this._glTFMaterialExporter = new _GLTFMaterialExporter(this);
         this._loadExtensions();
@@ -383,20 +393,21 @@ export class _Exporter {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private reorderVertexAttributeDataBasedOnPrimitiveMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter): void {
-        if (this._convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
+    private reorderVertexAttributeDataBasedOnPrimitiveMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): void {
+        if (convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
             switch (primitiveMode) {
                 case Material.TriangleFillMode: {
-                    this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleFillMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
                 case Material.TriangleStripDrawMode: {
-                    this.reorderTriangleStripDrawMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleStripDrawMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
                 case Material.TriangleFanDrawMode: {
-                    this.reorderTriangleFanMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
+                    this.reorderTriangleFanMode(submesh, primitiveMode, sideOrientation, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter, convertToRightHandedSystem);
                     break;
                 }
             }
@@ -413,8 +424,9 @@ export class _Exporter {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private reorderTriangleFillMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter) {
+    private reorderTriangleFillMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean) {
         const vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh() as Mesh);
         if (vertexBuffer) {
             let stride = vertexBuffer.byteStride / VertexBuffer.GetTypeByteLength(vertexBuffer.type);
@@ -475,7 +487,7 @@ export class _Exporter {
                         Tools.Error(`Unsupported Vertex Buffer type: ${vertexBufferKind}`);
                     }
                 }
-                this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter);
+                this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
             }
         }
         else {
@@ -493,8 +505,9 @@ export class _Exporter {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private reorderTriangleStripDrawMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter) {
+    private reorderTriangleStripDrawMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean) {
         const vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh() as Mesh);
         if (vertexBuffer) {
             const stride = vertexBuffer.byteStride / VertexBuffer.GetTypeByteLength(vertexBuffer.type);
@@ -535,7 +548,7 @@ export class _Exporter {
                     Tools.Error(`Unsupported Vertex Buffer type: ${vertexBufferKind}`);
                 }
             }
-            this.writeVertexAttributeData(vertexData, byteOffset + 12, vertexBufferKind, meshAttributeArray, binaryWriter);
+            this.writeVertexAttributeData(vertexData, byteOffset + 12, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
         }
         else {
             Tools.Warn(`reorderTriangleStripDrawMode: Vertex buffer kind ${vertexBufferKind} not present!`);
@@ -552,8 +565,9 @@ export class _Exporter {
      * @param meshAttributeArray The vertex attribute data
      * @param byteOffset The offset to the binary data
      * @param binaryWriter The binary data for the glTF file
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private reorderTriangleFanMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter) {
+    private reorderTriangleFanMode(submesh: SubMesh, primitiveMode: number, sideOrientation: number, vertexBufferKind: string, meshAttributeArray: FloatArray, byteOffset: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean) {
         const vertexBuffer = this.getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh() as Mesh);
         if (vertexBuffer) {
             let stride = vertexBuffer.byteStride / VertexBuffer.GetTypeByteLength(vertexBuffer.type);
@@ -597,7 +611,7 @@ export class _Exporter {
                     Tools.Error(`Unsupported Vertex Buffer type: ${vertexBufferKind}`);
                 }
             }
-            this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter);
+            this.writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, meshAttributeArray, binaryWriter, convertToRightHandedSystem);
         }
         else {
             Tools.Warn(`reorderTriangleFanMode: Vertex buffer kind ${vertexBufferKind} not present!`);
@@ -611,10 +625,11 @@ export class _Exporter {
      * @param vertexAttributeKind The vertex attribute type
      * @param meshAttributeArray The vertex attribute data
      * @param binaryWriter The writer containing the binary data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private writeVertexAttributeData(vertices: Vector2[] | Vector3[] | Vector4[], byteOffset: number, vertexAttributeKind: string, meshAttributeArray: FloatArray, binaryWriter: _BinaryWriter) {
+    private writeVertexAttributeData(vertices: Vector2[] | Vector3[] | Vector4[], byteOffset: number, vertexAttributeKind: string, meshAttributeArray: FloatArray, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean) {
         for (let vertex of vertices) {
-            if (this._convertToRightHandedSystem && !(vertexAttributeKind === VertexBuffer.ColorKind) && !(vertex instanceof Vector2)) {
+            if (convertToRightHandedSystem && !(vertexAttributeKind === VertexBuffer.ColorKind) && !(vertex instanceof Vector2)) {
                 if (vertex instanceof Vector3) {
                     if (vertexAttributeKind === VertexBuffer.NormalKind) {
                         _GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
@@ -651,8 +666,9 @@ export class _Exporter {
      * @param meshAttributeArray Array containing the attribute data
      * @param binaryWriter The buffer to write the binary data to
      * @param indices Used to specify the order of the vertex data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    public writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter) {
+    public writeAttributeData(vertexBufferKind: string, meshAttributeArray: FloatArray, byteStride: number, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean) {
         const stride = byteStride / 4;
         let vertexAttributes: number[][] = [];
         let index: number;
@@ -662,7 +678,7 @@ export class _Exporter {
                 for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     index = k * stride;
                     const vertexData = Vector3.FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                     }
                     vertexAttributes.push(vertexData.asArray());
@@ -673,7 +689,7 @@ export class _Exporter {
                 for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     index = k * stride;
                     const vertexData = Vector3.FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                     }
                     vertexData.normalize();
@@ -685,7 +701,7 @@ export class _Exporter {
                 for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     index = k * stride;
                     const vertexData = Vector4.FromArray(meshAttributeArray, index);
-                    if (this._convertToRightHandedSystem) {
+                    if (convertToRightHandedSystem) {
                         _GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                     }
                     _GLTFUtilities._NormalizeTangentFromRef(vertexData);
@@ -706,7 +722,7 @@ export class _Exporter {
             case VertexBuffer.UV2Kind: {
                 for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     index = k * stride;
-                    vertexAttributes.push(this._convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
+                    vertexAttributes.push(convertToRightHandedSystem ? [meshAttributeArray[index], meshAttributeArray[index + 1]] : [meshAttributeArray[index], meshAttributeArray[index + 1]]);
                 }
                 break;
             }
@@ -963,13 +979,14 @@ export class _Exporter {
      * Sets the TRS for each node
      * @param node glTF Node for storing the transformation data
      * @param babylonTransformNode Babylon mesh used as the source for the transformation data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private setNodeTransformation(node: INode, babylonTransformNode: TransformNode): void {
+    private setNodeTransformation(node: INode, babylonTransformNode: TransformNode, convertToRightHandedSystem: boolean): void {
         if (!babylonTransformNode.getPivotPoint().equalsToFloats(0, 0, 0)) {
             Tools.Warn("Pivot points are not supported in the glTF serializer");
         }
         if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-            node.translation = this._convertToRightHandedSystem ? _GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+            node.translation = convertToRightHandedSystem ? _GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
         }
 
         if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
@@ -981,7 +998,7 @@ export class _Exporter {
             rotationQuaternion.multiplyInPlace(babylonTransformNode.rotationQuaternion);
         }
         if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
-            if (this._convertToRightHandedSystem) {
+            if (convertToRightHandedSystem) {
                 _GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
 
             }
@@ -1004,8 +1021,9 @@ export class _Exporter {
      * @param kind Indicates the type of vertices data
      * @param babylonTransformNode The Babylon mesh to get the vertices data from
      * @param binaryWriter The buffer to write the bufferview data to
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private createBufferViewKind(kind: string, babylonTransformNode: TransformNode, binaryWriter: _BinaryWriter, byteStride: number) {
+    private createBufferViewKind(kind: string, babylonTransformNode: TransformNode, binaryWriter: _BinaryWriter, byteStride: number, convertToRightHandedSystem: boolean) {
         const bufferMesh = babylonTransformNode instanceof Mesh ?
             babylonTransformNode as Mesh : babylonTransformNode instanceof InstancedMesh ?
                 (babylonTransformNode as InstancedMesh).sourceMesh : null;
@@ -1022,7 +1040,8 @@ export class _Exporter {
                     kind,
                     vertexData,
                     byteStride,
-                    binaryWriter
+                    binaryWriter,
+                    convertToRightHandedSystem
                 );
             }
         }
@@ -1123,8 +1142,9 @@ export class _Exporter {
      * @param mesh glTF Mesh object to store the primitive attribute information
      * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
      * @param binaryWriter Buffer to write the attribute data to
+     * @param convertToRightHandedSystem Converts the values to right-handed
      */
-    private setPrimitiveAttributesAsync(mesh: IMesh, babylonTransformNode: TransformNode, binaryWriter: _BinaryWriter): Promise<void> {
+    private setPrimitiveAttributesAsync(mesh: IMesh, babylonTransformNode: TransformNode, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): Promise<void> {
         let promises: Promise<IMeshPrimitive>[] = [];
         let bufferMesh: Nullable<Mesh> = null;
         let bufferView: IBufferView;
@@ -1160,7 +1180,7 @@ export class _Exporter {
                         attribute.accessorType = AccessorType.VEC3;
                     }
 
-                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride);
+                    this.createBufferViewKind(attributeKind, babylonTransformNode, binaryWriter, attribute.byteStride, convertToRightHandedSystem);
                     attribute.bufferViewIndex = this._bufferViews.length - 1;
                     vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
                 }
@@ -1233,7 +1253,7 @@ export class _Exporter {
                                 if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                     minMax = { min: null, max: null };
                                     if (attributeKind == VertexBuffer.PositionKind) {
-                                        minMax = _GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                        minMax = _GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, convertToRightHandedSystem);
                                     }
                                     const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, AccessorComponentType.FLOAT, vertexData.length / stride, 0, minMax.min, minMax.max);
                                     this._accessors.push(accessor);
@@ -1251,8 +1271,8 @@ export class _Exporter {
                     if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
                         let sideOrientation = bufferMesh.overrideMaterialSideOrientation !== null ? bufferMesh.overrideMaterialSideOrientation : babylonMaterial.sideOrientation;
 
-                        // Only reverse the winding if we have a clockwise winding
-                        if (sideOrientation === Material.ClockWiseSideOrientation) {
+                        if ((sideOrientation == Material.ClockWiseSideOrientation && this._babylonScene.useRightHandedSystem)
+                            || (sideOrientation == Material.ClockWiseSideOrientation && convertToRightHandedSystem && bufferMesh.overrideMaterialSideOrientation !== bufferMesh.material?.sideOrientation)) {
                             let byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
                             if (byteOffset == null) { byteOffset = 0; }
                             let babylonIndices: Nullable<IndicesArray> = null;
@@ -1270,7 +1290,7 @@ export class _Exporter {
                                         if (!byteOffset) {
                                             byteOffset = 0;
                                         }
-                                        this.reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, sideOrientation, attribute.kind, vertexData, byteOffset, binaryWriter);
+                                        this.reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, sideOrientation, attribute.kind, vertexData, byteOffset, binaryWriter, convertToRightHandedSystem);
                                     }
                                 }
                             }
@@ -1294,6 +1314,43 @@ export class _Exporter {
     }
 
     /**
+     * Check if the node is used to convert its descendants from a right handed coordinate system to the Babylon scene's coordinate system.
+     * @param node The node to check
+     * @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.name !== "__root__") {
+                return false;
+            }
+            // Transform
+            let matrix = node.getWorldMatrix();
+            let matrixToLeftHanded = Matrix.Compose(this._convertToRightHandedSystem ? new Vector3(-1, 1, 1) : Vector3.One(), Quaternion.Identity(), Vector3.Zero());
+            let matrixProduct = matrix.multiply(matrixToLeftHanded);
+            let matrixIdentity = Matrix.IdentityReadOnly;
+
+            for (let i = 0; i < 16; i++) {
+                if (Math.abs(matrixProduct.m[i] - matrixIdentity.m[i]) > Epsilon) {
+                    return false;
+                }
+            }
+
+            // Geometry
+            if ((node instanceof Mesh && node.geometry !== null) ||
+                (node instanceof InstancedMesh && node.sourceMesh.geometry !== null)) {
+                return false;
+            }
+
+            if (this._includeCoordinateSystemConversionNodes) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Creates a glTF scene based on the array of meshes
      * Returns the the total byte offset
      * @param babylonScene Babylon scene to get the mesh data from
@@ -1305,6 +1362,36 @@ export class _Exporter {
         let glTFNode: INode;
         let directDescendents: Node[];
         const nodes: Node[] = [...babylonScene.transformNodes, ...babylonScene.meshes, ...babylonScene.lights];
+        let rootNodesToLeftHanded: Node[] = [];
+
+        this._convertToRightHandedSystem = !babylonScene.useRightHandedSystem;
+        this._convertToRightHandedSystemMap = {};
+
+        // Set default values for all nodes
+        babylonScene.rootNodes.forEach((rootNode) => {
+            this._convertToRightHandedSystemMap[rootNode.uniqueId] = this._convertToRightHandedSystem;
+            rootNode.getDescendants(false).forEach((descendant) => {
+                this._convertToRightHandedSystemMap[descendant.uniqueId] = this._convertToRightHandedSystem;
+            });
+        });
+
+        // Check if root nodes converting to left-handed are present
+        babylonScene.rootNodes.forEach((rootNode) => {
+            if (this.isBabylonCoordinateSystemConvertingNode(rootNode)) {
+                rootNodesToLeftHanded.push(rootNode);
+
+                // Exclude the node from list of nodes to export
+                const indexRootNode = nodes.indexOf(rootNode);
+                if (indexRootNode !== -1) { // should always be true
+                    nodes.splice(indexRootNode, 1);
+                }
+
+                // Cancel conversion to right handed system
+                rootNode.getDescendants(false).forEach((descendant) => {
+                    this._convertToRightHandedSystemMap[descendant.uniqueId] = false;
+                });
+            }
+        });
 
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, ImageMimeType.PNG, true).then(() => {
             return this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then((nodeMap) => {
@@ -1329,12 +1416,13 @@ export class _Exporter {
                             }
                         }
 
-                        if (!babylonNode.parent) {
+                        if (!babylonNode.parent || rootNodesToLeftHanded.indexOf(babylonNode.parent) !== -1) {
                             if (this._options.shouldExportNode && !this._options.shouldExportNode(babylonNode)) {
                                 Tools.Log("Omitting " + babylonNode.name + " from scene.");
                             }
                             else {
-                                if (this._convertToRightHandedSystem) {
+                                let convertToRightHandedSystem = this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                                if (convertToRightHandedSystem) {
                                     if (glTFNode.translation) {
                                         glTFNode.translation[2] *= -1;
                                         glTFNode.translation[0] *= -1;
@@ -1388,7 +1476,8 @@ export class _Exporter {
         for (let babylonNode of nodes) {
             if (!this._options.shouldExportNode || this._options.shouldExportNode(babylonNode)) {
                 promiseChain = promiseChain.then(() => {
-                    return this.createNodeAsync(babylonNode, binaryWriter).then((node) => {
+                    let convertToRightHandedSystem = this._convertToRightHandedSystemMap[babylonNode.uniqueId];
+                    return this.createNodeAsync(babylonNode, binaryWriter, convertToRightHandedSystem).then((node) => {
                         const promise = this._extensionsPostExportNodeAsync("createNodeAsync", node, babylonNode);
                         if (promise == null) {
                             Tools.Warn(`Not exporting node ${babylonNode.name}`);
@@ -1404,7 +1493,7 @@ export class _Exporter {
                                 nodeMap[babylonNode.uniqueId] = nodeIndex;
 
                                 if (!babylonScene.animationGroups.length && babylonNode.animations.length) {
-                                    _GLTFAnimation._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, this._convertToRightHandedSystem, this._animationSampleRate);
+                                    _GLTFAnimation._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, convertToRightHandedSystem, this._animationSampleRate);
                                 }
                             });
                         }
@@ -1427,7 +1516,7 @@ export class _Exporter {
             });
 
             if (babylonScene.animationGroups.length) {
-                _GLTFAnimation._CreateNodeAnimationFromAnimationGroups(babylonScene, this._animations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, this._convertToRightHandedSystem, this._animationSampleRate);
+                _GLTFAnimation._CreateNodeAnimationFromAnimationGroups(babylonScene, this._animations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, this._convertToRightHandedSystemMap, this._animationSampleRate);
             }
 
             return nodeMap;
@@ -1438,9 +1527,10 @@ export class _Exporter {
      * Creates a glTF node from a Babylon mesh
      * @param babylonMesh Source Babylon mesh
      * @param binaryWriter Buffer for storing geometry data
+     * @param convertToRightHandedSystem Converts the values to right-handed
      * @returns glTF node
      */
-    private createNodeAsync(babylonNode: Node, binaryWriter: _BinaryWriter): Promise<INode> {
+    private createNodeAsync(babylonNode: Node, binaryWriter: _BinaryWriter, convertToRightHandedSystem: boolean): Promise<INode> {
         return Promise.resolve().then(() => {
             // create node to hold translation/rotation/scale and the mesh
             const node: INode = {};
@@ -1453,9 +1543,9 @@ export class _Exporter {
 
             if (babylonNode instanceof TransformNode) {
                 // Set transformation
-                this.setNodeTransformation(node, babylonNode);
+                this.setNodeTransformation(node, babylonNode, convertToRightHandedSystem);
 
-                return this.setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter).then(() => {
+                return this.setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter, convertToRightHandedSystem).then(() => {
                     if (mesh.primitives.length) {
                         this._meshes.push(mesh);
                         node.mesh = this._meshes.length - 1;

+ 5 - 0
serializers/src/glTF/2.0/glTFSerializer.ts

@@ -30,6 +30,11 @@ export interface IExportOptions {
      * Begin serialization without waiting for the scene to be ready
      */
     exportWithoutWaitingForScene?: boolean;
+
+    /**
+     * Indicates if coordinate system swapping root nodes should be included in export
+     */
+    includeCoordinateSystemConversionNodes?: boolean;
 }
 
 /**

+ 52 - 0
src/Engines/engine.ts

@@ -1798,6 +1798,58 @@ export class Engine extends ThinEngine {
         this._gl.deleteBuffer(buffer);
     }
 
+    private _clientWaitAsync(sync: WebGLSync, flags = 0, interval_ms = 10) {
+        let gl = <WebGL2RenderingContext>(this._gl as any);
+        return new Promise((resolve, reject) => {
+            let check = () => {
+                const res = gl.clientWaitSync(sync, flags, 0);
+                if (res == gl.WAIT_FAILED) {
+                reject();
+                return;
+                }
+                if (res == gl.TIMEOUT_EXPIRED) {
+                setTimeout(check, interval_ms);
+                return;
+                }
+                resolve();
+            };
+
+            check();
+        });
+    }
+
+    /** @hidden */
+    public _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView) {
+        if (this._webGLVersion < 2) {
+            throw new Error("_readPixelsAsync only work on WebGL2+");
+        }
+
+        let gl = <WebGL2RenderingContext>(this._gl as any);
+        const buf = gl.createBuffer();
+        gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
+        gl.bufferData(gl.PIXEL_PACK_BUFFER, outputBuffer.byteLength, gl.STREAM_READ);
+        gl.readPixels(x, y, w, h, format, type, 0);
+        gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
+
+        const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
+        if (!sync) {
+            return null;
+        }
+
+        gl.flush();
+
+        return this._clientWaitAsync(sync, 0, 10).then(() => {
+            gl.deleteSync(sync);
+
+            gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
+            gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, outputBuffer);
+            gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
+            gl.deleteBuffer(buf);
+
+            return outputBuffer;
+        });
+    }
+
     /** @hidden */
     public _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex = -1, level = 0, buffer: Nullable<ArrayBufferView> = null): ArrayBufferView {
         let gl = this._gl;

+ 3 - 7
src/Loading/Plugins/babylonFileLoader.ts

@@ -112,8 +112,6 @@ var loadDetailLevels = (scene: Scene, mesh: AbstractMesh) => {
 var loadAssetContainer = (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void, addToScene = false): AssetContainer => {
     var container = new AssetContainer(scene);
 
-    scene._blockEntityCollection = !addToScene;
-
     // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
     // when SceneLoader.debugLogging = true (default), or exception encountered.
     // Everything stored in var log instead of writing separate lines to support only writing in exception,
@@ -437,10 +435,6 @@ var loadAssetContainer = (scene: Scene, data: string, rootUrl: string, onError?:
         if (parsedData.actions !== undefined && parsedData.actions !== null) {
             ActionManager.Parse(parsedData.actions, null, scene);
         }
-
-        if (!addToScene) {
-            container.removeAllFromScene();
-        }
     } catch (err) {
         let msg = logOperation("loadAssets", parsedData ? parsedData.producer : "Unknown") + log;
         if (onError) {
@@ -450,7 +444,9 @@ var loadAssetContainer = (scene: Scene, data: string, rootUrl: string, onError?:
             throw err;
         }
     } finally {
-        scene._blockEntityCollection = false;
+        if (!addToScene) {
+            container.removeAllFromScene();
+        }
         if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
             Logger.Log(logOperation("loadAssets", parsedData ? parsedData.producer : "Unknown") + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
         }

+ 1 - 0
src/Materials/Textures/hdrCubeTexture.ts

@@ -133,6 +133,7 @@ export class HDRCubeTexture extends BaseTexture {
 
         this._noMipmap = noMipmap;
         this._size = size;
+        this._generateHarmonics = generateHarmonics;
 
         this._texture = this._getFromCache(url, this._noMipmap);
 

+ 14 - 3
src/Sprites/spriteManager.ts

@@ -56,7 +56,7 @@ export interface ISpriteManager extends IDisposable {
      */
     intersects(ray: Ray, camera: Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
 
-        /**
+    /**
      * Intersects the sprites with a ray
      * @param ray defines the ray to intersect with
      * @param camera defines the current active camera
@@ -140,6 +140,17 @@ export class SpriteManager implements ISpriteManager {
         this._spriteTexture = value;
     }
 
+    private _blendMode = Constants.ALPHA_COMBINE;
+    /**
+     * Blend mode use to render the particle, it can be any of
+     * the static Constants.ALPHA_x properties provided in this class.
+     * Default value is Constants.ALPHA_COMBINE
+     */
+    public get blendMode() { return this._blendMode; }
+    public set blendMode(blendMode: number) {
+        this._blendMode = blendMode;
+    }
+
     /**
      * Creates a new sprite manager
      * @param name defines the manager's name
@@ -571,7 +582,7 @@ export class SpriteManager implements ISpriteManager {
         engine.setColorWrite(true);
         effect.setBool("alphaTest", false);
 
-        engine.setAlphaMode(Constants.ALPHA_COMBINE);
+        engine.setAlphaMode(this._blendMode);
         engine.drawElementsType(Material.TriangleFillMode, 0, (offset / 4) * 6);
         engine.setAlphaMode(Constants.ALPHA_DISABLE);
     }
@@ -603,4 +614,4 @@ export class SpriteManager implements ISpriteManager {
         this.onDisposeObservable.notifyObservers(this);
         this.onDisposeObservable.clear();
     }
-}
+}

二進制
tests/validation/ReferenceImages/glTFSerializerNegativeWorldMatrix.png