瀏覽代碼

Merged with upstream master

Nathan Duke 6 年之前
父節點
當前提交
d2c33d299e
共有 64 個文件被更改,包括 1104 次插入203 次删除
  1. 41 9
      Playground/babylon.d.txt
  2. 二進制
      Playground/scenes/Box/Box_extras.bin
  3. 242 0
      Playground/scenes/Box/Box_extras.gltf
  4. 1 1
      assets/meshes/controllers/oculusQuest/left.babylon
  5. 1 1
      assets/meshes/controllers/oculusQuest/right.babylon
  6. 11 4
      dist/preview release/babylon.d.ts
  7. 1 1
      dist/preview release/babylon.js
  8. 59 43
      dist/preview release/babylon.max.js
  9. 1 1
      dist/preview release/babylon.max.js.map
  10. 22 8
      dist/preview release/babylon.module.d.ts
  11. 41 9
      dist/preview release/documentation.d.ts
  12. 1 1
      dist/preview release/glTF2Interface/package.json
  13. 2 2
      dist/preview release/gui/package.json
  14. 6 6
      dist/preview release/inspector/package.json
  15. 74 2
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  16. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  17. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  18. 74 2
      dist/preview release/loaders/babylon.glTFFileLoader.js
  19. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  20. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  21. 23 0
      dist/preview release/loaders/babylonjs.loaders.d.ts
  22. 74 2
      dist/preview release/loaders/babylonjs.loaders.js
  23. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  24. 2 2
      dist/preview release/loaders/babylonjs.loaders.min.js
  25. 54 0
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  26. 3 3
      dist/preview release/loaders/package.json
  27. 2 2
      dist/preview release/materialsLibrary/package.json
  28. 2 2
      dist/preview release/nodeEditor/package.json
  29. 1 1
      dist/preview release/package.json
  30. 1 1
      dist/preview release/packagesSizeBaseLine.json
  31. 2 2
      dist/preview release/postProcessesLibrary/package.json
  32. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  33. 15 8
      dist/preview release/serializers/babylon.glTF2Serializer.js
  34. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  35. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  36. 7 5
      dist/preview release/serializers/babylonjs.serializers.d.ts
  37. 15 8
      dist/preview release/serializers/babylonjs.serializers.js
  38. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  39. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  40. 14 10
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  41. 3 3
      dist/preview release/serializers/package.json
  42. 22 8
      dist/preview release/viewer/babylon.module.d.ts
  43. 18 14
      dist/preview release/viewer/babylon.viewer.js
  44. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  45. 54 0
      dist/preview release/viewer/babylonjs.loaders.module.d.ts
  46. 6 0
      dist/preview release/what's new.md
  47. 101 0
      loaders/src/glTF/2.0/Extensions/ExtrasAsMetadata.ts
  48. 2 1
      loaders/src/glTF/2.0/Extensions/index.ts
  49. 1 1
      package.json
  50. 2 2
      sandbox/animation.js
  51. 2 0
      sandbox/index.css
  52. 16 12
      serializers/src/glTF/2.0/glTFExporter.ts
  53. 8 0
      serializers/src/glTF/2.0/glTFSerializer.ts
  54. 1 1
      src/Animations/easing.ts
  55. 7 2
      src/Behaviors/Meshes/pointerDragBehavior.ts
  56. 1 1
      src/Cameras/camera.ts
  57. 0 1
      src/Cameras/targetCamera.ts
  58. 2 2
      src/Engines/engine.ts
  59. 1 1
      src/Gamepads/Controllers/oculusTouchController.ts
  60. 3 1
      src/Materials/multiMaterial.ts
  61. 3 1
      src/Meshes/abstractMesh.ts
  62. 2 2
      src/Shaders/gpuUpdateParticles.vertex.fx
  63. 15 0
      src/scene.ts
  64. 28 0
      tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

+ 41 - 9
Playground/babylon.d.txt

@@ -6625,7 +6625,7 @@ declare module BABYLON {
          */
         easeInCore(gradient: number): number;
         /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
+         * Given an input gradient between 0 and 1, this returns the corresponding value
          * of the easing function.
          * @param gradient Defines the value between 0 and 1 we want the easing value for
          * @returns the corresponding value on the curve defined by the easing function
@@ -7879,7 +7879,6 @@ declare module BABYLON {
         /** @hidden */
private _computeLocalCameraSpeed(): number;
         /**
          * Defines the target the camera should look at.
-         * This will automatically adapt alpha beta and radius to fit within the new target.
          * @param target Defines the new target as a Vector or a mesh
          */
         setTarget(target: Vector3): void;
@@ -24480,7 +24479,9 @@ declare module BABYLON {
          */
         isVerticesDataPresent(kind: string): boolean;
         /**
-         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+         * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+         * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
          * @returns a BoundingInfo
          */
         getBoundingInfo(): BoundingInfo;
@@ -30039,7 +30040,7 @@ declare module BABYLON {
          */
         inertia: number;
         /**
-         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
          */
         mode: number;
         /**
@@ -32884,6 +32885,12 @@ declare module BABYLON {
          */
         getMaterialByID(id: string): Nullable<Material>;
         /**
+         * Gets a the last added material using a given id
+         * @param id defines the material's ID
+         * @return the last material with the given id or null if none found.
+         */
+        getLastMaterialByID(id: string): Nullable<Material>;
+        /**
          * Gets a material using its name
          * @param name defines the material's name
          * @return the material or null if none found.
@@ -64160,6 +64167,29 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
     }
 }
+declare module BABYLON.GLTF2.Loader.Extensions {
+    /**
+     * Store glTF extras (if present) in BJS objects' metadata
+     */
+    export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        readonly name: string;
+        /** Defines whether this extension is enabled. */
+        enabled: boolean;
+        private _loader;
+        private _assignExtras;
+        /** @hidden */
+        constructor(loader: GLTFLoader);
+        /** @hidden */
+        dispose(): void;
+        /** @hidden */
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
+        /** @hidden */
+        loadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /** @hidden */
+        createMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    }
+}
 declare module BABYLON {
     /**
      * Class reading and parsing the MTL file bundled with the obj file.
@@ -64772,6 +64802,12 @@ declare module BABYLON {
          */
         shouldExportNode?(node: Node): boolean;
         /**
+         * Function used to extract the part of node's metadata that will be exported into glTF node extras
+         * @param metadata source metadata to read from
+         * @returns the data to store to glTF node extras
+         */
+        metadataSelector?(metadata: any): any;
+        /**
          * The sample rate to bake animation curves
          */
         animationSampleRate?: number;
@@ -64960,10 +64996,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Baked animation sample rate
          */
         private _animationSampleRate;
-        /**
-         * Callback which specifies if a node should be exported or not
-         */
-        private _shouldExportNode;
+        private _options;
         private _localEngine;
private _glTFMaterialExporter: _GLTFMaterialExporter;
         private _extensions;
         private static _ExtensionNames;
@@ -65143,7 +65176,6 @@ declare module BABYLON.GLTF2.Exporter {
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene
          * @param nodes Babylon transform nodes
-         * @param shouldExportNode Callback specifying if a transform node should be exported
          * @param binaryWriter Buffer to write binary data to
          * @returns Node mapping of unique id to index
          */

二進制
Playground/scenes/Box/Box_extras.bin


+ 242 - 0
Playground/scenes/Box/Box_extras.gltf

@@ -0,0 +1,242 @@
+{
+    "asset" : {
+        "generator" : "Khronos glTF Blender I/O v0.9.28",
+        "version" : "2.0"
+    },
+    "extensionsUsed" : [
+        "KHR_lights_punctual"
+    ],
+    "extensionsRequired" : [
+        "KHR_lights_punctual"
+    ],
+    "extensions" : {
+        "KHR_lights_punctual" : {
+            "lights" : [
+                {
+                    "color" : [
+                        1,
+                        1,
+                        1
+                    ],
+                    "intensity" : 1000,
+                    "type" : "point",
+                    "name" : "Light"
+                }
+            ]
+        }
+    },
+    "scene" : 0,
+    "scenes" : [
+        {
+            "extras" : {
+                "custom" : "sceneProp"
+            },
+            "name" : "Scene",
+            "nodes" : [
+                1,
+                3,
+                4
+            ]
+        }
+    ],
+    "nodes" : [
+        {
+            "extensions" : {
+                "KHR_lights_punctual" : {
+                    "light" : 0
+                }
+            },
+            "name" : "Light_Orientation",
+            "rotation" : [
+                -0.7071067690849304,
+                0,
+                0,
+                0.7071067690849304
+            ]
+        },
+        {
+            "children" : [
+                0
+            ],
+            "name" : "Light",
+            "rotation" : [
+                0.16907575726509094,
+                0.7558803558349609,
+                -0.27217137813568115,
+                0.570947527885437
+            ],
+            "translation" : [
+                4.076245307922363,
+                5.903861999511719,
+                -1.0054539442062378
+            ]
+        },
+        {
+            "camera" : 0,
+            "name" : "Camera_Orientation",
+            "rotation" : [
+                -0.7071067690849304,
+                0,
+                0,
+                0.7071067690849304
+            ]
+        },
+        {
+            "children" : [
+                2
+            ],
+            "name" : "Camera",
+            "rotation" : [
+                0.483536034822464,
+                0.33687159419059753,
+                -0.20870360732078552,
+                0.7804827094078064
+            ],
+            "translation" : [
+                7.358891487121582,
+                4.958309173583984,
+                6.925790786743164
+            ]
+        },
+        {
+            "extras" : {
+                "kind" : "nice cube",
+                "magic" : 42
+            },
+            "mesh" : 0,
+            "name" : "Box001",
+            "rotation" : [
+                1,
+                0,
+                0,
+                -1.3435885648505064e-07
+            ]
+        }
+    ],
+    "cameras" : [
+        {
+            "extras" : {
+                "custom" : "cameraProp"
+            },
+            "name" : "Camera",
+            "perspective" : {
+                "yfov" : 0.39959652046304894,
+                "zfar" : 100,
+                "znear" : 0.10000000149011612
+            },
+            "type" : "perspective"
+        }
+    ],
+    "materials" : [
+        {
+            "doubleSided" : true,
+            "extras" : {
+                "custom" : "materialProp"
+            },
+            "name" : "01___Default",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 0,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.8945907354354858
+            }
+        }
+    ],
+    "meshes" : [
+        {
+            "extras" : {
+                "custom" : "meshProp"
+            },
+            "name" : "Box001",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 0
+                }
+            ]
+        }
+    ],
+    "textures" : [
+        {
+            "source" : 0
+        }
+    ],
+    "images" : [
+        {
+            "mimeType" : "image/png",
+            "name" : "20140615_192225",
+            "uri" : "20140615_192225.png"
+        }
+    ],
+    "accessors" : [
+        {
+            "bufferView" : 0,
+            "componentType" : 5126,
+            "count" : 24,
+            "max" : [
+                13.23270034790039,
+                0,
+                16.17329978942871
+            ],
+            "min" : [
+                -13.83899974822998,
+                -23.56559944152832,
+                -16.098499298095703
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 1,
+            "componentType" : 5126,
+            "count" : 24,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 2,
+            "componentType" : 5126,
+            "count" : 24,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 3,
+            "componentType" : 5123,
+            "count" : 36,
+            "type" : "SCALAR"
+        }
+    ],
+    "bufferViews" : [
+        {
+            "buffer" : 0,
+            "byteLength" : 288,
+            "byteOffset" : 0
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 288,
+            "byteOffset" : 288
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 192,
+            "byteOffset" : 576
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 72,
+            "byteOffset" : 768
+        }
+    ],
+    "buffers" : [
+        {
+            "byteLength" : 840,
+            "uri" : "Box_extras.bin"
+        }
+    ]
+}

File diff suppressed because it is too large
+ 1 - 1
assets/meshes/controllers/oculusQuest/left.babylon


File diff suppressed because it is too large
+ 1 - 1
assets/meshes/controllers/oculusQuest/right.babylon


+ 11 - 4
dist/preview release/babylon.d.ts

@@ -6694,7 +6694,7 @@ declare module BABYLON {
          */
         easeInCore(gradient: number): number;
         /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
+         * Given an input gradient between 0 and 1, this returns the corresponding value
          * of the easing function.
          * @param gradient Defines the value between 0 and 1 we want the easing value for
          * @returns the corresponding value on the curve defined by the easing function
@@ -7986,7 +7986,6 @@ declare module BABYLON {
         _computeLocalCameraSpeed(): number;
         /**
          * Defines the target the camera should look at.
-         * This will automatically adapt alpha beta and radius to fit within the new target.
          * @param target Defines the new target as a Vector or a mesh
          */
         setTarget(target: Vector3): void;
@@ -24935,7 +24934,9 @@ declare module BABYLON {
          */
         isVerticesDataPresent(kind: string): boolean;
         /**
-         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+         * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+         * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
          * @returns a BoundingInfo
          */
         getBoundingInfo(): BoundingInfo;
@@ -30622,7 +30623,7 @@ declare module BABYLON {
          */
         inertia: number;
         /**
-         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
          */
         mode: number;
         /**
@@ -33547,6 +33548,12 @@ declare module BABYLON {
          */
         getMaterialByID(id: string): Nullable<Material>;
         /**
+         * Gets a the last added material using a given id
+         * @param id defines the material's ID
+         * @return the last material with the given id or null if none found.
+         */
+        getLastMaterialByID(id: string): Nullable<Material>;
+        /**
          * Gets a material using its name
          * @param name defines the material's name
          * @return the material or null if none found.

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


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


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


+ 22 - 8
dist/preview release/babylon.module.d.ts

@@ -6731,7 +6731,7 @@ declare module "babylonjs/Animations/easing" {
          */
         easeInCore(gradient: number): number;
         /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
+         * Given an input gradient between 0 and 1, this returns the corresponding value
          * of the easing function.
          * @param gradient Defines the value between 0 and 1 we want the easing value for
          * @returns the corresponding value on the curve defined by the easing function
@@ -8056,7 +8056,6 @@ declare module "babylonjs/Cameras/targetCamera" {
         _computeLocalCameraSpeed(): number;
         /**
          * Defines the target the camera should look at.
-         * This will automatically adapt alpha beta and radius to fit within the new target.
          * @param target Defines the new target as a Vector or a mesh
          */
         setTarget(target: Vector3): void;
@@ -25612,7 +25611,9 @@ declare module "babylonjs/Meshes/abstractMesh" {
          */
         isVerticesDataPresent(kind: string): boolean;
         /**
-         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+         * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+         * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
          * @returns a BoundingInfo
          */
         getBoundingInfo(): BoundingInfo;
@@ -31423,7 +31424,7 @@ declare module "babylonjs/Cameras/camera" {
          */
         inertia: number;
         /**
-         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
          */
         mode: number;
         /**
@@ -34419,6 +34420,12 @@ declare module "babylonjs/scene" {
          */
         getMaterialByID(id: string): Nullable<Material>;
         /**
+         * Gets a the last added material using a given id
+         * @param id defines the material's ID
+         * @return the last material with the given id or null if none found.
+         */
+        getLastMaterialByID(id: string): Nullable<Material>;
+        /**
          * Gets a material using its name
          * @param name defines the material's name
          * @return the material or null if none found.
@@ -69693,7 +69700,7 @@ declare module BABYLON {
          */
         easeInCore(gradient: number): number;
         /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
+         * Given an input gradient between 0 and 1, this returns the corresponding value
          * of the easing function.
          * @param gradient Defines the value between 0 and 1 we want the easing value for
          * @returns the corresponding value on the curve defined by the easing function
@@ -70985,7 +70992,6 @@ declare module BABYLON {
         _computeLocalCameraSpeed(): number;
         /**
          * Defines the target the camera should look at.
-         * This will automatically adapt alpha beta and radius to fit within the new target.
          * @param target Defines the new target as a Vector or a mesh
          */
         setTarget(target: Vector3): void;
@@ -87934,7 +87940,9 @@ declare module BABYLON {
          */
         isVerticesDataPresent(kind: string): boolean;
         /**
-         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+         * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+         * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
          * @returns a BoundingInfo
          */
         getBoundingInfo(): BoundingInfo;
@@ -93621,7 +93629,7 @@ declare module BABYLON {
          */
         inertia: number;
         /**
-         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
          */
         mode: number;
         /**
@@ -96546,6 +96554,12 @@ declare module BABYLON {
          */
         getMaterialByID(id: string): Nullable<Material>;
         /**
+         * Gets a the last added material using a given id
+         * @param id defines the material's ID
+         * @return the last material with the given id or null if none found.
+         */
+        getLastMaterialByID(id: string): Nullable<Material>;
+        /**
          * Gets a material using its name
          * @param name defines the material's name
          * @return the material or null if none found.

+ 41 - 9
dist/preview release/documentation.d.ts

@@ -6694,7 +6694,7 @@ declare module BABYLON {
          */
         easeInCore(gradient: number): number;
         /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
+         * Given an input gradient between 0 and 1, this returns the corresponding value
          * of the easing function.
          * @param gradient Defines the value between 0 and 1 we want the easing value for
          * @returns the corresponding value on the curve defined by the easing function
@@ -7986,7 +7986,6 @@ declare module BABYLON {
         _computeLocalCameraSpeed(): number;
         /**
          * Defines the target the camera should look at.
-         * This will automatically adapt alpha beta and radius to fit within the new target.
          * @param target Defines the new target as a Vector or a mesh
          */
         setTarget(target: Vector3): void;
@@ -24935,7 +24934,9 @@ declare module BABYLON {
          */
         isVerticesDataPresent(kind: string): boolean;
         /**
-         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+         * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+         * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
          * @returns a BoundingInfo
          */
         getBoundingInfo(): BoundingInfo;
@@ -30622,7 +30623,7 @@ declare module BABYLON {
          */
         inertia: number;
         /**
-         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
          */
         mode: number;
         /**
@@ -33547,6 +33548,12 @@ declare module BABYLON {
          */
         getMaterialByID(id: string): Nullable<Material>;
         /**
+         * Gets a the last added material using a given id
+         * @param id defines the material's ID
+         * @return the last material with the given id or null if none found.
+         */
+        getLastMaterialByID(id: string): Nullable<Material>;
+        /**
          * Gets a material using its name
          * @param name defines the material's name
          * @return the material or null if none found.
@@ -65182,6 +65189,29 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
     }
 }
+declare module BABYLON.GLTF2.Loader.Extensions {
+    /**
+     * Store glTF extras (if present) in BJS objects' metadata
+     */
+    export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        readonly name: string;
+        /** Defines whether this extension is enabled. */
+        enabled: boolean;
+        private _loader;
+        private _assignExtras;
+        /** @hidden */
+        constructor(loader: GLTFLoader);
+        /** @hidden */
+        dispose(): void;
+        /** @hidden */
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
+        /** @hidden */
+        loadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /** @hidden */
+        createMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    }
+}
 declare module BABYLON {
     /**
      * Class reading and parsing the MTL file bundled with the obj file.
@@ -65804,6 +65834,12 @@ declare module BABYLON {
          */
         shouldExportNode?(node: Node): boolean;
         /**
+         * Function used to extract the part of node's metadata that will be exported into glTF node extras
+         * @param metadata source metadata to read from
+         * @returns the data to store to glTF node extras
+         */
+        metadataSelector?(metadata: any): any;
+        /**
          * The sample rate to bake animation curves
          */
         animationSampleRate?: number;
@@ -66017,10 +66053,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Baked animation sample rate
          */
         private _animationSampleRate;
-        /**
-         * Callback which specifies if a node should be exported or not
-         */
-        private _shouldExportNode;
+        private _options;
         private _localEngine;
         _glTFMaterialExporter: _GLTFMaterialExporter;
         private _extensions;
@@ -66207,7 +66240,6 @@ declare module BABYLON.GLTF2.Exporter {
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene
          * @param nodes Babylon transform nodes
-         * @param shouldExportNode Callback specifying if a transform node should be exported
          * @param binaryWriter Buffer to write binary data to
          * @returns Node mapping of unique id to index
          */

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

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

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

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

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.1.0-alpha.5",
+    "version": "4.1.0-alpha.6",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,11 +29,11 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-alpha.5",
-        "babylonjs-gui": "4.1.0-alpha.5",
-        "babylonjs-loaders": "4.1.0-alpha.5",
-        "babylonjs-serializers": "4.1.0-alpha.5",
-        "babylonjs-gltf2interface": "4.1.0-alpha.5"
+        "babylonjs": "4.1.0-alpha.6",
+        "babylonjs-gui": "4.1.0-alpha.6",
+        "babylonjs-loaders": "4.1.0-alpha.6",
+        "babylonjs-serializers": "4.1.0-alpha.6",
+        "babylonjs-gltf2interface": "4.1.0-alpha.6"
     },
     "devDependencies": {
         "@types/react": "~16.7.3",

+ 74 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -249,6 +249,72 @@ _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].RegisterExtension(NAME, f
 
 /***/ }),
 
+/***/ "./glTF/2.0/Extensions/ExtrasAsMetadata.ts":
+/*!*************************************************!*\
+  !*** ./glTF/2.0/Extensions/ExtrasAsMetadata.ts ***!
+  \*************************************************/
+/*! exports provided: ExtrasAsMetadata */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return ExtrasAsMetadata; });
+/* harmony import */ var _glTFLoader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../glTFLoader */ "./glTF/2.0/glTFLoader.ts");
+
+var NAME = "ExtrasAsMetadata";
+/**
+ * Store glTF extras (if present) in BJS objects' metadata
+ */
+var ExtrasAsMetadata = /** @class */ (function () {
+    /** @hidden */
+    function ExtrasAsMetadata(loader) {
+        /** The name of this extension. */
+        this.name = NAME;
+        /** Defines whether this extension is enabled. */
+        this.enabled = true;
+        this._loader = loader;
+    }
+    ExtrasAsMetadata.prototype._assignExtras = function (babylonObject, gltfProp) {
+        if (gltfProp.extras && Object.keys(gltfProp.extras).length > 0) {
+            var metadata = (babylonObject.metadata = babylonObject.metadata || {});
+            var gltf = (metadata.gltf = metadata.gltf || {});
+            gltf.extras = gltfProp.extras;
+        }
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.dispose = function () {
+        delete this._loader;
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.loadNodeAsync = function (context, node, assign) {
+        var _this = this;
+        return this._loader.loadNodeAsync(context, node, function (babylonTransformNode) {
+            _this._assignExtras(babylonTransformNode, node);
+            assign(babylonTransformNode);
+        });
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.loadCameraAsync = function (context, camera, assign) {
+        var _this = this;
+        return this._loader.loadCameraAsync(context, camera, function (babylonCamera) {
+            _this._assignExtras(babylonCamera, camera);
+            assign(babylonCamera);
+        });
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.createMaterial = function (context, material, babylonDrawMode) {
+        var babylonMaterial = this._loader.createMaterial(context, material, babylonDrawMode);
+        this._assignExtras(babylonMaterial, material);
+        return babylonMaterial;
+    };
+    return ExtrasAsMetadata;
+}());
+
+_glTFLoader__WEBPACK_IMPORTED_MODULE_0__["GLTFLoader"].RegisterExtension(NAME, function (loader) { return new ExtrasAsMetadata(loader); });
+
+
+/***/ }),
+
 /***/ "./glTF/2.0/Extensions/KHR_draco_mesh_compression.ts":
 /*!***********************************************************!*\
   !*** ./glTF/2.0/Extensions/KHR_draco_mesh_compression.ts ***!
@@ -1275,7 +1341,7 @@ _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].RegisterExtension(NAME, f
 /*!**************************************!*\
   !*** ./glTF/2.0/Extensions/index.ts ***!
   \**************************************/
-/*! exports provided: EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors */
+/*! exports provided: EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors, ExtrasAsMetadata */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
@@ -1310,6 +1376,10 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _MSFT_sRGBFactors__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./MSFT_sRGBFactors */ "./glTF/2.0/Extensions/MSFT_sRGBFactors.ts");
 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MSFT_sRGBFactors", function() { return _MSFT_sRGBFactors__WEBPACK_IMPORTED_MODULE_9__["MSFT_sRGBFactors"]; });
 
+/* harmony import */ var _ExtrasAsMetadata__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./ExtrasAsMetadata */ "./glTF/2.0/Extensions/ExtrasAsMetadata.ts");
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return _ExtrasAsMetadata__WEBPACK_IMPORTED_MODULE_10__["ExtrasAsMetadata"]; });
+
+
 
 
 
@@ -3360,7 +3430,7 @@ _glTFFileLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFFileLoader"]._CreateGLTF2Loade
 /*!***************************!*\
   !*** ./glTF/2.0/index.ts ***!
   \***************************/
-/*! exports provided: ArrayItem, GLTFLoader, EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors */
+/*! exports provided: ArrayItem, GLTFLoader, EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors, ExtrasAsMetadata */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
@@ -3391,6 +3461,8 @@ __webpack_require__.r(__webpack_exports__);
 
 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MSFT_sRGBFactors", function() { return _Extensions__WEBPACK_IMPORTED_MODULE_1__["MSFT_sRGBFactors"]; });
 
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return _Extensions__WEBPACK_IMPORTED_MODULE_1__["ExtrasAsMetadata"]; });
+
 
 
 

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


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


+ 74 - 2
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2798,6 +2798,72 @@ _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].RegisterExtension(NAME, f
 
 /***/ }),
 
+/***/ "./glTF/2.0/Extensions/ExtrasAsMetadata.ts":
+/*!*************************************************!*\
+  !*** ./glTF/2.0/Extensions/ExtrasAsMetadata.ts ***!
+  \*************************************************/
+/*! exports provided: ExtrasAsMetadata */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return ExtrasAsMetadata; });
+/* harmony import */ var _glTFLoader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../glTFLoader */ "./glTF/2.0/glTFLoader.ts");
+
+var NAME = "ExtrasAsMetadata";
+/**
+ * Store glTF extras (if present) in BJS objects' metadata
+ */
+var ExtrasAsMetadata = /** @class */ (function () {
+    /** @hidden */
+    function ExtrasAsMetadata(loader) {
+        /** The name of this extension. */
+        this.name = NAME;
+        /** Defines whether this extension is enabled. */
+        this.enabled = true;
+        this._loader = loader;
+    }
+    ExtrasAsMetadata.prototype._assignExtras = function (babylonObject, gltfProp) {
+        if (gltfProp.extras && Object.keys(gltfProp.extras).length > 0) {
+            var metadata = (babylonObject.metadata = babylonObject.metadata || {});
+            var gltf = (metadata.gltf = metadata.gltf || {});
+            gltf.extras = gltfProp.extras;
+        }
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.dispose = function () {
+        delete this._loader;
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.loadNodeAsync = function (context, node, assign) {
+        var _this = this;
+        return this._loader.loadNodeAsync(context, node, function (babylonTransformNode) {
+            _this._assignExtras(babylonTransformNode, node);
+            assign(babylonTransformNode);
+        });
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.loadCameraAsync = function (context, camera, assign) {
+        var _this = this;
+        return this._loader.loadCameraAsync(context, camera, function (babylonCamera) {
+            _this._assignExtras(babylonCamera, camera);
+            assign(babylonCamera);
+        });
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.createMaterial = function (context, material, babylonDrawMode) {
+        var babylonMaterial = this._loader.createMaterial(context, material, babylonDrawMode);
+        this._assignExtras(babylonMaterial, material);
+        return babylonMaterial;
+    };
+    return ExtrasAsMetadata;
+}());
+
+_glTFLoader__WEBPACK_IMPORTED_MODULE_0__["GLTFLoader"].RegisterExtension(NAME, function (loader) { return new ExtrasAsMetadata(loader); });
+
+
+/***/ }),
+
 /***/ "./glTF/2.0/Extensions/KHR_draco_mesh_compression.ts":
 /*!***********************************************************!*\
   !*** ./glTF/2.0/Extensions/KHR_draco_mesh_compression.ts ***!
@@ -3824,7 +3890,7 @@ _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].RegisterExtension(NAME, f
 /*!**************************************!*\
   !*** ./glTF/2.0/Extensions/index.ts ***!
   \**************************************/
-/*! exports provided: EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors */
+/*! exports provided: EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors, ExtrasAsMetadata */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
@@ -3859,6 +3925,10 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _MSFT_sRGBFactors__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./MSFT_sRGBFactors */ "./glTF/2.0/Extensions/MSFT_sRGBFactors.ts");
 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MSFT_sRGBFactors", function() { return _MSFT_sRGBFactors__WEBPACK_IMPORTED_MODULE_9__["MSFT_sRGBFactors"]; });
 
+/* harmony import */ var _ExtrasAsMetadata__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./ExtrasAsMetadata */ "./glTF/2.0/Extensions/ExtrasAsMetadata.ts");
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return _ExtrasAsMetadata__WEBPACK_IMPORTED_MODULE_10__["ExtrasAsMetadata"]; });
+
+
 
 
 
@@ -5909,7 +5979,7 @@ _glTFFileLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFFileLoader"]._CreateGLTF2Loade
 /*!***************************!*\
   !*** ./glTF/2.0/index.ts ***!
   \***************************/
-/*! exports provided: ArrayItem, GLTFLoader, EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors */
+/*! exports provided: ArrayItem, GLTFLoader, EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors, ExtrasAsMetadata */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
@@ -5940,6 +6010,8 @@ __webpack_require__.r(__webpack_exports__);
 
 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MSFT_sRGBFactors", function() { return _Extensions__WEBPACK_IMPORTED_MODULE_1__["MSFT_sRGBFactors"]; });
 
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return _Extensions__WEBPACK_IMPORTED_MODULE_1__["ExtrasAsMetadata"]; });
+
 
 
 

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


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


+ 23 - 0
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -1779,6 +1779,29 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
     }
 }
+declare module BABYLON.GLTF2.Loader.Extensions {
+    /**
+     * Store glTF extras (if present) in BJS objects' metadata
+     */
+    export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        readonly name: string;
+        /** Defines whether this extension is enabled. */
+        enabled: boolean;
+        private _loader;
+        private _assignExtras;
+        /** @hidden */
+        constructor(loader: GLTFLoader);
+        /** @hidden */
+        dispose(): void;
+        /** @hidden */
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
+        /** @hidden */
+        loadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /** @hidden */
+        createMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    }
+}
 declare module BABYLON {
     /**
      * Class reading and parsing the MTL file bundled with the obj file.

+ 74 - 2
dist/preview release/loaders/babylonjs.loaders.js

@@ -4140,6 +4140,72 @@ _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].RegisterExtension(NAME, f
 
 /***/ }),
 
+/***/ "./glTF/2.0/Extensions/ExtrasAsMetadata.ts":
+/*!*************************************************!*\
+  !*** ./glTF/2.0/Extensions/ExtrasAsMetadata.ts ***!
+  \*************************************************/
+/*! exports provided: ExtrasAsMetadata */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return ExtrasAsMetadata; });
+/* harmony import */ var _glTFLoader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../glTFLoader */ "./glTF/2.0/glTFLoader.ts");
+
+var NAME = "ExtrasAsMetadata";
+/**
+ * Store glTF extras (if present) in BJS objects' metadata
+ */
+var ExtrasAsMetadata = /** @class */ (function () {
+    /** @hidden */
+    function ExtrasAsMetadata(loader) {
+        /** The name of this extension. */
+        this.name = NAME;
+        /** Defines whether this extension is enabled. */
+        this.enabled = true;
+        this._loader = loader;
+    }
+    ExtrasAsMetadata.prototype._assignExtras = function (babylonObject, gltfProp) {
+        if (gltfProp.extras && Object.keys(gltfProp.extras).length > 0) {
+            var metadata = (babylonObject.metadata = babylonObject.metadata || {});
+            var gltf = (metadata.gltf = metadata.gltf || {});
+            gltf.extras = gltfProp.extras;
+        }
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.dispose = function () {
+        delete this._loader;
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.loadNodeAsync = function (context, node, assign) {
+        var _this = this;
+        return this._loader.loadNodeAsync(context, node, function (babylonTransformNode) {
+            _this._assignExtras(babylonTransformNode, node);
+            assign(babylonTransformNode);
+        });
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.loadCameraAsync = function (context, camera, assign) {
+        var _this = this;
+        return this._loader.loadCameraAsync(context, camera, function (babylonCamera) {
+            _this._assignExtras(babylonCamera, camera);
+            assign(babylonCamera);
+        });
+    };
+    /** @hidden */
+    ExtrasAsMetadata.prototype.createMaterial = function (context, material, babylonDrawMode) {
+        var babylonMaterial = this._loader.createMaterial(context, material, babylonDrawMode);
+        this._assignExtras(babylonMaterial, material);
+        return babylonMaterial;
+    };
+    return ExtrasAsMetadata;
+}());
+
+_glTFLoader__WEBPACK_IMPORTED_MODULE_0__["GLTFLoader"].RegisterExtension(NAME, function (loader) { return new ExtrasAsMetadata(loader); });
+
+
+/***/ }),
+
 /***/ "./glTF/2.0/Extensions/KHR_draco_mesh_compression.ts":
 /*!***********************************************************!*\
   !*** ./glTF/2.0/Extensions/KHR_draco_mesh_compression.ts ***!
@@ -5166,7 +5232,7 @@ _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].RegisterExtension(NAME, f
 /*!**************************************!*\
   !*** ./glTF/2.0/Extensions/index.ts ***!
   \**************************************/
-/*! exports provided: EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors */
+/*! exports provided: EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors, ExtrasAsMetadata */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
@@ -5201,6 +5267,10 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _MSFT_sRGBFactors__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./MSFT_sRGBFactors */ "./glTF/2.0/Extensions/MSFT_sRGBFactors.ts");
 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MSFT_sRGBFactors", function() { return _MSFT_sRGBFactors__WEBPACK_IMPORTED_MODULE_9__["MSFT_sRGBFactors"]; });
 
+/* harmony import */ var _ExtrasAsMetadata__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./ExtrasAsMetadata */ "./glTF/2.0/Extensions/ExtrasAsMetadata.ts");
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return _ExtrasAsMetadata__WEBPACK_IMPORTED_MODULE_10__["ExtrasAsMetadata"]; });
+
+
 
 
 
@@ -7251,7 +7321,7 @@ _glTFFileLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFFileLoader"]._CreateGLTF2Loade
 /*!***************************!*\
   !*** ./glTF/2.0/index.ts ***!
   \***************************/
-/*! exports provided: ArrayItem, GLTFLoader, EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors */
+/*! exports provided: ArrayItem, GLTFLoader, EXT_lights_image_based, KHR_draco_mesh_compression, KHR_lights, KHR_materials_pbrSpecularGlossiness, KHR_materials_unlit, KHR_texture_transform, MSFT_audio_emitter, MSFT_lod, MSFT_minecraftMesh, MSFT_sRGBFactors, ExtrasAsMetadata */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
@@ -7282,6 +7352,8 @@ __webpack_require__.r(__webpack_exports__);
 
 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MSFT_sRGBFactors", function() { return _Extensions__WEBPACK_IMPORTED_MODULE_1__["MSFT_sRGBFactors"]; });
 
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ExtrasAsMetadata", function() { return _Extensions__WEBPACK_IMPORTED_MODULE_1__["ExtrasAsMetadata"]; });
+
 
 
 

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


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


+ 54 - 0
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -1922,6 +1922,36 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/MSFT_sRGBFactors" {
         loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
     }
 }
+declare module "babylonjs-loaders/glTF/2.0/Extensions/ExtrasAsMetadata" {
+    import { Nullable } from "babylonjs/types";
+    import { TransformNode } from "babylonjs/Meshes/transformNode";
+    import { Camera } from "babylonjs/Cameras/camera";
+    import { INode, ICamera, IMaterial } from "babylonjs-loaders/glTF/2.0/glTFLoaderInterfaces";
+    import { IGLTFLoaderExtension } from "babylonjs-loaders/glTF/2.0/glTFLoaderExtension";
+    import { GLTFLoader } from "babylonjs-loaders/glTF/2.0/glTFLoader";
+    import { Material } from "babylonjs/Materials/material";
+    /**
+     * Store glTF extras (if present) in BJS objects' metadata
+     */
+    export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        readonly name: string;
+        /** Defines whether this extension is enabled. */
+        enabled: boolean;
+        private _loader;
+        private _assignExtras;
+        /** @hidden */
+        constructor(loader: GLTFLoader);
+        /** @hidden */
+        dispose(): void;
+        /** @hidden */
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
+        /** @hidden */
+        loadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /** @hidden */
+        createMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    }
+}
 declare module "babylonjs-loaders/glTF/2.0/Extensions/index" {
     export * from "babylonjs-loaders/glTF/2.0/Extensions/EXT_lights_image_based";
     export * from "babylonjs-loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
@@ -1933,6 +1963,7 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/index" {
     export * from "babylonjs-loaders/glTF/2.0/Extensions/MSFT_lod";
     export * from "babylonjs-loaders/glTF/2.0/Extensions/MSFT_minecraftMesh";
     export * from "babylonjs-loaders/glTF/2.0/Extensions/MSFT_sRGBFactors";
+    export * from "babylonjs-loaders/glTF/2.0/Extensions/ExtrasAsMetadata";
 }
 declare module "babylonjs-loaders/glTF/2.0/index" {
     export * from "babylonjs-loaders/glTF/2.0/glTFLoader";
@@ -4080,6 +4111,29 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
     }
 }
+declare module BABYLON.GLTF2.Loader.Extensions {
+    /**
+     * Store glTF extras (if present) in BJS objects' metadata
+     */
+    export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        readonly name: string;
+        /** Defines whether this extension is enabled. */
+        enabled: boolean;
+        private _loader;
+        private _assignExtras;
+        /** @hidden */
+        constructor(loader: GLTFLoader);
+        /** @hidden */
+        dispose(): void;
+        /** @hidden */
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
+        /** @hidden */
+        loadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /** @hidden */
+        createMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    }
+}
 declare module BABYLON {
     /**
      * Class reading and parsing the MTL file bundled with the obj file.

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

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

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

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

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

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

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

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

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

@@ -1 +1 @@
-{"engineOnly":251951,"sceneOnly":509837,"minGridMaterial":638707,"minStandardMaterial":764718}
+{"engineOnly":251951,"sceneOnly":509990,"minGridMaterial":638864,"minStandardMaterial":764875}

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

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

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

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

+ 15 - 8
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -1210,9 +1210,8 @@ var _Exporter = /** @class */ (function () {
         this._animations = [];
         this._imageData = {};
         this._convertToRightHandedSystem = this._babylonScene.useRightHandedSystem ? false : true;
-        var _options = options || {};
-        this._shouldExportNode = _options.shouldExportNode ? _options.shouldExportNode : function (babylonNode) { return true; };
-        this._animationSampleRate = _options.animationSampleRate ? _options.animationSampleRate : 1 / 60;
+        this._options = options || {};
+        this._animationSampleRate = options && options.animationSampleRate ? options.animationSampleRate : 1 / 60;
         this._glTFMaterialExporter = new _glTFMaterialExporter__WEBPACK_IMPORTED_MODULE_1__["_GLTFMaterialExporter"](this);
         this._loadExtensions();
     }
@@ -2210,7 +2209,7 @@ var _Exporter = /** @class */ (function () {
         var directDescendents;
         var nodes = babylonScene.transformNodes.concat(babylonScene.meshes, babylonScene.lights);
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, "image/png" /* PNG */, true).then(function () {
-            return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, _this._shouldExportNode, binaryWriter).then(function (nodeMap) {
+            return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then(function (nodeMap) {
                 _this._nodeMap = nodeMap;
                 _this._totalByteLength = binaryWriter.getByteOffset();
                 if (_this._totalByteLength == undefined) {
@@ -2222,8 +2221,16 @@ var _Exporter = /** @class */ (function () {
                     glTFNodeIndex = _this._nodeMap[babylonNode.uniqueId];
                     if (glTFNodeIndex !== undefined) {
                         glTFNode = _this._nodes[glTFNodeIndex];
+                        if (babylonNode.metadata) {
+                            if (_this._options.metadataSelector) {
+                                glTFNode.extras = _this._options.metadataSelector(babylonNode.metadata);
+                            }
+                            else if (babylonNode.metadata.gltf) {
+                                glTFNode.extras = babylonNode.metadata.gltf.extras;
+                            }
+                        }
                         if (!babylonNode.parent) {
-                            if (!_this._shouldExportNode(babylonNode)) {
+                            if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
                                 babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
                             }
                             else {
@@ -2262,11 +2269,10 @@ var _Exporter = /** @class */ (function () {
      * Creates a mapping of Node unique id to node index and handles animations
      * @param babylonScene Babylon Scene
      * @param nodes Babylon transform nodes
-     * @param shouldExportNode Callback specifying if a transform node should be exported
      * @param binaryWriter Buffer to write binary data to
      * @returns Node mapping of unique id to index
      */
-    _Exporter.prototype.createNodeMapAndAnimationsAsync = function (babylonScene, nodes, shouldExportNode, binaryWriter) {
+    _Exporter.prototype.createNodeMapAndAnimationsAsync = function (babylonScene, nodes, binaryWriter) {
         var _this = this;
         var promiseChain = Promise.resolve();
         var nodeMap = {};
@@ -2278,7 +2284,7 @@ var _Exporter = /** @class */ (function () {
         };
         var idleGLTFAnimations = [];
         var _loop_1 = function (babylonNode) {
-            if (shouldExportNode(babylonNode)) {
+            if (!this_1._options.shouldExportNode || this_1._options.shouldExportNode(babylonNode)) {
                 promiseChain = promiseChain.then(function () {
                     return _this.createNodeAsync(babylonNode, binaryWriter).then(function (node) {
                         var promise = _this._extensionsPostExportNodeAsync("createNodeAsync", node, babylonNode);
@@ -2306,6 +2312,7 @@ var _Exporter = /** @class */ (function () {
                 "Excluding node " + babylonNode.name;
             }
         };
+        var this_1 = this;
         for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
             var babylonNode = nodes_2[_i];
             _loop_1(babylonNode);

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


+ 7 - 5
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -335,6 +335,12 @@ declare module BABYLON {
          */
         shouldExportNode?(node: Node): boolean;
         /**
+         * Function used to extract the part of node's metadata that will be exported into glTF node extras
+         * @param metadata source metadata to read from
+         * @returns the data to store to glTF node extras
+         */
+        metadataSelector?(metadata: any): any;
+        /**
          * The sample rate to bake animation curves
          */
         animationSampleRate?: number;
@@ -548,10 +554,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Baked animation sample rate
          */
         private _animationSampleRate;
-        /**
-         * Callback which specifies if a node should be exported or not
-         */
-        private _shouldExportNode;
+        private _options;
         private _localEngine;
         _glTFMaterialExporter: _GLTFMaterialExporter;
         private _extensions;
@@ -738,7 +741,6 @@ declare module BABYLON.GLTF2.Exporter {
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene
          * @param nodes Babylon transform nodes
-         * @param shouldExportNode Callback specifying if a transform node should be exported
          * @param binaryWriter Buffer to write binary data to
          * @returns Node mapping of unique id to index
          */

+ 15 - 8
dist/preview release/serializers/babylonjs.serializers.js

@@ -1388,9 +1388,8 @@ var _Exporter = /** @class */ (function () {
         this._animations = [];
         this._imageData = {};
         this._convertToRightHandedSystem = this._babylonScene.useRightHandedSystem ? false : true;
-        var _options = options || {};
-        this._shouldExportNode = _options.shouldExportNode ? _options.shouldExportNode : function (babylonNode) { return true; };
-        this._animationSampleRate = _options.animationSampleRate ? _options.animationSampleRate : 1 / 60;
+        this._options = options || {};
+        this._animationSampleRate = options && options.animationSampleRate ? options.animationSampleRate : 1 / 60;
         this._glTFMaterialExporter = new _glTFMaterialExporter__WEBPACK_IMPORTED_MODULE_1__["_GLTFMaterialExporter"](this);
         this._loadExtensions();
     }
@@ -2388,7 +2387,7 @@ var _Exporter = /** @class */ (function () {
         var directDescendents;
         var nodes = babylonScene.transformNodes.concat(babylonScene.meshes, babylonScene.lights);
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, "image/png" /* PNG */, true).then(function () {
-            return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, _this._shouldExportNode, binaryWriter).then(function (nodeMap) {
+            return _this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then(function (nodeMap) {
                 _this._nodeMap = nodeMap;
                 _this._totalByteLength = binaryWriter.getByteOffset();
                 if (_this._totalByteLength == undefined) {
@@ -2400,8 +2399,16 @@ var _Exporter = /** @class */ (function () {
                     glTFNodeIndex = _this._nodeMap[babylonNode.uniqueId];
                     if (glTFNodeIndex !== undefined) {
                         glTFNode = _this._nodes[glTFNodeIndex];
+                        if (babylonNode.metadata) {
+                            if (_this._options.metadataSelector) {
+                                glTFNode.extras = _this._options.metadataSelector(babylonNode.metadata);
+                            }
+                            else if (babylonNode.metadata.gltf) {
+                                glTFNode.extras = babylonNode.metadata.gltf.extras;
+                            }
+                        }
                         if (!babylonNode.parent) {
-                            if (!_this._shouldExportNode(babylonNode)) {
+                            if (_this._options.shouldExportNode && !_this._options.shouldExportNode(babylonNode)) {
                                 babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["Tools"].Log("Omitting " + babylonNode.name + " from scene.");
                             }
                             else {
@@ -2440,11 +2447,10 @@ var _Exporter = /** @class */ (function () {
      * Creates a mapping of Node unique id to node index and handles animations
      * @param babylonScene Babylon Scene
      * @param nodes Babylon transform nodes
-     * @param shouldExportNode Callback specifying if a transform node should be exported
      * @param binaryWriter Buffer to write binary data to
      * @returns Node mapping of unique id to index
      */
-    _Exporter.prototype.createNodeMapAndAnimationsAsync = function (babylonScene, nodes, shouldExportNode, binaryWriter) {
+    _Exporter.prototype.createNodeMapAndAnimationsAsync = function (babylonScene, nodes, binaryWriter) {
         var _this = this;
         var promiseChain = Promise.resolve();
         var nodeMap = {};
@@ -2456,7 +2462,7 @@ var _Exporter = /** @class */ (function () {
         };
         var idleGLTFAnimations = [];
         var _loop_1 = function (babylonNode) {
-            if (shouldExportNode(babylonNode)) {
+            if (!this_1._options.shouldExportNode || this_1._options.shouldExportNode(babylonNode)) {
                 promiseChain = promiseChain.then(function () {
                     return _this.createNodeAsync(babylonNode, binaryWriter).then(function (node) {
                         var promise = _this._extensionsPostExportNodeAsync("createNodeAsync", node, babylonNode);
@@ -2484,6 +2490,7 @@ var _Exporter = /** @class */ (function () {
                 "Excluding node " + babylonNode.name;
             }
         };
+        var this_1 = this;
         for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
             var babylonNode = nodes_2[_i];
             _loop_1(babylonNode);

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


+ 14 - 10
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -358,6 +358,12 @@ declare module "babylonjs-serializers/glTF/2.0/glTFSerializer" {
          */
         shouldExportNode?(node: Node): boolean;
         /**
+         * Function used to extract the part of node's metadata that will be exported into glTF node extras
+         * @param metadata source metadata to read from
+         * @returns the data to store to glTF node extras
+         */
+        metadataSelector?(metadata: any): any;
+        /**
          * The sample rate to bake animation curves
          */
         animationSampleRate?: number;
@@ -587,10 +593,7 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * Baked animation sample rate
          */
         private _animationSampleRate;
-        /**
-         * Callback which specifies if a node should be exported or not
-         */
-        private _shouldExportNode;
+        private _options;
         private _localEngine;
         _glTFMaterialExporter: _GLTFMaterialExporter;
         private _extensions;
@@ -777,7 +780,6 @@ declare module "babylonjs-serializers/glTF/2.0/glTFExporter" {
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene
          * @param nodes Babylon transform nodes
-         * @param shouldExportNode Callback specifying if a transform node should be exported
          * @param binaryWriter Buffer to write binary data to
          * @returns Node mapping of unique id to index
          */
@@ -1513,6 +1515,12 @@ declare module BABYLON {
          */
         shouldExportNode?(node: Node): boolean;
         /**
+         * Function used to extract the part of node's metadata that will be exported into glTF node extras
+         * @param metadata source metadata to read from
+         * @returns the data to store to glTF node extras
+         */
+        metadataSelector?(metadata: any): any;
+        /**
          * The sample rate to bake animation curves
          */
         animationSampleRate?: number;
@@ -1726,10 +1734,7 @@ declare module BABYLON.GLTF2.Exporter {
          * Baked animation sample rate
          */
         private _animationSampleRate;
-        /**
-         * Callback which specifies if a node should be exported or not
-         */
-        private _shouldExportNode;
+        private _options;
         private _localEngine;
         _glTFMaterialExporter: _GLTFMaterialExporter;
         private _extensions;
@@ -1916,7 +1921,6 @@ declare module BABYLON.GLTF2.Exporter {
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene
          * @param nodes Babylon transform nodes
-         * @param shouldExportNode Callback specifying if a transform node should be exported
          * @param binaryWriter Buffer to write binary data to
          * @returns Node mapping of unique id to index
          */

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

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

+ 22 - 8
dist/preview release/viewer/babylon.module.d.ts

@@ -6731,7 +6731,7 @@ declare module "babylonjs/Animations/easing" {
          */
         easeInCore(gradient: number): number;
         /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
+         * Given an input gradient between 0 and 1, this returns the corresponding value
          * of the easing function.
          * @param gradient Defines the value between 0 and 1 we want the easing value for
          * @returns the corresponding value on the curve defined by the easing function
@@ -8056,7 +8056,6 @@ declare module "babylonjs/Cameras/targetCamera" {
         _computeLocalCameraSpeed(): number;
         /**
          * Defines the target the camera should look at.
-         * This will automatically adapt alpha beta and radius to fit within the new target.
          * @param target Defines the new target as a Vector or a mesh
          */
         setTarget(target: Vector3): void;
@@ -25612,7 +25611,9 @@ declare module "babylonjs/Meshes/abstractMesh" {
          */
         isVerticesDataPresent(kind: string): boolean;
         /**
-         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+         * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+         * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
          * @returns a BoundingInfo
          */
         getBoundingInfo(): BoundingInfo;
@@ -31423,7 +31424,7 @@ declare module "babylonjs/Cameras/camera" {
          */
         inertia: number;
         /**
-         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
          */
         mode: number;
         /**
@@ -34419,6 +34420,12 @@ declare module "babylonjs/scene" {
          */
         getMaterialByID(id: string): Nullable<Material>;
         /**
+         * Gets a the last added material using a given id
+         * @param id defines the material's ID
+         * @return the last material with the given id or null if none found.
+         */
+        getLastMaterialByID(id: string): Nullable<Material>;
+        /**
          * Gets a material using its name
          * @param name defines the material's name
          * @return the material or null if none found.
@@ -69693,7 +69700,7 @@ declare module BABYLON {
          */
         easeInCore(gradient: number): number;
         /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
+         * Given an input gradient between 0 and 1, this returns the corresponding value
          * of the easing function.
          * @param gradient Defines the value between 0 and 1 we want the easing value for
          * @returns the corresponding value on the curve defined by the easing function
@@ -70985,7 +70992,6 @@ declare module BABYLON {
         _computeLocalCameraSpeed(): number;
         /**
          * Defines the target the camera should look at.
-         * This will automatically adapt alpha beta and radius to fit within the new target.
          * @param target Defines the new target as a Vector or a mesh
          */
         setTarget(target: Vector3): void;
@@ -87934,7 +87940,9 @@ declare module BABYLON {
          */
         isVerticesDataPresent(kind: string): boolean;
         /**
-         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+         * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+         * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+         * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
          * @returns a BoundingInfo
          */
         getBoundingInfo(): BoundingInfo;
@@ -93621,7 +93629,7 @@ declare module BABYLON {
          */
         inertia: number;
         /**
-         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+         * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
          */
         mode: number;
         /**
@@ -96546,6 +96554,12 @@ declare module BABYLON {
          */
         getMaterialByID(id: string): Nullable<Material>;
         /**
+         * Gets a the last added material using a given id
+         * @param id defines the material's ID
+         * @return the last material with the given id or null if none found.
+         */
+        getLastMaterialByID(id: string): Nullable<Material>;
+        /**
          * Gets a material using its name
          * @param name defines the material's name
          * @return the material or null if none found.

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


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


+ 54 - 0
dist/preview release/viewer/babylonjs.loaders.module.d.ts

@@ -1922,6 +1922,36 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/MSFT_sRGBFactors" {
         loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
     }
 }
+declare module "babylonjs-loaders/glTF/2.0/Extensions/ExtrasAsMetadata" {
+    import { Nullable } from "babylonjs/types";
+    import { TransformNode } from "babylonjs/Meshes/transformNode";
+    import { Camera } from "babylonjs/Cameras/camera";
+    import { INode, ICamera, IMaterial } from "babylonjs-loaders/glTF/2.0/glTFLoaderInterfaces";
+    import { IGLTFLoaderExtension } from "babylonjs-loaders/glTF/2.0/glTFLoaderExtension";
+    import { GLTFLoader } from "babylonjs-loaders/glTF/2.0/glTFLoader";
+    import { Material } from "babylonjs/Materials/material";
+    /**
+     * Store glTF extras (if present) in BJS objects' metadata
+     */
+    export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        readonly name: string;
+        /** Defines whether this extension is enabled. */
+        enabled: boolean;
+        private _loader;
+        private _assignExtras;
+        /** @hidden */
+        constructor(loader: GLTFLoader);
+        /** @hidden */
+        dispose(): void;
+        /** @hidden */
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
+        /** @hidden */
+        loadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /** @hidden */
+        createMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    }
+}
 declare module "babylonjs-loaders/glTF/2.0/Extensions/index" {
     export * from "babylonjs-loaders/glTF/2.0/Extensions/EXT_lights_image_based";
     export * from "babylonjs-loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
@@ -1933,6 +1963,7 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/index" {
     export * from "babylonjs-loaders/glTF/2.0/Extensions/MSFT_lod";
     export * from "babylonjs-loaders/glTF/2.0/Extensions/MSFT_minecraftMesh";
     export * from "babylonjs-loaders/glTF/2.0/Extensions/MSFT_sRGBFactors";
+    export * from "babylonjs-loaders/glTF/2.0/Extensions/ExtrasAsMetadata";
 }
 declare module "babylonjs-loaders/glTF/2.0/index" {
     export * from "babylonjs-loaders/glTF/2.0/glTFLoader";
@@ -4080,6 +4111,29 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
     }
 }
+declare module BABYLON.GLTF2.Loader.Extensions {
+    /**
+     * Store glTF extras (if present) in BJS objects' metadata
+     */
+    export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        readonly name: string;
+        /** Defines whether this extension is enabled. */
+        enabled: boolean;
+        private _loader;
+        private _assignExtras;
+        /** @hidden */
+        constructor(loader: GLTFLoader);
+        /** @hidden */
+        dispose(): void;
+        /** @hidden */
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
+        /** @hidden */
+        loadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /** @hidden */
+        createMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    }
+}
 declare module BABYLON {
     /**
      * Class reading and parsing the MTL file bundled with the obj file.

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

@@ -21,6 +21,7 @@
 - Method to check if device orientation is available ([TrevorDev](https://github.com/TrevorDev))
 - Added support for sound sprites [Doc](https://doc.babylonjs.com/how_to/playing_sounds_and_music#playing-a-sound-sprite) ([Deltakosh](https://github.com/deltakosh/))
 - Display Oculus Quest controller when using a Quest in WebVR ([TrevorDev](https://github.com/TrevorDev))
+- Added startAndReleaseDragOnPointerEvents property to pointerDragBehavior which can be set to false when using custom drag triggering ([TrevorDev](https://github.com/TrevorDev))
 
 ### Engine
 - Morph targets now can morph UV channel as well ([Deltakosh](https://github.com/deltakosh/))
@@ -58,6 +59,7 @@
 ### Loaders
 - Added support for non-float accessors in animation data for glTF loader. ([bghgary](https://github.com/bghgary))
 - Support loading cube data in .basis loader ([TrevorDev](https://github.com/TrevorDev))
+- Load glTF extras into BJS metadata ([pjoe](https://github.com/pjoe))
 
 ### Materials
 - Added `ShaderMaterial.setColor4Array` ([JonathanTron](https://github.com/JonathanTron/))
@@ -66,6 +68,9 @@
 ### Sounds
 - Added `ISoundOptions.skipCodecCheck` to make `Sound` more flexible with URLs ([nbduke](https://github.com/nbduke))
 
+### Documentation
+- Added a note on shallow bounding of getBoundingInfo ([tibotiber](https://github.com/tibotiber))
+
 ## Bug fixes
 - Added support for `AnimationGroup` serialization ([Drigax](https://github.com/drigax/))
 - Removing assetContainer from scene will also remove gui layers ([TrevorDev](https://github.com/TrevorDev))
@@ -78,6 +83,7 @@
 - Avoid using default utility layer in gizmo manager to support multiple scenes ([TrevorDev](https://github.com/TrevorDev))
 - Fix bug when adding and removing observers in quick succession ([sable](https://github.com/thscott))
 - Cannon and Ammo forceUpdate will no longer cause an unexpected exception ([TrevorDev](https://github.com/TrevorDev))
+- Loading the same multi-material twice and disposing one should not impact the other ([TrevorDev](https://github.com/TrevorDev))
 
 ## Breaking changes
 - Setting mesh.scaling to a new vector will no longer automatically call forceUpdate (this should be done manually when needed) ([TrevorDev](https://github.com/TrevorDev))

+ 101 - 0
loaders/src/glTF/2.0/Extensions/ExtrasAsMetadata.ts

@@ -0,0 +1,101 @@
+import { Nullable } from "babylonjs/types";
+import { TransformNode } from "babylonjs/Meshes/transformNode";
+import { Camera } from "babylonjs/Cameras/camera";
+
+import { IProperty } from "babylonjs-gltf2interface";
+import { INode, ICamera, IMaterial } from "../glTFLoaderInterfaces";
+import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
+import { GLTFLoader } from "../glTFLoader";
+import { Material } from "babylonjs/Materials/material";
+
+const NAME = "ExtrasAsMetadata";
+
+interface ObjectWithMetadata {
+    metadata: any;
+}
+
+/**
+ * Store glTF extras (if present) in BJS objects' metadata
+ */
+export class ExtrasAsMetadata implements IGLTFLoaderExtension {
+    /** The name of this extension. */
+    public readonly name = NAME;
+
+    /** Defines whether this extension is enabled. */
+    public enabled = true;
+
+    private _loader: GLTFLoader;
+
+    private _assignExtras(
+        babylonObject: ObjectWithMetadata,
+        gltfProp: IProperty
+    ): void {
+        if (gltfProp.extras && Object.keys(gltfProp.extras).length > 0) {
+            const metadata = (babylonObject.metadata = babylonObject.metadata || {});
+            const gltf = (metadata.gltf = metadata.gltf || {});
+            gltf.extras = gltfProp.extras;
+        }
+    }
+
+    /** @hidden */
+    public constructor(loader: GLTFLoader) {
+        this._loader = loader;
+    }
+
+    /** @hidden */
+    public dispose(): void {
+        delete this._loader;
+    }
+
+    /** @hidden */
+    public loadNodeAsync(
+        context: string,
+        node: INode,
+        assign: (babylonTransformNode: TransformNode) => void
+    ): Nullable<Promise<TransformNode>> {
+        return this._loader.loadNodeAsync(
+            context,
+            node,
+            (babylonTransformNode): void => {
+                this._assignExtras(babylonTransformNode, node);
+                assign(babylonTransformNode);
+            }
+        );
+    }
+
+    /** @hidden */
+    public loadCameraAsync(
+        context: string,
+        camera: ICamera,
+        assign: (babylonCamera: Camera) => void
+    ): Nullable<Promise<Camera>> {
+        return this._loader.loadCameraAsync(
+            context,
+            camera,
+            (babylonCamera): void => {
+                this._assignExtras(babylonCamera, camera);
+                assign(babylonCamera);
+            }
+        );
+    }
+
+    /** @hidden */
+    public createMaterial(
+        context: string,
+        material: IMaterial,
+        babylonDrawMode: number
+    ): Nullable<Material> {
+        const babylonMaterial = this._loader.createMaterial(
+            context,
+            material,
+            babylonDrawMode
+        );
+        this._assignExtras(babylonMaterial, material);
+        return babylonMaterial;
+    }
+}
+
+GLTFLoader.RegisterExtension(
+    NAME,
+    (loader): IGLTFLoaderExtension => new ExtrasAsMetadata(loader)
+);

+ 2 - 1
loaders/src/glTF/2.0/Extensions/index.ts

@@ -7,4 +7,5 @@ export * from "./KHR_texture_transform";
 export * from "./MSFT_audio_emitter";
 export * from "./MSFT_lod";
 export * from "./MSFT_minecraftMesh";
-export * from "./MSFT_sRGBFactors";
+export * from "./MSFT_sRGBFactors";
+export * from "./ExtrasAsMetadata";

+ 1 - 1
package.json

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

+ 2 - 2
sandbox/animation.js

@@ -21,7 +21,7 @@ function formatId(name) {
 
 function displayDropdownContent(display) {
     if (display) {
-        dropdownContent.style.display = "flex";
+        dropdownContent.style.display = "block";
         chevronDown.style.display = "inline";
         chevronUp.style.display = "none";
         dropdownBtn.classList.add("open");
@@ -36,7 +36,7 @@ function displayDropdownContent(display) {
     }
 }
 dropdownBtn.addEventListener("click", function() {
-    if (dropdownContent.style.display === "flex") {
+    if (dropdownContent.style.display === "block") {
         displayDropdownContent(false);
     }
     else {

+ 2 - 0
sandbox/index.css

@@ -386,6 +386,8 @@ a:visited {
     bottom: var(--footer-height);
     min-width: 135px;
     width: 200px;
+    max-height: 50vh;
+    overflow-y: auto;
     flex-direction: column;
     transition: all 0.3s ease; /* Add transition for hover effects */
 }

+ 16 - 12
serializers/src/glTF/2.0/glTFExporter.ts

@@ -131,10 +131,7 @@ export class _Exporter {
      */
     private _animationSampleRate: number;
 
-    /**
-     * Callback which specifies if a node should be exported or not
-     */
-    private _shouldExportNode: ((babylonNode: Node) => boolean);
+    private _options: IExportOptions;
 
     private _localEngine: Engine;
 
@@ -230,9 +227,8 @@ export class _Exporter {
         this._animations = [];
         this._imageData = {};
         this._convertToRightHandedSystem = this._babylonScene.useRightHandedSystem ? false : true;
-        const _options = options || {};
-        this._shouldExportNode = _options.shouldExportNode ? _options.shouldExportNode : (babylonNode: Node) => true;
-        this._animationSampleRate = _options.animationSampleRate ? _options.animationSampleRate : 1 / 60;
+        this._options = options || {};
+        this._animationSampleRate = options && options.animationSampleRate ? options.animationSampleRate : 1 / 60;
 
         this._glTFMaterialExporter = new _GLTFMaterialExporter(this);
         this._loadExtensions();
@@ -1246,7 +1242,7 @@ export class _Exporter {
         const nodes: Node[] = [...babylonScene.transformNodes, ...babylonScene.meshes, ...babylonScene.lights];
 
         return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(babylonScene.materials, ImageMimeType.PNG, true).then(() => {
-            return this.createNodeMapAndAnimationsAsync(babylonScene, nodes, this._shouldExportNode, binaryWriter).then((nodeMap) => {
+            return this.createNodeMapAndAnimationsAsync(babylonScene, nodes, binaryWriter).then((nodeMap) => {
                 this._nodeMap = nodeMap;
 
                 this._totalByteLength = binaryWriter.getByteOffset();
@@ -1259,8 +1255,17 @@ export class _Exporter {
                     glTFNodeIndex = this._nodeMap[babylonNode.uniqueId];
                     if (glTFNodeIndex !== undefined) {
                         glTFNode = this._nodes[glTFNodeIndex];
+
+                        if (babylonNode.metadata) {
+                            if (this._options.metadataSelector) {
+                                glTFNode.extras = this._options.metadataSelector(babylonNode.metadata);
+                            } else if (babylonNode.metadata.gltf) {
+                                glTFNode.extras = babylonNode.metadata.gltf.extras;
+                            }
+                        }
+
                         if (!babylonNode.parent) {
-                            if (!this._shouldExportNode(babylonNode)) {
+                            if (this._options.shouldExportNode && !this._options.shouldExportNode(babylonNode)) {
                                 Tools.Log("Omitting " + babylonNode.name + " from scene.");
                             }
                             else {
@@ -1301,11 +1306,10 @@ export class _Exporter {
      * Creates a mapping of Node unique id to node index and handles animations
      * @param babylonScene Babylon Scene
      * @param nodes Babylon transform nodes
-     * @param shouldExportNode Callback specifying if a transform node should be exported
      * @param binaryWriter Buffer to write binary data to
      * @returns Node mapping of unique id to index
      */
-    private createNodeMapAndAnimationsAsync(babylonScene: Scene, nodes: Node[], shouldExportNode: (babylonNode: Node) => boolean, binaryWriter: _BinaryWriter): Promise<{ [key: number]: number }> {
+    private createNodeMapAndAnimationsAsync(babylonScene: Scene, nodes: Node[], binaryWriter: _BinaryWriter): Promise<{ [key: number]: number }> {
         let promiseChain = Promise.resolve();
         const nodeMap: { [key: number]: number } = {};
         let nodeIndex: number;
@@ -1317,7 +1321,7 @@ export class _Exporter {
         let idleGLTFAnimations: IAnimation[] = [];
 
         for (let babylonNode of nodes) {
-            if (shouldExportNode(babylonNode)) {
+            if (!this._options.shouldExportNode || this._options.shouldExportNode(babylonNode)) {
                 promiseChain = promiseChain.then(() => {
                     return this.createNodeAsync(babylonNode, binaryWriter).then((node) => {
                         const promise = this._extensionsPostExportNodeAsync("createNodeAsync", node, babylonNode);

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

@@ -13,6 +13,14 @@ export interface IExportOptions {
      * @returns boolean, which indicates whether the node should be exported (true) or not (false)
      */
     shouldExportNode?(node: Node): boolean;
+
+    /**
+     * Function used to extract the part of node's metadata that will be exported into glTF node extras
+     * @param metadata source metadata to read from
+     * @returns the data to store to glTF node extras
+     */
+    metadataSelector?(metadata: any): any;
+
     /**
      * The sample rate to bake animation curves
      */

+ 1 - 1
src/Animations/easing.ts

@@ -63,7 +63,7 @@ export class EasingFunction implements IEasingFunction {
     }
 
     /**
-     * Given an input gradient between 0 and 1, this returns the corrseponding value
+     * Given an input gradient between 0 and 1, this returns the corresponding value
      * of the easing function.
      * @param gradient Defines the value between 0 and 1 we want the easing value for
      * @returns the corresponding value on the curve defined by the easing function

+ 7 - 2
src/Behaviors/Meshes/pointerDragBehavior.ts

@@ -82,6 +82,11 @@ export class PointerDragBehavior implements Behavior<AbstractMesh> {
      *  If the drag behavior will react to drag events (Default: true)
      */
     public enabled = true;
+
+    /**
+     * If pointer events should start and release the drag (Default: true)
+     */
+    public startAndReleaseDragOnPointerEvents = true;
     /**
      * If camera controls should be detached during the drag
      */
@@ -171,11 +176,11 @@ export class PointerDragBehavior implements Behavior<AbstractMesh> {
 
             if (pointerInfo.type == PointerEventTypes.POINTERDOWN) {
 
-                if (!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.pickedPoint && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
+                if (this.startAndReleaseDragOnPointerEvents && !this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.pickedPoint && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
                     this._startDrag((<PointerEvent>pointerInfo.event).pointerId, pointerInfo.pickInfo.ray, pointerInfo.pickInfo.pickedPoint);
                 }
             } else if (pointerInfo.type == PointerEventTypes.POINTERUP) {
-                if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId) {
+                if (this.startAndReleaseDragOnPointerEvents && this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId) {
                     this.releaseDrag();
                 }
             } else if (pointerInfo.type == PointerEventTypes.POINTERMOVE) {

+ 1 - 1
src/Cameras/camera.ts

@@ -177,7 +177,7 @@ export class Camera extends Node {
     public inertia = 0.9;
 
     /**
-     * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
+     * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
      */
     @serialize()
     public mode = Camera.PERSPECTIVE_CAMERA;

+ 0 - 1
src/Cameras/targetCamera.ts

@@ -219,7 +219,6 @@ export class TargetCamera extends Camera {
 
     /**
      * Defines the target the camera should look at.
-     * This will automatically adapt alpha beta and radius to fit within the new target.
      * @param target Defines the new target as a Vector or a mesh
      */
     public setTarget(target: Vector3): void {

+ 2 - 2
src/Engines/engine.ts

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

+ 1 - 1
src/Gamepads/Controllers/oculusTouchController.ts

@@ -84,7 +84,7 @@ export class OculusTouchController extends WebVRController {
             - button_enter
             */
 
-            this._defaultModel = newMeshes[1];
+            this._defaultModel = OculusTouchController._IsQuest ? newMeshes[0] : newMeshes[1];
             this.attachToMesh(this._defaultModel);
             if (meshLoaded) {
                 meshLoaded(this._defaultModel);

+ 3 - 1
src/Materials/multiMaterial.ts

@@ -235,7 +235,9 @@ export class MultiMaterial extends Material {
             var subMatId = parsedMultiMaterial.materials[matIndex];
 
             if (subMatId) {
-                multiMaterial.subMaterials.push(scene.getMaterialByID(subMatId));
+                // If the same multimaterial is loaded twice, the 2nd multimaterial needs to reference the latest material by that id which
+                // is why this lookup should use getLastMaterialByID instead of getMaterialByID
+                multiMaterial.subMaterials.push(scene.getLastMaterialByID(subMatId));
             } else {
                 multiMaterial.subMaterials.push(null);
             }

+ 3 - 1
src/Meshes/abstractMesh.ts

@@ -958,7 +958,9 @@ export class AbstractMesh extends TransformNode implements IDisposable, ICullabl
     }
 
     /**
-     * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
+     * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined.
+     * Note that it returns a shallow bounding of the mesh (i.e. it does not include children).
+     * To get the full bounding of all children, call `getHierarchyBoundingVectors` instead.
      * @returns a BoundingInfo
      */
     public getBoundingInfo(): BoundingInfo {

+ 2 - 2
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -279,7 +279,7 @@ void main() {
     float s = 2.0 * PI * randoms2.x;
 
     #ifdef CONEEMITTERSPAWNPOINT
-        float h = 0.00001;
+        float h = 0.0001;
     #else
         float h = randoms2.y * height.y;
         
@@ -301,7 +301,7 @@ void main() {
         direction = vec3(0., 1.0, 0.);
     } else {
         vec3 randoms3 = getRandomVec3(seed.z);
-        direction = position + directionRandomizer * randoms3;
+        direction = normalize(position + directionRandomizer * randoms3);        
     }
 #else    
     // Create the particle at origin

+ 15 - 0
src/scene.ts

@@ -2449,6 +2449,21 @@ export class Scene extends AbstractScene implements IAnimatable {
     }
 
     /**
+     * Gets a the last added material using a given id
+     * @param id defines the material's ID
+     * @return the last material with the given id or null if none found.
+     */
+    public getLastMaterialByID(id: string): Nullable<Material> {
+        for (var index = this.materials.length - 1; index >= 0; index--) {
+            if (this.materials[index].id === id) {
+                return this.materials[index];
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Gets a material using its name
      * @param name defines the material's name
      * @return the material or null if none found.

+ 28 - 0
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -513,6 +513,34 @@ describe('Babylon Scene Loader', function() {
             });
         });
 
+        it('Load Box with extras', () => {
+            const scene = new BABYLON.Scene(subject);
+            return BABYLON.SceneLoader.AppendAsync("/Playground/scenes/Box/", "Box_extras.gltf", scene).then((scene) => {
+                expect(scene.meshes.length, "scene.meshes.length").to.equal(2);
+                expect(scene.materials.length, "scene.materials.length").to.equal(1);
+                const mesh = scene.getMeshByName("Box001");
+                expect(mesh, "Box001").to.exist;
+                expect(mesh.metadata, "Box001 metadata").to.exist;
+                expect(mesh.metadata.gltf, "Box001 metadata.gltf").to.exist;
+                expect(mesh.metadata.gltf.extras, "Box001 metadata.gltf.extras").to.exist;
+                expect(mesh.metadata.gltf.extras.kind, "Box001 extras.kind").to.equal("nice cube");
+                expect(mesh.metadata.gltf.extras.magic, "Box001 extras.magic").to.equal(42);
+                const camera = scene.getCameraByName("Camera");
+                expect(camera, "Camera").to.exist;
+                expect(camera.metadata, "Camera metadata").to.exist;
+                expect(mesh.metadata.gltf, "Camera metadata.gltf").to.exist;
+                expect(mesh.metadata.gltf.extras, "Camera metadata.gltf.extras").to.exist;
+                expect(camera.metadata.gltf.extras.custom, "Camera extras.custom").to.equal("cameraProp");
+                const material = scene.getMaterialByName("01___Default")
+                expect(material, "Material").to.exist;
+                expect(material.metadata, "Material metadata").to.exist;
+                expect(mesh.metadata.gltf, "Material metadata.gltf").to.exist;
+                expect(mesh.metadata.gltf.extras, "Material metadata.gltf.extras").to.exist;
+                expect(material.metadata.gltf.extras.custom, "Material extras.custom").to.equal("materialProp");
+
+            });
+        });
+
         // TODO: test animation group callback
         // TODO: test material instancing
         // TODO: test KHR_materials_pbrSpecularGlossiness