Browse Source

Merge remote-tracking branch 'upstream/master' into cannonGoFlat

Raanan Weber 5 years ago
parent
commit
ed483cefec
93 changed files with 186816 additions and 162392 deletions
  1. 180950 161222
      Playground/js/babylonWebGpu.max.js
  2. 1 1
      Playground/js/babylonWebGpu.max.js.map
  3. 425 35
      dist/preview release/babylon.d.ts
  4. 1 1
      dist/preview release/babylon.js
  5. 850 180
      dist/preview release/babylon.max.js
  6. 1 1
      dist/preview release/babylon.max.js.map
  7. 865 72
      dist/preview release/babylon.module.d.ts
  8. 427 35
      dist/preview release/documentation.d.ts
  9. 1 1
      dist/preview release/glTF2Interface/package.json
  10. 2 2
      dist/preview release/gui/package.json
  11. 5 5
      dist/preview release/inspector/babylon.inspector.bundle.js
  12. 372 54
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  13. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  14. 41 0
      dist/preview release/inspector/babylon.inspector.d.ts
  15. 83 0
      dist/preview release/inspector/babylon.inspector.module.d.ts
  16. 7 7
      dist/preview release/inspector/package.json
  17. 51 51
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  18. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  19. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  20. 51 51
      dist/preview release/loaders/babylon.glTFFileLoader.js
  21. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  22. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  23. 51 51
      dist/preview release/loaders/babylonjs.loaders.js
  24. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  25. 2 2
      dist/preview release/loaders/babylonjs.loaders.min.js
  26. 3 3
      dist/preview release/loaders/package.json
  27. 30 4
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js
  28. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js.map
  29. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  30. 2 0
      dist/preview release/materialsLibrary/babylonjs.materials.d.ts
  31. 30 4
      dist/preview release/materialsLibrary/babylonjs.materials.js
  32. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.js.map
  33. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  34. 4 0
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  35. 2 2
      dist/preview release/materialsLibrary/package.json
  36. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  37. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  38. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  39. 2 2
      dist/preview release/nodeEditor/package.json
  40. 1 1
      dist/preview release/package.json
  41. 2 2
      dist/preview release/postProcessesLibrary/package.json
  42. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  43. 3 3
      dist/preview release/serializers/package.json
  44. 865 72
      dist/preview release/viewer/babylon.module.d.ts
  45. 88 80
      dist/preview release/viewer/babylon.viewer.js
  46. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  47. 4 1
      dist/preview release/what's new.md
  48. 238 38
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx
  49. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx
  50. 21 1
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss
  51. 29 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar.tsx
  52. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx
  53. 143 1
      inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spriteManagerPropertyGridComponent.tsx
  54. 13 0
      inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spritePropertyGridComponent.tsx
  55. 1 1
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  56. 57 51
      loaders/src/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.ts
  57. 34 2
      materialsLibrary/src/shadowOnly/shadowOnlyMaterial.ts
  58. 1 1
      nodeEditor/src/diagram/graphFrame.ts
  59. 1 1
      package.json
  60. 4 1
      sandbox/index.js
  61. 1 1
      src/DeviceInput/InputDevices/deviceSourceManager.ts
  62. 2 1
      src/Engines/Processors/index.ts
  63. 408 0
      src/Engines/Processors/shaderCodeInliner.ts
  64. 29 10
      src/Engines/nativeEngine.ts
  65. 2 2
      src/Engines/thinEngine.ts
  66. 1 1
      src/Helpers/sceneHelpers.ts
  67. 2 2
      src/Inputs/scene.inputManager.ts
  68. 37 12
      src/Materials/PBR/pbrBaseMaterial.ts
  69. 15 5
      src/Materials/PBR/pbrSubSurfaceConfiguration.ts
  70. 8 2
      src/Materials/Textures/Filtering/hdrFiltering.ts
  71. 0 30
      src/Materials/Textures/baseTexture.ts
  72. 10 0
      src/Meshes/instancedMesh.ts
  73. 7 1
      src/Misc/tools.ts
  74. 0 2
      src/Probes/reflectionProbe.ts
  75. 1 0
      src/Shaders/ShadersInclude/bonesDeclaration.fx
  76. 22 14
      src/Shaders/ShadersInclude/hdrFilteringFunctions.fx
  77. 24 24
      src/Shaders/ShadersInclude/helperFunctions.fx
  78. 1 0
      src/Shaders/ShadersInclude/imageProcessingFunctions.fx
  79. 1 0
      src/Shaders/ShadersInclude/lightsFragmentFunctions.fx
  80. 1 0
      src/Shaders/ShadersInclude/pbrBlockClearcoat.fx
  81. 17 4
      src/Shaders/ShadersInclude/pbrBlockReflection.fx
  82. 1 0
      src/Shaders/ShadersInclude/pbrBlockSheen.fx
  83. 18 2
      src/Shaders/ShadersInclude/pbrBlockSubSurface.fx
  84. 1 0
      src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx
  85. 6 0
      src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx
  86. 1 5
      src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx
  87. 2 0
      src/Shaders/ShadersInclude/pbrUboDeclaration.fx
  88. 234 201
      src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx
  89. 3 1
      src/Shaders/hdrFiltering.fragment.fx
  90. 69 3
      src/Sprites/sprite.ts
  91. 104 8
      src/Sprites/spriteManager.ts
  92. BIN
      tests/validation/ReferenceImages/gltfExtensionExtMeshGpuInstancingTest.png
  93. 5 0
      tests/validation/config.json

File diff suppressed because it is too large
+ 180950 - 161222
Playground/js/babylonWebGpu.max.js


File diff suppressed because it is too large
+ 1 - 1
Playground/js/babylonWebGpu.max.js.map


+ 425 - 35
dist/preview release/babylon.d.ts

@@ -10258,13 +10258,25 @@ declare module BABYLON {
          * @param delay defines the start delay (in ms)
          * @param delay defines the start delay (in ms)
          * @param onAnimationEnd defines a callback to call when animation ends
          * @param onAnimationEnd defines a callback to call when animation ends
          */
          */
-        playAnimation(from: number, to: number, loop: boolean, delay: number, onAnimationEnd: () => void): void;
+        playAnimation(from: number, to: number, loop: boolean, delay: number, onAnimationEnd?: Nullable<() => void>): void;
         /** Stops current animation (if any) */
         /** Stops current animation (if any) */
         stopAnimation(): void;
         stopAnimation(): void;
         /** @hidden */
         /** @hidden */
         _animate(deltaTime: number): void;
         _animate(deltaTime: number): void;
         /** Release associated resources */
         /** Release associated resources */
         dispose(): void;
         dispose(): void;
+        /**
+         * Serializes the sprite to a JSON object
+         * @returns the JSON object
+         */
+        serialize(): any;
+        /**
+         * Parses a JSON object to create a new sprite
+         * @param parsedSprite The JSON object to parse
+         * @param manager defines the hosting manager
+         * @returns the new sprite
+         */
+        static Parse(parsedSprite: any, manager: SpriteManager): Sprite;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -10898,6 +10910,10 @@ declare module BABYLON {
     export class SpriteManager implements ISpriteManager {
     export class SpriteManager implements ISpriteManager {
         /** defines the manager's name */
         /** defines the manager's name */
         name: string;
         name: string;
+        /** Define the Url to load snippets */
+        static SnippetUrl: string;
+        /** Snippet ID if the manager was created from the snippet server */
+        snippetId: string;
         /** Gets the list of sprites */
         /** Gets the list of sprites */
         sprites: Sprite[];
         sprites: Sprite[];
         /** Gets or sets the rendering group id (0 by default) */
         /** Gets or sets the rendering group id (0 by default) */
@@ -10952,10 +10968,9 @@ declare module BABYLON {
          */
          */
         get scene(): Scene;
         get scene(): Scene;
         /**
         /**
-         * Gets or sets the capacity of the manager
+         * Gets the capacity of the manager
          */
          */
         get capacity(): number;
         get capacity(): number;
-        set capacity(value: number);
         /**
         /**
          * Gets or sets the spritesheet texture
          * Gets or sets the spritesheet texture
          */
          */
@@ -11022,6 +11037,28 @@ declare module BABYLON {
          * Release associated resources
          * Release associated resources
          */
          */
         dispose(): void;
         dispose(): void;
+        /**
+         * Serializes the sprite manager to a JSON object
+         * @param serializeTexture defines if the texture must be serialized as well
+         * @returns the JSON object
+         */
+        serialize(serializeTexture?: boolean): any;
+        /**
+         * Parses a JSON object to create a new sprite manager.
+         * @param parsedManager The JSON object to parse
+         * @param scene The scene to create the sprite managerin
+         * @param rootUrl The root url to use to load external dependencies like texture
+         * @returns the new sprite manager
+         */
+        static Parse(parsedManager: any, scene: Scene, rootUrl: string): SpriteManager;
+        /**
+         * Creates a sprite manager from a snippet saved by the sprite editor
+         * @param snippetId defines the snippet to load
+         * @param scene defines the hosting scene
+         * @param rootUrl defines the root URL to use to load textures and relative dependencies
+         * @returns a promise that will resolve to the new sprite manager
+         */
+        static CreateFromSnippetAsync(snippetId: string, scene: Scene, rootUrl?: string): Promise<SpriteManager>;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -16126,6 +16163,13 @@ declare module BABYLON {
          */
          */
         get sourceMesh(): Mesh;
         get sourceMesh(): Mesh;
         /**
         /**
+         * Creates a new InstancedMesh object from the mesh model.
+         * @see http://doc.babylonjs.com/how_to/how_to_use_instances
+         * @param name defines the name of the new instance
+         * @returns a new InstancedMesh
+         */
+        createInstance(name: string): InstancedMesh;
+        /**
          * Is this node ready to be used/rendered
          * Is this node ready to be used/rendered
          * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
          * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
          * @return {boolean} is it ready
          * @return {boolean} is it ready
@@ -32757,18 +32801,6 @@ declare module BABYLON {
          * @returns "BaseTexture"
          * @returns "BaseTexture"
          */
          */
         getClassName(): string;
         getClassName(): string;
-        private _realTimeFiltering;
-        /**
-         * Enables realtime filtering on the texture.
-         */
-        get realTimeFiltering(): boolean;
-        set realTimeFiltering(b: boolean);
-        private _realTimeFilteringQuality;
-        /**
-         * Quality switch for realtime filtering
-         */
-        get realTimeFilteringQuality(): number;
-        set realTimeFilteringQuality(n: number);
         /**
         /**
          * Define the list of animation attached to the texture.
          * Define the list of animation attached to the texture.
          */
          */
@@ -37079,9 +37111,10 @@ declare module BABYLON {
         static MakeArray(obj: any, allowsNullUndefined?: boolean): Nullable<Array<any>>;
         static MakeArray(obj: any, allowsNullUndefined?: boolean): Nullable<Array<any>>;
         /**
         /**
          * Gets the pointer prefix to use
          * Gets the pointer prefix to use
+         * @param engine defines the engine we are finding the prefix for
          * @returns "pointer" if touch is enabled. Else returns "mouse"
          * @returns "pointer" if touch is enabled. Else returns "mouse"
          */
          */
-        static GetPointerPrefix(): string;
+        static GetPointerPrefix(engine: Engine): string;
         /**
         /**
          * Sets the cors behavior on a dom element. This will add the required Tools.CorsBehavior to the element.
          * Sets the cors behavior on a dom element. This will add the required Tools.CorsBehavior to the element.
          * @param url define the url we are trying
          * @param url define the url we are trying
@@ -49476,24 +49509,209 @@ declare module BABYLON.Debug {
 }
 }
 declare module BABYLON {
 declare module BABYLON {
     /**
     /**
+     * Enum for Device Types
+     */
+    export enum DeviceType {
+        /** Generic */
+        Generic = 0,
+        /** Keyboard */
+        Keyboard = 1,
+        /** Mouse */
+        Mouse = 2,
+        /** Touch Pointers */
+        Touch = 3,
+        /** PS4 Dual Shock */
+        DualShock = 4,
+        /** Xbox */
+        Xbox = 5,
+        /** Switch Controller */
+        Switch = 6
+    }
+    /**
+     * Enum for All Pointers (Touch/Mouse)
+     */
+    export enum PointerInput {
+        /** Horizontal Axis */
+        Horizontal = 0,
+        /** Vertical Axis */
+        Vertical = 1,
+        /** Left Click or Touch */
+        LeftClick = 2,
+        /** Middle Click */
+        MiddleClick = 3,
+        /** Right Click */
+        RightClick = 4,
+        /** Browser Back */
+        BrowserBack = 5,
+        /** Browser Forward */
+        BrowserForward = 6
+    }
+    /**
+     * Enum for Dual Shock Gamepad
+     */
+    export enum DualShockInput {
+        /** Cross */
+        Cross = 0,
+        /** Circle */
+        Circle = 1,
+        /** Square */
+        Square = 2,
+        /** Triangle */
+        Triangle = 3,
+        /** L1 */
+        L1 = 4,
+        /** R1 */
+        R1 = 5,
+        /** L2 */
+        L2 = 6,
+        /** R2 */
+        R2 = 7,
+        /** Share */
+        Share = 8,
+        /** Options */
+        Options = 9,
+        /** L3 */
+        L3 = 10,
+        /** R3 */
+        R3 = 11,
+        /** DPadUp */
+        DPadUp = 12,
+        /** DPadDown */
+        DPadDown = 13,
+        /** DPadLeft */
+        DPadLeft = 14,
+        /** DRight */
+        DPadRight = 15,
+        /** Home */
+        Home = 16,
+        /** TouchPad */
+        TouchPad = 17,
+        /** LStickXAxis */
+        LStickXAxis = 18,
+        /** LStickYAxis */
+        LStickYAxis = 19,
+        /** RStickXAxis */
+        RStickXAxis = 20,
+        /** RStickYAxis */
+        RStickYAxis = 21
+    }
+    /**
+     * Enum for Xbox Gamepad
+     */
+    export enum XboxInput {
+        /** A */
+        A = 0,
+        /** B */
+        B = 1,
+        /** X */
+        X = 2,
+        /** Y */
+        Y = 3,
+        /** LB */
+        LB = 4,
+        /** RB */
+        RB = 5,
+        /** LT */
+        LT = 6,
+        /** RT */
+        RT = 7,
+        /** Back */
+        Back = 8,
+        /** Start */
+        Start = 9,
+        /** LS */
+        LS = 10,
+        /** RS */
+        RS = 11,
+        /** DPadUp */
+        DPadUp = 12,
+        /** DPadDown */
+        DPadDown = 13,
+        /** DPadLeft */
+        DPadLeft = 14,
+        /** DRight */
+        DPadRight = 15,
+        /** Home */
+        Home = 16,
+        /** LStickXAxis */
+        LStickXAxis = 17,
+        /** LStickYAxis */
+        LStickYAxis = 18,
+        /** RStickXAxis */
+        RStickXAxis = 19,
+        /** RStickYAxis */
+        RStickYAxis = 20
+    }
+    /**
+     * Enum for Switch (Pro/JoyCon L+R) Gamepad
+     */
+    export enum SwitchInput {
+        /** B */
+        B = 0,
+        /** A */
+        A = 1,
+        /** Y */
+        Y = 2,
+        /** X */
+        X = 3,
+        /** L */
+        L = 4,
+        /** R */
+        R = 5,
+        /** ZL */
+        ZL = 6,
+        /** ZR */
+        ZR = 7,
+        /** Minus */
+        Minus = 8,
+        /** Plus */
+        Plus = 9,
+        /** LS */
+        LS = 10,
+        /** RS */
+        RS = 11,
+        /** DPadUp */
+        DPadUp = 12,
+        /** DPadDown */
+        DPadDown = 13,
+        /** DPadLeft */
+        DPadLeft = 14,
+        /** DRight */
+        DPadRight = 15,
+        /** Home */
+        Home = 16,
+        /** Capture */
+        Capture = 17,
+        /** LStickXAxis */
+        LStickXAxis = 18,
+        /** LStickYAxis */
+        LStickYAxis = 19,
+        /** RStickXAxis */
+        RStickXAxis = 20,
+        /** RStickYAxis */
+        RStickYAxis = 21
+    }
+}
+declare module BABYLON {
+    /**
      * This class will take all inputs from Keyboard, Pointer, and
      * This class will take all inputs from Keyboard, Pointer, and
      * any Gamepads and provide a polling system that all devices
      * any Gamepads and provide a polling system that all devices
      * will use.  This class assumes that there will only be one
      * will use.  This class assumes that there will only be one
      * pointer device and one keyboard.
      * pointer device and one keyboard.
      */
      */
     export class DeviceInputSystem implements IDisposable {
     export class DeviceInputSystem implements IDisposable {
-        /** POINTER_DEVICE */
-        static readonly POINTER_DEVICE: string;
-        /** KEYBOARD_DEVICE */
-        static readonly KEYBOARD_DEVICE: string;
         /**
         /**
-         * Observable to be triggered when a device is connected
+         * Callback to be triggered when a device is connected
+         */
+        onDeviceConnected: (deviceType: DeviceType, deviceSlot: number) => void;
+        /**
+         * Callback to be triggered when a device is disconnected
          */
          */
-        onDeviceConnectedObservable: Observable<string>;
+        onDeviceDisconnected: (deviceType: DeviceType, deviceSlot: number) => void;
         /**
         /**
-         * Observable to be triggered when a device is disconnected
+         * Callback to be triggered when event driven input is updated
          */
          */
-        onDeviceDisconnectedObservable: Observable<string>;
+        onInputChanged: (deviceType: DeviceType, deviceSlot: number, inputIndex: number, previousState: Nullable<number>, currentState: Nullable<number>) => void;
         private _inputs;
         private _inputs;
         private _gamepads;
         private _gamepads;
         private _keyboardActive;
         private _keyboardActive;
@@ -49519,20 +49737,29 @@ declare module BABYLON {
          * @param inputIndex Index of device input
          * @param inputIndex Index of device input
          * @returns Current value of input
          * @returns Current value of input
          */
          */
-        pollInput(deviceName: string, inputIndex: number): Nullable<number>;
         /**
         /**
-         * Dispose of all the eventlisteners and clears the observables
+         * Checks for current device input value, given an id and input index
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         * @param inputIndex Id of input to be checked
+         * @returns Current value of input
+         */
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        /**
+         * Dispose of all the eventlisteners
          */
          */
         dispose(): void;
         dispose(): void;
         /**
         /**
-         * Add device and inputs to device map
-         * @param deviceName Assigned name of device (may be SN)
+         * Add device and inputs to device array
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
          * @param numberOfInputs Number of input entries to create for given device
          * @param numberOfInputs Number of input entries to create for given device
          */
          */
         private _registerDevice;
         private _registerDevice;
         /**
         /**
          * Given a specific device name, remove that device from the device map
          * Given a specific device name, remove that device from the device map
-         * @param deviceName Name of device to be removed
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
          */
          */
         private _unregisterDevice;
         private _unregisterDevice;
         /**
         /**
@@ -49549,8 +49776,135 @@ declare module BABYLON {
         private _handleGamepadActions;
         private _handleGamepadActions;
         /**
         /**
          * Update all non-event based devices with each frame
          * Update all non-event based devices with each frame
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         * @param inputIndex Id of input to be checked
          */
          */
         private _updateDevice;
         private _updateDevice;
+        /**
+         * Gets DeviceType from the device name
+         * @param deviceName Name of Device from DeviceInputSystem
+         * @returns DeviceType enum value
+         */
+        private _getGamepadDeviceType;
+    }
+}
+declare module BABYLON {
+    /**
+     * Type to handle enforcement of inputs
+     */
+    export type DeviceInput<T extends DeviceType> = T extends DeviceType.Keyboard | DeviceType.Generic ? number : T extends DeviceType.Mouse | DeviceType.Touch ? PointerInput : T extends DeviceType.DualShock ? DualShockInput : T extends DeviceType.Xbox ? XboxInput : T extends DeviceType.Switch ? SwitchInput : never;
+}
+declare module BABYLON {
+    /**
+     * Class that handles all input for a specific device
+     */
+    export class DeviceSource<T extends DeviceType> {
+        /** Type of device */
+        readonly deviceType: DeviceType;
+        /** "Slot" or index that device is referenced in */
+        readonly deviceSlot: number;
+        /**
+         * Observable to handle device input changes per device
+         */
+        readonly onInputChangedObservable: Observable<{
+            inputIndex: DeviceInput<T>;
+            previousState: Nullable<number>;
+            currentState: Nullable<number>;
+        }>;
+        private readonly _deviceInputSystem;
+        /**
+         * Default Constructor
+         * @param deviceInputSystem Reference to DeviceInputSystem
+         * @param deviceType Type of device
+         * @param deviceSlot "Slot" or index that device is referenced in
+         */
+        constructor(deviceInputSystem: DeviceInputSystem, 
+        /** Type of device */
+        deviceType: DeviceType, 
+        /** "Slot" or index that device is referenced in */
+        deviceSlot?: number);
+        /**
+         * Get input for specific input
+         * @param inputIndex index of specific input on device
+         * @returns Input value from DeviceInputSystem
+         */
+        getInput(inputIndex: DeviceInput<T>): Nullable<number>;
+    }
+    /**
+     * Class to keep track of devices
+     */
+    export class DeviceSourceManager implements IDisposable {
+        /**
+         * Observable to be triggered when before a device is connected
+         */
+        readonly onBeforeDeviceConnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        /**
+         * Observable to be triggered when before a device is disconnected
+         */
+        readonly onBeforeDeviceDisconnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        /**
+         * Observable to be triggered when after a device is connected
+         */
+        readonly onAfterDeviceConnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        /**
+         * Observable to be triggered when after a device is disconnected
+         */
+        readonly onAfterDeviceDisconnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        private readonly _devices;
+        private readonly _firstDevice;
+        private readonly _deviceInputSystem;
+        /**
+         * Default Constructor
+         * @param engine engine to pull input element from
+         */
+        constructor(engine: Engine);
+        /**
+         * Gets a DeviceSource, given a type and slot
+         * @param deviceType Enum specifying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         * @returns DeviceSource object
+         */
+        getDeviceSource<T extends DeviceType>(deviceType: T, deviceSlot?: number): Nullable<DeviceSource<T>>;
+        /**
+         * Gets an array of DeviceSource objects for a given device type
+         * @param deviceType Enum specifying device type
+         * @returns Array of DeviceSource objects
+         */
+        getDeviceSources<T extends DeviceType>(deviceType: T): ReadonlyArray<DeviceSource<T>>;
+        /**
+         * Dispose of DeviceInputSystem and other parts
+         */
+        dispose(): void;
+        /**
+         * Function to add device name to device list
+         * @param deviceType Enum specifying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         */
+        private _addDevice;
+        /**
+         * Function to remove device name to device list
+         * @param deviceType Enum specifying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         */
+        private _removeDevice;
+        /**
+         * Updates array storing first connected device of each type
+         * @param type Type of Device
+         */
+        private _updateFirstDevices;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -50686,8 +51040,8 @@ declare module BABYLON {
          */
          */
         getHostDocument(): Nullable<Document>;
         getHostDocument(): Nullable<Document>;
         clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil?: boolean): void;
         clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil?: boolean): void;
-        createIndexBuffer(indices: IndicesArray): NativeDataBuffer;
-        createVertexBuffer(data: DataArray): NativeDataBuffer;
+        createIndexBuffer(indices: IndicesArray, updateable?: boolean): NativeDataBuffer;
+        createVertexBuffer(data: DataArray, updateable?: boolean): NativeDataBuffer;
         recordVertexArrayObject(vertexBuffers: {
         recordVertexArrayObject(vertexBuffers: {
             [key: string]: VertexBuffer;
             [key: string]: VertexBuffer;
         }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject;
         }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject;
@@ -53711,10 +54065,11 @@ declare module BABYLON {
          * @param uniformBuffer defines the Uniform buffer to fill in.
          * @param uniformBuffer defines the Uniform buffer to fill in.
          * @param scene defines the scene the material belongs to.
          * @param scene defines the scene the material belongs to.
          * @param engine defines the engine the material belongs to.
          * @param engine defines the engine the material belongs to.
-         * @param isFrozen defines wether the material is frozen or not.
-         * @param lodBasedMicrosurface defines wether the material relies on lod based microsurface or not.
+         * @param isFrozen defines whether the material is frozen or not.
+         * @param lodBasedMicrosurface defines whether the material relies on lod based microsurface or not.
+         * @param realTimeFiltering defines whether the textures should be filtered on the fly.
          */
          */
-        bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean): void;
+        bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean, realTimeFiltering: boolean): void;
         /**
         /**
          * Unbinds the material from the mesh.
          * Unbinds the material from the mesh.
          * @param activeEffect defines the effect that should be unbound from.
          * @param activeEffect defines the effect that should be unbound from.
@@ -54546,6 +54901,18 @@ declare module BABYLON {
          * Force the shader to compute irradiance in the fragment shader in order to take bump in account.
          * Force the shader to compute irradiance in the fragment shader in order to take bump in account.
          */
          */
         protected _forceIrradianceInFragment: boolean;
         protected _forceIrradianceInFragment: boolean;
+        private _realTimeFiltering;
+        /**
+         * Enables realtime filtering on the texture.
+         */
+        get realTimeFiltering(): boolean;
+        set realTimeFiltering(b: boolean);
+        private _realTimeFilteringQuality;
+        /**
+         * Quality switch for realtime filtering
+         */
+        get realTimeFilteringQuality(): number;
+        set realTimeFilteringQuality(n: number);
         /**
         /**
          * Force normal to face away from face.
          * Force normal to face away from face.
          */
          */
@@ -58107,7 +58474,7 @@ declare module BABYLON {
           * @param onFinished Callback when filtering is done
           * @param onFinished Callback when filtering is done
           * @return Promise called when prefiltering is done
           * @return Promise called when prefiltering is done
           */
           */
-        prefilter(texture: BaseTexture, onFinished?: Nullable<() => void>): Promise<unknown>;
+        prefilter(texture: BaseTexture, onFinished?: Nullable<() => void>): Promise<unknown> | undefined;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -72456,6 +72823,29 @@ declare module BABYLON {
 }
 }
 declare module BABYLON {
 declare module BABYLON {
     /** @hidden */
     /** @hidden */
+    export class ShaderCodeInliner {
+        static readonly InlineToken: string;
+        static readonly RegexpFindFunctionNameAndType: RegExp;
+        private _sourceCode;
+        private _functionDescr;
+        private _numMaxIterations;
+        debug: boolean;
+        get code(): string;
+        constructor(sourceCode: string, numMaxIterations?: number);
+        processCode(): void;
+        private _collectFunctions;
+        private _processInlining;
+        private _extractBetweenMarkers;
+        private _skipWhitespaces;
+        private _removeComments;
+        private _replaceFunctionCallsByCode;
+        private _findBackward;
+        private _escapeRegExp;
+        private _replaceNames;
+    }
+}
+declare module BABYLON {
+    /** @hidden */
     export var blurPixelShader: {
     export var blurPixelShader: {
         name: string;
         name: string;
         shader: string;
         shader: string;

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


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


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


File diff suppressed because it is too large
+ 865 - 72
dist/preview release/babylon.module.d.ts


+ 427 - 35
dist/preview release/documentation.d.ts

@@ -10258,13 +10258,25 @@ declare module BABYLON {
          * @param delay defines the start delay (in ms)
          * @param delay defines the start delay (in ms)
          * @param onAnimationEnd defines a callback to call when animation ends
          * @param onAnimationEnd defines a callback to call when animation ends
          */
          */
-        playAnimation(from: number, to: number, loop: boolean, delay: number, onAnimationEnd: () => void): void;
+        playAnimation(from: number, to: number, loop: boolean, delay: number, onAnimationEnd?: Nullable<() => void>): void;
         /** Stops current animation (if any) */
         /** Stops current animation (if any) */
         stopAnimation(): void;
         stopAnimation(): void;
         /** @hidden */
         /** @hidden */
         _animate(deltaTime: number): void;
         _animate(deltaTime: number): void;
         /** Release associated resources */
         /** Release associated resources */
         dispose(): void;
         dispose(): void;
+        /**
+         * Serializes the sprite to a JSON object
+         * @returns the JSON object
+         */
+        serialize(): any;
+        /**
+         * Parses a JSON object to create a new sprite
+         * @param parsedSprite The JSON object to parse
+         * @param manager defines the hosting manager
+         * @returns the new sprite
+         */
+        static Parse(parsedSprite: any, manager: SpriteManager): Sprite;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -10898,6 +10910,10 @@ declare module BABYLON {
     export class SpriteManager implements ISpriteManager {
     export class SpriteManager implements ISpriteManager {
         /** defines the manager's name */
         /** defines the manager's name */
         name: string;
         name: string;
+        /** Define the Url to load snippets */
+        static SnippetUrl: string;
+        /** Snippet ID if the manager was created from the snippet server */
+        snippetId: string;
         /** Gets the list of sprites */
         /** Gets the list of sprites */
         sprites: Sprite[];
         sprites: Sprite[];
         /** Gets or sets the rendering group id (0 by default) */
         /** Gets or sets the rendering group id (0 by default) */
@@ -10952,10 +10968,9 @@ declare module BABYLON {
          */
          */
         get scene(): Scene;
         get scene(): Scene;
         /**
         /**
-         * Gets or sets the capacity of the manager
+         * Gets the capacity of the manager
          */
          */
         get capacity(): number;
         get capacity(): number;
-        set capacity(value: number);
         /**
         /**
          * Gets or sets the spritesheet texture
          * Gets or sets the spritesheet texture
          */
          */
@@ -11022,6 +11037,28 @@ declare module BABYLON {
          * Release associated resources
          * Release associated resources
          */
          */
         dispose(): void;
         dispose(): void;
+        /**
+         * Serializes the sprite manager to a JSON object
+         * @param serializeTexture defines if the texture must be serialized as well
+         * @returns the JSON object
+         */
+        serialize(serializeTexture?: boolean): any;
+        /**
+         * Parses a JSON object to create a new sprite manager.
+         * @param parsedManager The JSON object to parse
+         * @param scene The scene to create the sprite managerin
+         * @param rootUrl The root url to use to load external dependencies like texture
+         * @returns the new sprite manager
+         */
+        static Parse(parsedManager: any, scene: Scene, rootUrl: string): SpriteManager;
+        /**
+         * Creates a sprite manager from a snippet saved by the sprite editor
+         * @param snippetId defines the snippet to load
+         * @param scene defines the hosting scene
+         * @param rootUrl defines the root URL to use to load textures and relative dependencies
+         * @returns a promise that will resolve to the new sprite manager
+         */
+        static CreateFromSnippetAsync(snippetId: string, scene: Scene, rootUrl?: string): Promise<SpriteManager>;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -16126,6 +16163,13 @@ declare module BABYLON {
          */
          */
         get sourceMesh(): Mesh;
         get sourceMesh(): Mesh;
         /**
         /**
+         * Creates a new InstancedMesh object from the mesh model.
+         * @see http://doc.babylonjs.com/how_to/how_to_use_instances
+         * @param name defines the name of the new instance
+         * @returns a new InstancedMesh
+         */
+        createInstance(name: string): InstancedMesh;
+        /**
          * Is this node ready to be used/rendered
          * Is this node ready to be used/rendered
          * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
          * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
          * @return {boolean} is it ready
          * @return {boolean} is it ready
@@ -32757,18 +32801,6 @@ declare module BABYLON {
          * @returns "BaseTexture"
          * @returns "BaseTexture"
          */
          */
         getClassName(): string;
         getClassName(): string;
-        private _realTimeFiltering;
-        /**
-         * Enables realtime filtering on the texture.
-         */
-        get realTimeFiltering(): boolean;
-        set realTimeFiltering(b: boolean);
-        private _realTimeFilteringQuality;
-        /**
-         * Quality switch for realtime filtering
-         */
-        get realTimeFilteringQuality(): number;
-        set realTimeFilteringQuality(n: number);
         /**
         /**
          * Define the list of animation attached to the texture.
          * Define the list of animation attached to the texture.
          */
          */
@@ -37079,9 +37111,10 @@ declare module BABYLON {
         static MakeArray(obj: any, allowsNullUndefined?: boolean): Nullable<Array<any>>;
         static MakeArray(obj: any, allowsNullUndefined?: boolean): Nullable<Array<any>>;
         /**
         /**
          * Gets the pointer prefix to use
          * Gets the pointer prefix to use
+         * @param engine defines the engine we are finding the prefix for
          * @returns "pointer" if touch is enabled. Else returns "mouse"
          * @returns "pointer" if touch is enabled. Else returns "mouse"
          */
          */
-        static GetPointerPrefix(): string;
+        static GetPointerPrefix(engine: Engine): string;
         /**
         /**
          * Sets the cors behavior on a dom element. This will add the required Tools.CorsBehavior to the element.
          * Sets the cors behavior on a dom element. This will add the required Tools.CorsBehavior to the element.
          * @param url define the url we are trying
          * @param url define the url we are trying
@@ -49476,24 +49509,209 @@ declare module BABYLON.Debug {
 }
 }
 declare module BABYLON {
 declare module BABYLON {
     /**
     /**
+     * Enum for Device Types
+     */
+    export enum DeviceType {
+        /** Generic */
+        Generic = 0,
+        /** Keyboard */
+        Keyboard = 1,
+        /** Mouse */
+        Mouse = 2,
+        /** Touch Pointers */
+        Touch = 3,
+        /** PS4 Dual Shock */
+        DualShock = 4,
+        /** Xbox */
+        Xbox = 5,
+        /** Switch Controller */
+        Switch = 6
+    }
+    /**
+     * Enum for All Pointers (Touch/Mouse)
+     */
+    export enum PointerInput {
+        /** Horizontal Axis */
+        Horizontal = 0,
+        /** Vertical Axis */
+        Vertical = 1,
+        /** Left Click or Touch */
+        LeftClick = 2,
+        /** Middle Click */
+        MiddleClick = 3,
+        /** Right Click */
+        RightClick = 4,
+        /** Browser Back */
+        BrowserBack = 5,
+        /** Browser Forward */
+        BrowserForward = 6
+    }
+    /**
+     * Enum for Dual Shock Gamepad
+     */
+    export enum DualShockInput {
+        /** Cross */
+        Cross = 0,
+        /** Circle */
+        Circle = 1,
+        /** Square */
+        Square = 2,
+        /** Triangle */
+        Triangle = 3,
+        /** L1 */
+        L1 = 4,
+        /** R1 */
+        R1 = 5,
+        /** L2 */
+        L2 = 6,
+        /** R2 */
+        R2 = 7,
+        /** Share */
+        Share = 8,
+        /** Options */
+        Options = 9,
+        /** L3 */
+        L3 = 10,
+        /** R3 */
+        R3 = 11,
+        /** DPadUp */
+        DPadUp = 12,
+        /** DPadDown */
+        DPadDown = 13,
+        /** DPadLeft */
+        DPadLeft = 14,
+        /** DRight */
+        DPadRight = 15,
+        /** Home */
+        Home = 16,
+        /** TouchPad */
+        TouchPad = 17,
+        /** LStickXAxis */
+        LStickXAxis = 18,
+        /** LStickYAxis */
+        LStickYAxis = 19,
+        /** RStickXAxis */
+        RStickXAxis = 20,
+        /** RStickYAxis */
+        RStickYAxis = 21
+    }
+    /**
+     * Enum for Xbox Gamepad
+     */
+    export enum XboxInput {
+        /** A */
+        A = 0,
+        /** B */
+        B = 1,
+        /** X */
+        X = 2,
+        /** Y */
+        Y = 3,
+        /** LB */
+        LB = 4,
+        /** RB */
+        RB = 5,
+        /** LT */
+        LT = 6,
+        /** RT */
+        RT = 7,
+        /** Back */
+        Back = 8,
+        /** Start */
+        Start = 9,
+        /** LS */
+        LS = 10,
+        /** RS */
+        RS = 11,
+        /** DPadUp */
+        DPadUp = 12,
+        /** DPadDown */
+        DPadDown = 13,
+        /** DPadLeft */
+        DPadLeft = 14,
+        /** DRight */
+        DPadRight = 15,
+        /** Home */
+        Home = 16,
+        /** LStickXAxis */
+        LStickXAxis = 17,
+        /** LStickYAxis */
+        LStickYAxis = 18,
+        /** RStickXAxis */
+        RStickXAxis = 19,
+        /** RStickYAxis */
+        RStickYAxis = 20
+    }
+    /**
+     * Enum for Switch (Pro/JoyCon L+R) Gamepad
+     */
+    export enum SwitchInput {
+        /** B */
+        B = 0,
+        /** A */
+        A = 1,
+        /** Y */
+        Y = 2,
+        /** X */
+        X = 3,
+        /** L */
+        L = 4,
+        /** R */
+        R = 5,
+        /** ZL */
+        ZL = 6,
+        /** ZR */
+        ZR = 7,
+        /** Minus */
+        Minus = 8,
+        /** Plus */
+        Plus = 9,
+        /** LS */
+        LS = 10,
+        /** RS */
+        RS = 11,
+        /** DPadUp */
+        DPadUp = 12,
+        /** DPadDown */
+        DPadDown = 13,
+        /** DPadLeft */
+        DPadLeft = 14,
+        /** DRight */
+        DPadRight = 15,
+        /** Home */
+        Home = 16,
+        /** Capture */
+        Capture = 17,
+        /** LStickXAxis */
+        LStickXAxis = 18,
+        /** LStickYAxis */
+        LStickYAxis = 19,
+        /** RStickXAxis */
+        RStickXAxis = 20,
+        /** RStickYAxis */
+        RStickYAxis = 21
+    }
+}
+declare module BABYLON {
+    /**
      * This class will take all inputs from Keyboard, Pointer, and
      * This class will take all inputs from Keyboard, Pointer, and
      * any Gamepads and provide a polling system that all devices
      * any Gamepads and provide a polling system that all devices
      * will use.  This class assumes that there will only be one
      * will use.  This class assumes that there will only be one
      * pointer device and one keyboard.
      * pointer device and one keyboard.
      */
      */
     export class DeviceInputSystem implements IDisposable {
     export class DeviceInputSystem implements IDisposable {
-        /** POINTER_DEVICE */
-        static readonly POINTER_DEVICE: string;
-        /** KEYBOARD_DEVICE */
-        static readonly KEYBOARD_DEVICE: string;
         /**
         /**
-         * Observable to be triggered when a device is connected
+         * Callback to be triggered when a device is connected
+         */
+        onDeviceConnected: (deviceType: DeviceType, deviceSlot: number) => void;
+        /**
+         * Callback to be triggered when a device is disconnected
          */
          */
-        onDeviceConnectedObservable: Observable<string>;
+        onDeviceDisconnected: (deviceType: DeviceType, deviceSlot: number) => void;
         /**
         /**
-         * Observable to be triggered when a device is disconnected
+         * Callback to be triggered when event driven input is updated
          */
          */
-        onDeviceDisconnectedObservable: Observable<string>;
+        onInputChanged: (deviceType: DeviceType, deviceSlot: number, inputIndex: number, previousState: Nullable<number>, currentState: Nullable<number>) => void;
         private _inputs;
         private _inputs;
         private _gamepads;
         private _gamepads;
         private _keyboardActive;
         private _keyboardActive;
@@ -49519,20 +49737,29 @@ declare module BABYLON {
          * @param inputIndex Index of device input
          * @param inputIndex Index of device input
          * @returns Current value of input
          * @returns Current value of input
          */
          */
-        pollInput(deviceName: string, inputIndex: number): Nullable<number>;
         /**
         /**
-         * Dispose of all the eventlisteners and clears the observables
+         * Checks for current device input value, given an id and input index
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         * @param inputIndex Id of input to be checked
+         * @returns Current value of input
+         */
+        pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number>;
+        /**
+         * Dispose of all the eventlisteners
          */
          */
         dispose(): void;
         dispose(): void;
         /**
         /**
-         * Add device and inputs to device map
-         * @param deviceName Assigned name of device (may be SN)
+         * Add device and inputs to device array
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
          * @param numberOfInputs Number of input entries to create for given device
          * @param numberOfInputs Number of input entries to create for given device
          */
          */
         private _registerDevice;
         private _registerDevice;
         /**
         /**
          * Given a specific device name, remove that device from the device map
          * Given a specific device name, remove that device from the device map
-         * @param deviceName Name of device to be removed
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
          */
          */
         private _unregisterDevice;
         private _unregisterDevice;
         /**
         /**
@@ -49549,8 +49776,135 @@ declare module BABYLON {
         private _handleGamepadActions;
         private _handleGamepadActions;
         /**
         /**
          * Update all non-event based devices with each frame
          * Update all non-event based devices with each frame
+         * @param deviceType Enum specifiying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         * @param inputIndex Id of input to be checked
          */
          */
         private _updateDevice;
         private _updateDevice;
+        /**
+         * Gets DeviceType from the device name
+         * @param deviceName Name of Device from DeviceInputSystem
+         * @returns DeviceType enum value
+         */
+        private _getGamepadDeviceType;
+    }
+}
+declare module BABYLON {
+    /**
+     * Type to handle enforcement of inputs
+     */
+    export type DeviceInput<T extends DeviceType> = T extends DeviceType.Keyboard | DeviceType.Generic ? number : T extends DeviceType.Mouse | DeviceType.Touch ? PointerInput : T extends DeviceType.DualShock ? DualShockInput : T extends DeviceType.Xbox ? XboxInput : T extends DeviceType.Switch ? SwitchInput : never;
+}
+declare module BABYLON {
+    /**
+     * Class that handles all input for a specific device
+     */
+    export class DeviceSource<T extends DeviceType> {
+        /** Type of device */
+        readonly deviceType: DeviceType;
+        /** "Slot" or index that device is referenced in */
+        readonly deviceSlot: number;
+        /**
+         * Observable to handle device input changes per device
+         */
+        readonly onInputChangedObservable: Observable<{
+            inputIndex: DeviceInput<T>;
+            previousState: Nullable<number>;
+            currentState: Nullable<number>;
+        }>;
+        private readonly _deviceInputSystem;
+        /**
+         * Default Constructor
+         * @param deviceInputSystem Reference to DeviceInputSystem
+         * @param deviceType Type of device
+         * @param deviceSlot "Slot" or index that device is referenced in
+         */
+        constructor(deviceInputSystem: DeviceInputSystem, 
+        /** Type of device */
+        deviceType: DeviceType, 
+        /** "Slot" or index that device is referenced in */
+        deviceSlot?: number);
+        /**
+         * Get input for specific input
+         * @param inputIndex index of specific input on device
+         * @returns Input value from DeviceInputSystem
+         */
+        getInput(inputIndex: DeviceInput<T>): Nullable<number>;
+    }
+    /**
+     * Class to keep track of devices
+     */
+    export class DeviceSourceManager implements IDisposable {
+        /**
+         * Observable to be triggered when before a device is connected
+         */
+        readonly onBeforeDeviceConnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        /**
+         * Observable to be triggered when before a device is disconnected
+         */
+        readonly onBeforeDeviceDisconnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        /**
+         * Observable to be triggered when after a device is connected
+         */
+        readonly onAfterDeviceConnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        /**
+         * Observable to be triggered when after a device is disconnected
+         */
+        readonly onAfterDeviceDisconnectedObservable: Observable<{
+            deviceType: DeviceType;
+            deviceSlot: number;
+        }>;
+        private readonly _devices;
+        private readonly _firstDevice;
+        private readonly _deviceInputSystem;
+        /**
+         * Default Constructor
+         * @param engine engine to pull input element from
+         */
+        constructor(engine: Engine);
+        /**
+         * Gets a DeviceSource, given a type and slot
+         * @param deviceType Enum specifying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         * @returns DeviceSource object
+         */
+        getDeviceSource<T extends DeviceType>(deviceType: T, deviceSlot?: number): Nullable<DeviceSource<T>>;
+        /**
+         * Gets an array of DeviceSource objects for a given device type
+         * @param deviceType Enum specifying device type
+         * @returns Array of DeviceSource objects
+         */
+        getDeviceSources<T extends DeviceType>(deviceType: T): ReadonlyArray<DeviceSource<T>>;
+        /**
+         * Dispose of DeviceInputSystem and other parts
+         */
+        dispose(): void;
+        /**
+         * Function to add device name to device list
+         * @param deviceType Enum specifying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         */
+        private _addDevice;
+        /**
+         * Function to remove device name to device list
+         * @param deviceType Enum specifying device type
+         * @param deviceSlot "Slot" or index that device is referenced in
+         */
+        private _removeDevice;
+        /**
+         * Updates array storing first connected device of each type
+         * @param type Type of Device
+         */
+        private _updateFirstDevices;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -50686,8 +51040,8 @@ declare module BABYLON {
          */
          */
         getHostDocument(): Nullable<Document>;
         getHostDocument(): Nullable<Document>;
         clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil?: boolean): void;
         clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil?: boolean): void;
-        createIndexBuffer(indices: IndicesArray): NativeDataBuffer;
-        createVertexBuffer(data: DataArray): NativeDataBuffer;
+        createIndexBuffer(indices: IndicesArray, updateable?: boolean): NativeDataBuffer;
+        createVertexBuffer(data: DataArray, updateable?: boolean): NativeDataBuffer;
         recordVertexArrayObject(vertexBuffers: {
         recordVertexArrayObject(vertexBuffers: {
             [key: string]: VertexBuffer;
             [key: string]: VertexBuffer;
         }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject;
         }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject;
@@ -53711,10 +54065,11 @@ declare module BABYLON {
          * @param uniformBuffer defines the Uniform buffer to fill in.
          * @param uniformBuffer defines the Uniform buffer to fill in.
          * @param scene defines the scene the material belongs to.
          * @param scene defines the scene the material belongs to.
          * @param engine defines the engine the material belongs to.
          * @param engine defines the engine the material belongs to.
-         * @param isFrozen defines wether the material is frozen or not.
-         * @param lodBasedMicrosurface defines wether the material relies on lod based microsurface or not.
+         * @param isFrozen defines whether the material is frozen or not.
+         * @param lodBasedMicrosurface defines whether the material relies on lod based microsurface or not.
+         * @param realTimeFiltering defines whether the textures should be filtered on the fly.
          */
          */
-        bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean): void;
+        bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean, realTimeFiltering: boolean): void;
         /**
         /**
          * Unbinds the material from the mesh.
          * Unbinds the material from the mesh.
          * @param activeEffect defines the effect that should be unbound from.
          * @param activeEffect defines the effect that should be unbound from.
@@ -54546,6 +54901,18 @@ declare module BABYLON {
          * Force the shader to compute irradiance in the fragment shader in order to take bump in account.
          * Force the shader to compute irradiance in the fragment shader in order to take bump in account.
          */
          */
         protected _forceIrradianceInFragment: boolean;
         protected _forceIrradianceInFragment: boolean;
+        private _realTimeFiltering;
+        /**
+         * Enables realtime filtering on the texture.
+         */
+        get realTimeFiltering(): boolean;
+        set realTimeFiltering(b: boolean);
+        private _realTimeFilteringQuality;
+        /**
+         * Quality switch for realtime filtering
+         */
+        get realTimeFilteringQuality(): number;
+        set realTimeFilteringQuality(n: number);
         /**
         /**
          * Force normal to face away from face.
          * Force normal to face away from face.
          */
          */
@@ -58107,7 +58474,7 @@ declare module BABYLON {
           * @param onFinished Callback when filtering is done
           * @param onFinished Callback when filtering is done
           * @return Promise called when prefiltering is done
           * @return Promise called when prefiltering is done
           */
           */
-        prefilter(texture: BaseTexture, onFinished?: Nullable<() => void>): Promise<unknown>;
+        prefilter(texture: BaseTexture, onFinished?: Nullable<() => void>): Promise<unknown> | undefined;
     }
     }
 }
 }
 declare module BABYLON {
 declare module BABYLON {
@@ -72456,6 +72823,29 @@ declare module BABYLON {
 }
 }
 declare module BABYLON {
 declare module BABYLON {
     /** @hidden */
     /** @hidden */
+    export class ShaderCodeInliner {
+        static readonly InlineToken: string;
+        static readonly RegexpFindFunctionNameAndType: RegExp;
+        private _sourceCode;
+        private _functionDescr;
+        private _numMaxIterations;
+        debug: boolean;
+        get code(): string;
+        constructor(sourceCode: string, numMaxIterations?: number);
+        processCode(): void;
+        private _collectFunctions;
+        private _processInlining;
+        private _extractBetweenMarkers;
+        private _skipWhitespaces;
+        private _removeComments;
+        private _replaceFunctionCallsByCode;
+        private _findBackward;
+        private _escapeRegExp;
+        private _replaceNames;
+    }
+}
+declare module BABYLON {
+    /** @hidden */
     export var blurPixelShader: {
     export var blurPixelShader: {
         name: string;
         name: string;
         shader: string;
         shader: string;
@@ -82364,6 +82754,7 @@ declare module BABYLON {
 declare module BABYLON {
 declare module BABYLON {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
         private _activeLight;
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: BABYLON.Scene);
         constructor(name: string, scene: BABYLON.Scene);
         shadowColor: BABYLON.Color3;
         shadowColor: BABYLON.Color3;
         needAlphaBlending(): boolean;
         needAlphaBlending(): boolean;
@@ -82371,6 +82762,7 @@ declare module BABYLON {
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         get activeLight(): BABYLON.IShadowLight;
         get activeLight(): BABYLON.IShadowLight;
         set activeLight(light: BABYLON.IShadowLight);
         set activeLight(light: BABYLON.IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;
         clone(name: string): ShadowOnlyMaterial;

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

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

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

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

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


File diff suppressed because it is too large
+ 372 - 54
dist/preview release/inspector/babylon.inspector.bundle.max.js


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


+ 41 - 0
dist/preview release/inspector/babylon.inspector.d.ts

@@ -604,6 +604,18 @@ declare module INSPECTOR {
     }
     }
 }
 }
 declare module INSPECTOR {
 declare module INSPECTOR {
+    interface IGraphActionsBarProps {
+        addKeyframe: () => void;
+        handleValueChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+        flatTangent: () => void;
+        currentValue: number;
+    }
+    export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
+        constructor(props: IGraphActionsBarProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface IAnimationCurveEditorComponentProps {
     interface IAnimationCurveEditorComponentProps {
         close: (event: any) => void;
         close: (event: any) => void;
         playOrPause: () => void;
         playOrPause: () => void;
@@ -619,13 +631,16 @@ declare module INSPECTOR {
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: BABYLON.Animation[];
         animations: BABYLON.Animation[];
         animationName: string;
         animationName: string;
+        animationType: string;
         animationTargetProperty: string;
         animationTargetProperty: string;
         isOpen: boolean;
         isOpen: boolean;
         selected: BABYLON.Animation;
         selected: BABYLON.Animation;
         currentPathData: string | undefined;
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
         currentFrame: number;
+        currentValue: number;
         frameAxisLength: ICanvasAxis[];
         frameAxisLength: ICanvasAxis[];
+        flatTangent: boolean;
     }> {
     }> {
         readonly _heightScale: number;
         readonly _heightScale: number;
         readonly _canvasLength: number;
         readonly _canvasLength: number;
@@ -638,8 +653,11 @@ declare module INSPECTOR {
         constructor(props: IAnimationCurveEditorComponentProps);
         constructor(props: IAnimationCurveEditorComponentProps);
         componentDidMount(): void;
         componentDidMount(): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handleValueChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>): void;
         handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
         addAnimation(): void;
         addAnimation(): void;
+        addKeyframeClick(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {
         getAnimationProperties(animation: BABYLON.Animation): {
@@ -648,6 +666,8 @@ declare module INSPECTOR {
         };
         };
         getPathData(animation: BABYLON.Animation): string;
         getPathData(animation: BABYLON.Animation): string;
         drawAllFrames(initialKey: BABYLON.IAnimationKey, endKey: BABYLON.IAnimationKey, easingFunction: BABYLON.EasingFunction): void;
         drawAllFrames(initialKey: BABYLON.IAnimationKey, endKey: BABYLON.IAnimationKey, easingFunction: BABYLON.EasingFunction): void;
+        curvePathFlat(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, dataType: number): string;
+        curvePathWithTangents(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, type: number): string;
         curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
         curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
         linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
@@ -655,8 +675,21 @@ declare module INSPECTOR {
         setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
         setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
         isAnimationPlaying(): void;
         isAnimationPlaying(): void;
         selectAnimation(animation: BABYLON.Animation): void;
         selectAnimation(animation: BABYLON.Animation): void;
+        getAnimationData(animation: BABYLON.Animation): {
+            loopMode: number | undefined;
+            name: string;
+            blendingSpeed: number;
+            targetPropertyPath: string[];
+            targetProperty: string;
+            framesPerSecond: number;
+            highestFrame: number;
+            serialized: any;
+            usesTangents: BABYLON.IAnimationKey | undefined;
+        };
+        getAnimationTypeofChange(selected: string): number;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
         changeCurrentFrame(frame: number): void;
         changeCurrentFrame(frame: number): void;
+        setFlatTangent(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
@@ -1819,7 +1852,14 @@ declare module INSPECTOR {
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     }
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        private _snippetUrl;
         constructor(props: ISpriteManagerPropertyGridComponentProps);
         constructor(props: ISpriteManagerPropertyGridComponentProps);
+        addNewSprite(): void;
+        disposeManager(): void;
+        saveToFile(): void;
+        loadFromFile(file: File): void;
+        loadFromSnippet(): void;
+        saveToSnippet(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
@@ -1835,6 +1875,7 @@ declare module INSPECTOR {
         constructor(props: ISpritePropertyGridComponentProps);
         constructor(props: ISpritePropertyGridComponentProps);
         onManagerLink(): void;
         onManagerLink(): void;
         switchPlayStopState(): void;
         switchPlayStopState(): void;
+        disposeSprite(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }

+ 83 - 0
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -682,6 +682,19 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar" {
+    import * as React from "react";
+    interface IGraphActionsBarProps {
+        addKeyframe: () => void;
+        handleValueChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+        flatTangent: () => void;
+        currentValue: number;
+    }
+    export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
+        constructor(props: IGraphActionsBarProps);
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent" {
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent" {
     import * as React from "react";
     import * as React from "react";
     import { Animation } from 'babylonjs/Animations/animation';
     import { Animation } from 'babylonjs/Animations/animation';
@@ -706,13 +719,16 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: Animation[];
         animations: Animation[];
         animationName: string;
         animationName: string;
+        animationType: string;
         animationTargetProperty: string;
         animationTargetProperty: string;
         isOpen: boolean;
         isOpen: boolean;
         selected: Animation;
         selected: Animation;
         currentPathData: string | undefined;
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
         currentFrame: number;
+        currentValue: number;
         frameAxisLength: ICanvasAxis[];
         frameAxisLength: ICanvasAxis[];
+        flatTangent: boolean;
     }> {
     }> {
         readonly _heightScale: number;
         readonly _heightScale: number;
         readonly _canvasLength: number;
         readonly _canvasLength: number;
@@ -725,8 +741,11 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         constructor(props: IAnimationCurveEditorComponentProps);
         constructor(props: IAnimationCurveEditorComponentProps);
         componentDidMount(): void;
         componentDidMount(): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handleValueChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>): void;
         handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
         addAnimation(): void;
         addAnimation(): void;
+        addKeyframeClick(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: Vector2, index: number): void;
         updateKeyframe(keyframe: Vector2, index: number): void;
         getAnimationProperties(animation: Animation): {
         getAnimationProperties(animation: Animation): {
@@ -735,6 +754,8 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         };
         };
         getPathData(animation: Animation): string;
         getPathData(animation: Animation): string;
         drawAllFrames(initialKey: IAnimationKey, endKey: IAnimationKey, easingFunction: EasingFunction): void;
         drawAllFrames(initialKey: IAnimationKey, endKey: IAnimationKey, easingFunction: EasingFunction): void;
+        curvePathFlat(keyframes: IAnimationKey[], data: string, middle: number, dataType: number): string;
+        curvePathWithTangents(keyframes: IAnimationKey[], data: string, middle: number, type: number): string;
         curvePath(keyframes: IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction): string;
         curvePath(keyframes: IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction): string;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         linearInterpolation(keyframes: IAnimationKey[], data: string, middle: number): string;
         linearInterpolation(keyframes: IAnimationKey[], data: string, middle: number): string;
@@ -742,8 +763,21 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         setKeyframePoint(controlPoints: Vector2[], index: number, keyframesCount: number): void;
         setKeyframePoint(controlPoints: Vector2[], index: number, keyframesCount: number): void;
         isAnimationPlaying(): void;
         isAnimationPlaying(): void;
         selectAnimation(animation: Animation): void;
         selectAnimation(animation: Animation): void;
+        getAnimationData(animation: Animation): {
+            loopMode: number | undefined;
+            name: string;
+            blendingSpeed: number;
+            targetPropertyPath: string[];
+            targetProperty: string;
+            framesPerSecond: number;
+            highestFrame: number;
+            serialized: any;
+            usesTangents: IAnimationKey | undefined;
+        };
+        getAnimationTypeofChange(selected: string): number;
         interpolateControlPoints(p0: Vector2, p1: Vector2, u: number, p2: Vector2, v: number, p3: Vector2): Vector2[] | undefined;
         interpolateControlPoints(p0: Vector2, p1: Vector2, u: number, p2: Vector2, v: number, p3: Vector2): Vector2[] | undefined;
         changeCurrentFrame(frame: number): void;
         changeCurrentFrame(frame: number): void;
+        setFlatTangent(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
@@ -2317,7 +2351,14 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/spr
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
     }
     }
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        private _snippetUrl;
         constructor(props: ISpriteManagerPropertyGridComponentProps);
         constructor(props: ISpriteManagerPropertyGridComponentProps);
+        addNewSprite(): void;
+        disposeManager(): void;
+        saveToFile(): void;
+        loadFromFile(file: File): void;
+        loadFromSnippet(): void;
+        saveToSnippet(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
@@ -2339,6 +2380,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/spr
         constructor(props: ISpritePropertyGridComponentProps);
         constructor(props: ISpritePropertyGridComponentProps);
         onManagerLink(): void;
         onManagerLink(): void;
         switchPlayStopState(): void;
         switchPlayStopState(): void;
+        disposeSprite(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
@@ -3649,6 +3691,18 @@ declare module INSPECTOR {
     }
     }
 }
 }
 declare module INSPECTOR {
 declare module INSPECTOR {
+    interface IGraphActionsBarProps {
+        addKeyframe: () => void;
+        handleValueChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+        flatTangent: () => void;
+        currentValue: number;
+    }
+    export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
+        constructor(props: IGraphActionsBarProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface IAnimationCurveEditorComponentProps {
     interface IAnimationCurveEditorComponentProps {
         close: (event: any) => void;
         close: (event: any) => void;
         playOrPause: () => void;
         playOrPause: () => void;
@@ -3664,13 +3718,16 @@ declare module INSPECTOR {
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: BABYLON.Animation[];
         animations: BABYLON.Animation[];
         animationName: string;
         animationName: string;
+        animationType: string;
         animationTargetProperty: string;
         animationTargetProperty: string;
         isOpen: boolean;
         isOpen: boolean;
         selected: BABYLON.Animation;
         selected: BABYLON.Animation;
         currentPathData: string | undefined;
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
         currentFrame: number;
+        currentValue: number;
         frameAxisLength: ICanvasAxis[];
         frameAxisLength: ICanvasAxis[];
+        flatTangent: boolean;
     }> {
     }> {
         readonly _heightScale: number;
         readonly _heightScale: number;
         readonly _canvasLength: number;
         readonly _canvasLength: number;
@@ -3683,8 +3740,11 @@ declare module INSPECTOR {
         constructor(props: IAnimationCurveEditorComponentProps);
         constructor(props: IAnimationCurveEditorComponentProps);
         componentDidMount(): void;
         componentDidMount(): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handleValueChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>): void;
         handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
         handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
         addAnimation(): void;
         addAnimation(): void;
+        addKeyframeClick(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {
         getAnimationProperties(animation: BABYLON.Animation): {
@@ -3693,6 +3753,8 @@ declare module INSPECTOR {
         };
         };
         getPathData(animation: BABYLON.Animation): string;
         getPathData(animation: BABYLON.Animation): string;
         drawAllFrames(initialKey: BABYLON.IAnimationKey, endKey: BABYLON.IAnimationKey, easingFunction: BABYLON.EasingFunction): void;
         drawAllFrames(initialKey: BABYLON.IAnimationKey, endKey: BABYLON.IAnimationKey, easingFunction: BABYLON.EasingFunction): void;
+        curvePathFlat(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, dataType: number): string;
+        curvePathWithTangents(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, type: number): string;
         curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
         curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
         linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
@@ -3700,8 +3762,21 @@ declare module INSPECTOR {
         setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
         setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
         isAnimationPlaying(): void;
         isAnimationPlaying(): void;
         selectAnimation(animation: BABYLON.Animation): void;
         selectAnimation(animation: BABYLON.Animation): void;
+        getAnimationData(animation: BABYLON.Animation): {
+            loopMode: number | undefined;
+            name: string;
+            blendingSpeed: number;
+            targetPropertyPath: string[];
+            targetProperty: string;
+            framesPerSecond: number;
+            highestFrame: number;
+            serialized: any;
+            usesTangents: BABYLON.IAnimationKey | undefined;
+        };
+        getAnimationTypeofChange(selected: string): number;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
         changeCurrentFrame(frame: number): void;
         changeCurrentFrame(frame: number): void;
+        setFlatTangent(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
@@ -4864,7 +4939,14 @@ declare module INSPECTOR {
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     }
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        private _snippetUrl;
         constructor(props: ISpriteManagerPropertyGridComponentProps);
         constructor(props: ISpriteManagerPropertyGridComponentProps);
+        addNewSprite(): void;
+        disposeManager(): void;
+        saveToFile(): void;
+        loadFromFile(file: File): void;
+        loadFromSnippet(): void;
+        saveToSnippet(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }
@@ -4880,6 +4962,7 @@ declare module INSPECTOR {
         constructor(props: ISpritePropertyGridComponentProps);
         constructor(props: ISpritePropertyGridComponentProps);
         onManagerLink(): void;
         onManagerLink(): void;
         switchPlayStopState(): void;
         switchPlayStopState(): void;
+        disposeSprite(): void;
         render(): JSX.Element;
         render(): JSX.Element;
     }
     }
 }
 }

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

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

+ 51 - 51
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -290,60 +290,60 @@ var EXT_mesh_gpu_instancing = /** @class */ (function () {
     EXT_mesh_gpu_instancing.prototype.loadNodeAsync = function (context, node, assign) {
     EXT_mesh_gpu_instancing.prototype.loadNodeAsync = function (context, node, assign) {
         var _this = this;
         var _this = this;
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].LoadExtensionAsync(context, node, this.name, function (extensionContext, extension) {
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].LoadExtensionAsync(context, node, this.name, function (extensionContext, extension) {
-            return _this._loader.loadNodeAsync("#/nodes/" + node.index, node, function (babylonTransformNode) {
-                var promises = new Array();
-                var instanceCount = null;
-                var loadAttribute = function (attribute, assignBufferFunc) {
-                    if (extension.attributes[attribute] == undefined) {
-                        return;
-                    }
-                    var accessor = _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["ArrayItem"].Get(extensionContext + "/attributes/" + attribute, _this._loader.gltf.accessors, extension.attributes[attribute]);
-                    if (instanceCount === null) {
-                        instanceCount = accessor.count;
-                    }
-                    else if (instanceCount !== accessor.count) {
-                        throw new Error(extensionContext + "/attributes: Instance buffer accessors do not have the same count.");
-                    }
-                    promises.push(_this._loader._loadFloatAccessorAsync("/accessors/" + accessor.bufferView, accessor).then(function (data) {
-                        assignBufferFunc(data);
-                    }));
-                };
-                var translationBuffer = null;
-                var rotationBuffer = null;
-                var scaleBuffer = null;
-                loadAttribute("TRANSLATION", function (data) { translationBuffer = data; });
-                loadAttribute("ROTATION", function (data) { rotationBuffer = data; });
-                loadAttribute("SCALE", function (data) { scaleBuffer = data; });
-                return Promise.all(promises).then(function () {
-                    if (instanceCount) {
-                        var instanceName = "";
-                        var instance = null;
-                        var digitLength = instanceCount.toString().length;
+            var promise = _this._loader.loadNodeAsync("#/nodes/" + node.index, node, assign);
+            if (!node._primitiveBabylonMeshes) {
+                return promise;
+            }
+            // Hide the source meshes.
+            for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
+                var babylonMesh = _a[_i];
+                babylonMesh.isVisible = false;
+            }
+            var promises = new Array();
+            var instanceCount = 0;
+            var loadAttribute = function (attribute) {
+                if (extension.attributes[attribute] == undefined) {
+                    promises.push(Promise.resolve(null));
+                    return;
+                }
+                var accessor = _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["ArrayItem"].Get(extensionContext + "/attributes/" + attribute, _this._loader.gltf.accessors, extension.attributes[attribute]);
+                promises.push(_this._loader._loadFloatAccessorAsync("/accessors/" + accessor.bufferView, accessor));
+                if (instanceCount === 0) {
+                    instanceCount = accessor.count;
+                }
+                else if (instanceCount !== accessor.count) {
+                    throw new Error(extensionContext + "/attributes: Instance buffer accessors do not have the same count.");
+                }
+            };
+            loadAttribute("TRANSLATION");
+            loadAttribute("ROTATION");
+            loadAttribute("SCALE");
+            if (instanceCount == 0) {
+                return promise;
+            }
+            var digitLength = instanceCount.toString().length;
+            for (var i = 0; i < instanceCount; ++i) {
+                for (var _b = 0, _c = node._primitiveBabylonMeshes; _b < _c.length; _b++) {
+                    var babylonMesh = _c[_b];
+                    var instanceName = (babylonMesh.name || babylonMesh.id) + "_" + babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["StringTools"].PadNumber(i, digitLength);
+                    var babylonInstancedMesh = babylonMesh.createInstance(instanceName);
+                    babylonInstancedMesh.setParent(babylonMesh);
+                }
+            }
+            return promise.then(function (babylonTransformNode) {
+                return Promise.all(promises).then(function (_a) {
+                    var translationBuffer = _a[0], rotationBuffer = _a[1], scaleBuffer = _a[2];
+                    for (var _i = 0, _b = node._primitiveBabylonMeshes; _i < _b.length; _i++) {
+                        var babylonMesh = _b[_i];
+                        var babylonInstancedMeshes = babylonMesh.getChildMeshes(true, function (node) { return node.isAnInstance; });
                         for (var i = 0; i < instanceCount; ++i) {
                         for (var i = 0; i < instanceCount; ++i) {
-                            if (node._primitiveBabylonMeshes) {
-                                for (var j = 0; j < node._primitiveBabylonMeshes.length; ++j) {
-                                    var babylonMeshPrimitive = node._primitiveBabylonMeshes[j];
-                                    instanceName = (babylonMeshPrimitive.name || babylonMeshPrimitive.id) + "_" + babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["StringTools"].PadNumber(i, digitLength);
-                                    if (babylonMeshPrimitive.isAnInstance) {
-                                        instance = babylonMeshPrimitive.sourceMesh.createInstance(instanceName);
-                                    }
-                                    else if (babylonMeshPrimitive.createInstance) {
-                                        instance = babylonMeshPrimitive.createInstance(instanceName);
-                                    }
-                                    if (instance) {
-                                        instance.setParent(babylonMeshPrimitive);
-                                        translationBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(translationBuffer, i * 3, instance.position)
-                                            : instance.position.set(0, 0, 0);
-                                        rotationBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].FromArrayToRef(rotationBuffer, i * 4, instance.rotationQuaternion)
-                                            : instance.rotationQuaternion.set(0, 0, 0, 1);
-                                        scaleBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(scaleBuffer, i * 3, instance.scaling)
-                                            : instance.scaling.set(1, 1, 1);
-                                    }
-                                }
-                            }
+                            var babylonInstancedMesh = babylonInstancedMeshes[i];
+                            translationBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(translationBuffer, i * 3, babylonInstancedMesh.position);
+                            rotationBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].FromArrayToRef(rotationBuffer, i * 4, babylonInstancedMesh.rotationQuaternion);
+                            scaleBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(scaleBuffer, i * 3, babylonInstancedMesh.scaling);
+                            babylonInstancedMesh.refreshBoundingInfo();
                         }
                         }
                     }
                     }
-                    assign(babylonTransformNode);
                     return babylonTransformNode;
                     return babylonTransformNode;
                 });
                 });
             });
             });

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


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

@@ -2870,60 +2870,60 @@ var EXT_mesh_gpu_instancing = /** @class */ (function () {
     EXT_mesh_gpu_instancing.prototype.loadNodeAsync = function (context, node, assign) {
     EXT_mesh_gpu_instancing.prototype.loadNodeAsync = function (context, node, assign) {
         var _this = this;
         var _this = this;
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].LoadExtensionAsync(context, node, this.name, function (extensionContext, extension) {
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].LoadExtensionAsync(context, node, this.name, function (extensionContext, extension) {
-            return _this._loader.loadNodeAsync("#/nodes/" + node.index, node, function (babylonTransformNode) {
-                var promises = new Array();
-                var instanceCount = null;
-                var loadAttribute = function (attribute, assignBufferFunc) {
-                    if (extension.attributes[attribute] == undefined) {
-                        return;
-                    }
-                    var accessor = _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["ArrayItem"].Get(extensionContext + "/attributes/" + attribute, _this._loader.gltf.accessors, extension.attributes[attribute]);
-                    if (instanceCount === null) {
-                        instanceCount = accessor.count;
-                    }
-                    else if (instanceCount !== accessor.count) {
-                        throw new Error(extensionContext + "/attributes: Instance buffer accessors do not have the same count.");
-                    }
-                    promises.push(_this._loader._loadFloatAccessorAsync("/accessors/" + accessor.bufferView, accessor).then(function (data) {
-                        assignBufferFunc(data);
-                    }));
-                };
-                var translationBuffer = null;
-                var rotationBuffer = null;
-                var scaleBuffer = null;
-                loadAttribute("TRANSLATION", function (data) { translationBuffer = data; });
-                loadAttribute("ROTATION", function (data) { rotationBuffer = data; });
-                loadAttribute("SCALE", function (data) { scaleBuffer = data; });
-                return Promise.all(promises).then(function () {
-                    if (instanceCount) {
-                        var instanceName = "";
-                        var instance = null;
-                        var digitLength = instanceCount.toString().length;
+            var promise = _this._loader.loadNodeAsync("#/nodes/" + node.index, node, assign);
+            if (!node._primitiveBabylonMeshes) {
+                return promise;
+            }
+            // Hide the source meshes.
+            for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
+                var babylonMesh = _a[_i];
+                babylonMesh.isVisible = false;
+            }
+            var promises = new Array();
+            var instanceCount = 0;
+            var loadAttribute = function (attribute) {
+                if (extension.attributes[attribute] == undefined) {
+                    promises.push(Promise.resolve(null));
+                    return;
+                }
+                var accessor = _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["ArrayItem"].Get(extensionContext + "/attributes/" + attribute, _this._loader.gltf.accessors, extension.attributes[attribute]);
+                promises.push(_this._loader._loadFloatAccessorAsync("/accessors/" + accessor.bufferView, accessor));
+                if (instanceCount === 0) {
+                    instanceCount = accessor.count;
+                }
+                else if (instanceCount !== accessor.count) {
+                    throw new Error(extensionContext + "/attributes: Instance buffer accessors do not have the same count.");
+                }
+            };
+            loadAttribute("TRANSLATION");
+            loadAttribute("ROTATION");
+            loadAttribute("SCALE");
+            if (instanceCount == 0) {
+                return promise;
+            }
+            var digitLength = instanceCount.toString().length;
+            for (var i = 0; i < instanceCount; ++i) {
+                for (var _b = 0, _c = node._primitiveBabylonMeshes; _b < _c.length; _b++) {
+                    var babylonMesh = _c[_b];
+                    var instanceName = (babylonMesh.name || babylonMesh.id) + "_" + babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["StringTools"].PadNumber(i, digitLength);
+                    var babylonInstancedMesh = babylonMesh.createInstance(instanceName);
+                    babylonInstancedMesh.setParent(babylonMesh);
+                }
+            }
+            return promise.then(function (babylonTransformNode) {
+                return Promise.all(promises).then(function (_a) {
+                    var translationBuffer = _a[0], rotationBuffer = _a[1], scaleBuffer = _a[2];
+                    for (var _i = 0, _b = node._primitiveBabylonMeshes; _i < _b.length; _i++) {
+                        var babylonMesh = _b[_i];
+                        var babylonInstancedMeshes = babylonMesh.getChildMeshes(true, function (node) { return node.isAnInstance; });
                         for (var i = 0; i < instanceCount; ++i) {
                         for (var i = 0; i < instanceCount; ++i) {
-                            if (node._primitiveBabylonMeshes) {
-                                for (var j = 0; j < node._primitiveBabylonMeshes.length; ++j) {
-                                    var babylonMeshPrimitive = node._primitiveBabylonMeshes[j];
-                                    instanceName = (babylonMeshPrimitive.name || babylonMeshPrimitive.id) + "_" + babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["StringTools"].PadNumber(i, digitLength);
-                                    if (babylonMeshPrimitive.isAnInstance) {
-                                        instance = babylonMeshPrimitive.sourceMesh.createInstance(instanceName);
-                                    }
-                                    else if (babylonMeshPrimitive.createInstance) {
-                                        instance = babylonMeshPrimitive.createInstance(instanceName);
-                                    }
-                                    if (instance) {
-                                        instance.setParent(babylonMeshPrimitive);
-                                        translationBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(translationBuffer, i * 3, instance.position)
-                                            : instance.position.set(0, 0, 0);
-                                        rotationBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].FromArrayToRef(rotationBuffer, i * 4, instance.rotationQuaternion)
-                                            : instance.rotationQuaternion.set(0, 0, 0, 1);
-                                        scaleBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(scaleBuffer, i * 3, instance.scaling)
-                                            : instance.scaling.set(1, 1, 1);
-                                    }
-                                }
-                            }
+                            var babylonInstancedMesh = babylonInstancedMeshes[i];
+                            translationBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(translationBuffer, i * 3, babylonInstancedMesh.position);
+                            rotationBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].FromArrayToRef(rotationBuffer, i * 4, babylonInstancedMesh.rotationQuaternion);
+                            scaleBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(scaleBuffer, i * 3, babylonInstancedMesh.scaling);
+                            babylonInstancedMesh.refreshBoundingInfo();
                         }
                         }
                     }
                     }
-                    assign(babylonTransformNode);
                     return babylonTransformNode;
                     return babylonTransformNode;
                 });
                 });
             });
             });

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
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


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

@@ -4250,60 +4250,60 @@ var EXT_mesh_gpu_instancing = /** @class */ (function () {
     EXT_mesh_gpu_instancing.prototype.loadNodeAsync = function (context, node, assign) {
     EXT_mesh_gpu_instancing.prototype.loadNodeAsync = function (context, node, assign) {
         var _this = this;
         var _this = this;
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].LoadExtensionAsync(context, node, this.name, function (extensionContext, extension) {
         return _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["GLTFLoader"].LoadExtensionAsync(context, node, this.name, function (extensionContext, extension) {
-            return _this._loader.loadNodeAsync("#/nodes/" + node.index, node, function (babylonTransformNode) {
-                var promises = new Array();
-                var instanceCount = null;
-                var loadAttribute = function (attribute, assignBufferFunc) {
-                    if (extension.attributes[attribute] == undefined) {
-                        return;
-                    }
-                    var accessor = _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["ArrayItem"].Get(extensionContext + "/attributes/" + attribute, _this._loader.gltf.accessors, extension.attributes[attribute]);
-                    if (instanceCount === null) {
-                        instanceCount = accessor.count;
-                    }
-                    else if (instanceCount !== accessor.count) {
-                        throw new Error(extensionContext + "/attributes: Instance buffer accessors do not have the same count.");
-                    }
-                    promises.push(_this._loader._loadFloatAccessorAsync("/accessors/" + accessor.bufferView, accessor).then(function (data) {
-                        assignBufferFunc(data);
-                    }));
-                };
-                var translationBuffer = null;
-                var rotationBuffer = null;
-                var scaleBuffer = null;
-                loadAttribute("TRANSLATION", function (data) { translationBuffer = data; });
-                loadAttribute("ROTATION", function (data) { rotationBuffer = data; });
-                loadAttribute("SCALE", function (data) { scaleBuffer = data; });
-                return Promise.all(promises).then(function () {
-                    if (instanceCount) {
-                        var instanceName = "";
-                        var instance = null;
-                        var digitLength = instanceCount.toString().length;
+            var promise = _this._loader.loadNodeAsync("#/nodes/" + node.index, node, assign);
+            if (!node._primitiveBabylonMeshes) {
+                return promise;
+            }
+            // Hide the source meshes.
+            for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
+                var babylonMesh = _a[_i];
+                babylonMesh.isVisible = false;
+            }
+            var promises = new Array();
+            var instanceCount = 0;
+            var loadAttribute = function (attribute) {
+                if (extension.attributes[attribute] == undefined) {
+                    promises.push(Promise.resolve(null));
+                    return;
+                }
+                var accessor = _glTFLoader__WEBPACK_IMPORTED_MODULE_1__["ArrayItem"].Get(extensionContext + "/attributes/" + attribute, _this._loader.gltf.accessors, extension.attributes[attribute]);
+                promises.push(_this._loader._loadFloatAccessorAsync("/accessors/" + accessor.bufferView, accessor));
+                if (instanceCount === 0) {
+                    instanceCount = accessor.count;
+                }
+                else if (instanceCount !== accessor.count) {
+                    throw new Error(extensionContext + "/attributes: Instance buffer accessors do not have the same count.");
+                }
+            };
+            loadAttribute("TRANSLATION");
+            loadAttribute("ROTATION");
+            loadAttribute("SCALE");
+            if (instanceCount == 0) {
+                return promise;
+            }
+            var digitLength = instanceCount.toString().length;
+            for (var i = 0; i < instanceCount; ++i) {
+                for (var _b = 0, _c = node._primitiveBabylonMeshes; _b < _c.length; _b++) {
+                    var babylonMesh = _c[_b];
+                    var instanceName = (babylonMesh.name || babylonMesh.id) + "_" + babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["StringTools"].PadNumber(i, digitLength);
+                    var babylonInstancedMesh = babylonMesh.createInstance(instanceName);
+                    babylonInstancedMesh.setParent(babylonMesh);
+                }
+            }
+            return promise.then(function (babylonTransformNode) {
+                return Promise.all(promises).then(function (_a) {
+                    var translationBuffer = _a[0], rotationBuffer = _a[1], scaleBuffer = _a[2];
+                    for (var _i = 0, _b = node._primitiveBabylonMeshes; _i < _b.length; _i++) {
+                        var babylonMesh = _b[_i];
+                        var babylonInstancedMeshes = babylonMesh.getChildMeshes(true, function (node) { return node.isAnInstance; });
                         for (var i = 0; i < instanceCount; ++i) {
                         for (var i = 0; i < instanceCount; ++i) {
-                            if (node._primitiveBabylonMeshes) {
-                                for (var j = 0; j < node._primitiveBabylonMeshes.length; ++j) {
-                                    var babylonMeshPrimitive = node._primitiveBabylonMeshes[j];
-                                    instanceName = (babylonMeshPrimitive.name || babylonMeshPrimitive.id) + "_" + babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["StringTools"].PadNumber(i, digitLength);
-                                    if (babylonMeshPrimitive.isAnInstance) {
-                                        instance = babylonMeshPrimitive.sourceMesh.createInstance(instanceName);
-                                    }
-                                    else if (babylonMeshPrimitive.createInstance) {
-                                        instance = babylonMeshPrimitive.createInstance(instanceName);
-                                    }
-                                    if (instance) {
-                                        instance.setParent(babylonMeshPrimitive);
-                                        translationBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(translationBuffer, i * 3, instance.position)
-                                            : instance.position.set(0, 0, 0);
-                                        rotationBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].FromArrayToRef(rotationBuffer, i * 4, instance.rotationQuaternion)
-                                            : instance.rotationQuaternion.set(0, 0, 0, 1);
-                                        scaleBuffer ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(scaleBuffer, i * 3, instance.scaling)
-                                            : instance.scaling.set(1, 1, 1);
-                                    }
-                                }
-                            }
+                            var babylonInstancedMesh = babylonInstancedMeshes[i];
+                            translationBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(translationBuffer, i * 3, babylonInstancedMesh.position);
+                            rotationBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Quaternion"].FromArrayToRef(rotationBuffer, i * 4, babylonInstancedMesh.rotationQuaternion);
+                            scaleBuffer && babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__["Vector3"].FromArrayToRef(scaleBuffer, i * 3, babylonInstancedMesh.scaling);
+                            babylonInstancedMesh.refreshBoundingInfo();
                         }
                         }
                     }
                     }
-                    assign(babylonTransformNode);
                     return babylonTransformNode;
                     return babylonTransformNode;
                 });
                 });
             });
             });

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


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

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

+ 30 - 4
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js

@@ -519,11 +519,12 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
     Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(ShadowOnlyMaterial, _super);
     Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(ShadowOnlyMaterial, _super);
     function ShadowOnlyMaterial(name, scene) {
     function ShadowOnlyMaterial(name, scene) {
         var _this = _super.call(this, name, scene) || this;
         var _this = _super.call(this, name, scene) || this;
+        _this._needAlphaBlending = true;
         _this.shadowColor = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Color3"].Black();
         _this.shadowColor = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Color3"].Black();
         return _this;
         return _this;
     }
     }
     ShadowOnlyMaterial.prototype.needAlphaBlending = function () {
     ShadowOnlyMaterial.prototype.needAlphaBlending = function () {
-        return true;
+        return this._needAlphaBlending;
     };
     };
     ShadowOnlyMaterial.prototype.needAlphaTesting = function () {
     ShadowOnlyMaterial.prototype.needAlphaTesting = function () {
         return false;
         return false;
@@ -541,8 +542,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         enumerable: true,
         enumerable: true,
         configurable: true
         configurable: true
     });
     });
+    ShadowOnlyMaterial.prototype._getFirstShadowLightForMesh = function (mesh) {
+        for (var _i = 0, _a = mesh.lightSources; _i < _a.length; _i++) {
+            var light = _a[_i];
+            if (light.shadowEnabled) {
+                return light;
+            }
+        }
+        return null;
+    };
     // Methods
     // Methods
     ShadowOnlyMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
     ShadowOnlyMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
+        var _a;
         if (this.isFrozen) {
         if (this.isFrozen) {
             if (subMesh.effect && subMesh.effect._wasPreviouslyReady) {
             if (subMesh.effect && subMesh.effect._wasPreviouslyReady) {
                 return true;
                 return true;
@@ -559,8 +570,8 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         var engine = scene.getEngine();
         var engine = scene.getEngine();
         // Ensure that active light is the first shadow light
         // Ensure that active light is the first shadow light
         if (this._activeLight) {
         if (this._activeLight) {
-            for (var _i = 0, _a = mesh.lightSources; _i < _a.length; _i++) {
-                var light = _a[_i];
+            for (var _i = 0, _b = mesh.lightSources; _i < _b.length; _i++) {
+                var light = _b[_i];
                 if (light.shadowEnabled) {
                 if (light.shadowEnabled) {
                     if (this._activeLight === light) {
                     if (this._activeLight === light) {
                         break; // We are good
                         break; // We are good
@@ -577,6 +588,12 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForMisc(mesh, scene, false, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh), defines);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForMisc(mesh, scene, false, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh), defines);
         defines._needNormals = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForLights(scene, mesh, defines, false, 1);
         defines._needNormals = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForLights(scene, mesh, defines, false, 1);
+        var shadowGenerator = (_a = this._getFirstShadowLightForMesh(mesh)) === null || _a === void 0 ? void 0 : _a.getShadowGenerator();
+        this._needAlphaBlending = true;
+        if (shadowGenerator && shadowGenerator.getClassName && shadowGenerator.getClassName() === 'CascadedShadowGenerator') {
+            var csg = shadowGenerator;
+            this._needAlphaBlending = !csg.autoCalcDepthBounds;
+        }
         // Attribs
         // Attribs
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForAttributes(mesh, defines, false, true);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForAttributes(mesh, defines, false, true);
         // Get correct effect
         // Get correct effect
@@ -664,9 +681,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         // Lights
         // Lights
         if (scene.lightsEnabled) {
         if (scene.lightsEnabled) {
             babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].BindLights(scene, mesh, this._activeEffect, defines, 1);
             babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].BindLights(scene, mesh, this._activeEffect, defines, 1);
+            var light = this._getFirstShadowLightForMesh(mesh);
+            if (light) {
+                // Make sure the uniforms for this light will be rebound for other materials using this light when rendering the current frame.
+                // Indeed, there is an optimization in Light that binds the light uniforms only once per frame for a given light (if using ubo).
+                // Doing this way assumes that all uses of this light are the same, meaning all parameters passed to Light._bindLlight
+                // are the same, notably useSpecular. However, isReadyForSubMesh (see above) is passing false for this parameter, which may not be
+                // the value the other materials may pass.
+                light._renderId = -1;
+            }
         }
         }
         // View
         // View
-        if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Scene"].FOGMODE_NONE) {
+        if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Scene"].FOGMODE_NONE || defines["SHADOWCSM0"]) {
             this._activeEffect.setMatrix("view", scene.getViewMatrix());
             this._activeEffect.setMatrix("view", scene.getViewMatrix());
         }
         }
         // Fog
         // Fog

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


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


+ 2 - 0
dist/preview release/materialsLibrary/babylonjs.materials.d.ts

@@ -559,6 +559,7 @@ declare module BABYLON {
 declare module BABYLON {
 declare module BABYLON {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
         private _activeLight;
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: BABYLON.Scene);
         constructor(name: string, scene: BABYLON.Scene);
         shadowColor: BABYLON.Color3;
         shadowColor: BABYLON.Color3;
         needAlphaBlending(): boolean;
         needAlphaBlending(): boolean;
@@ -566,6 +567,7 @@ declare module BABYLON {
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         get activeLight(): BABYLON.IShadowLight;
         get activeLight(): BABYLON.IShadowLight;
         set activeLight(light: BABYLON.IShadowLight);
         set activeLight(light: BABYLON.IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;
         clone(name: string): ShadowOnlyMaterial;

+ 30 - 4
dist/preview release/materialsLibrary/babylonjs.materials.js

@@ -4683,11 +4683,12 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
     Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(ShadowOnlyMaterial, _super);
     Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(ShadowOnlyMaterial, _super);
     function ShadowOnlyMaterial(name, scene) {
     function ShadowOnlyMaterial(name, scene) {
         var _this = _super.call(this, name, scene) || this;
         var _this = _super.call(this, name, scene) || this;
+        _this._needAlphaBlending = true;
         _this.shadowColor = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Color3"].Black();
         _this.shadowColor = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Color3"].Black();
         return _this;
         return _this;
     }
     }
     ShadowOnlyMaterial.prototype.needAlphaBlending = function () {
     ShadowOnlyMaterial.prototype.needAlphaBlending = function () {
-        return true;
+        return this._needAlphaBlending;
     };
     };
     ShadowOnlyMaterial.prototype.needAlphaTesting = function () {
     ShadowOnlyMaterial.prototype.needAlphaTesting = function () {
         return false;
         return false;
@@ -4705,8 +4706,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         enumerable: true,
         enumerable: true,
         configurable: true
         configurable: true
     });
     });
+    ShadowOnlyMaterial.prototype._getFirstShadowLightForMesh = function (mesh) {
+        for (var _i = 0, _a = mesh.lightSources; _i < _a.length; _i++) {
+            var light = _a[_i];
+            if (light.shadowEnabled) {
+                return light;
+            }
+        }
+        return null;
+    };
     // Methods
     // Methods
     ShadowOnlyMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
     ShadowOnlyMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
+        var _a;
         if (this.isFrozen) {
         if (this.isFrozen) {
             if (subMesh.effect && subMesh.effect._wasPreviouslyReady) {
             if (subMesh.effect && subMesh.effect._wasPreviouslyReady) {
                 return true;
                 return true;
@@ -4723,8 +4734,8 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         var engine = scene.getEngine();
         var engine = scene.getEngine();
         // Ensure that active light is the first shadow light
         // Ensure that active light is the first shadow light
         if (this._activeLight) {
         if (this._activeLight) {
-            for (var _i = 0, _a = mesh.lightSources; _i < _a.length; _i++) {
-                var light = _a[_i];
+            for (var _i = 0, _b = mesh.lightSources; _i < _b.length; _i++) {
+                var light = _b[_i];
                 if (light.shadowEnabled) {
                 if (light.shadowEnabled) {
                     if (this._activeLight === light) {
                     if (this._activeLight === light) {
                         break; // We are good
                         break; // We are good
@@ -4741,6 +4752,12 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForMisc(mesh, scene, false, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh), defines);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForMisc(mesh, scene, false, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh), defines);
         defines._needNormals = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForLights(scene, mesh, defines, false, 1);
         defines._needNormals = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForLights(scene, mesh, defines, false, 1);
+        var shadowGenerator = (_a = this._getFirstShadowLightForMesh(mesh)) === null || _a === void 0 ? void 0 : _a.getShadowGenerator();
+        this._needAlphaBlending = true;
+        if (shadowGenerator && shadowGenerator.getClassName && shadowGenerator.getClassName() === 'CascadedShadowGenerator') {
+            var csg = shadowGenerator;
+            this._needAlphaBlending = !csg.autoCalcDepthBounds;
+        }
         // Attribs
         // Attribs
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForAttributes(mesh, defines, false, true);
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForAttributes(mesh, defines, false, true);
         // Get correct effect
         // Get correct effect
@@ -4828,9 +4845,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         // Lights
         // Lights
         if (scene.lightsEnabled) {
         if (scene.lightsEnabled) {
             babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].BindLights(scene, mesh, this._activeEffect, defines, 1);
             babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].BindLights(scene, mesh, this._activeEffect, defines, 1);
+            var light = this._getFirstShadowLightForMesh(mesh);
+            if (light) {
+                // Make sure the uniforms for this light will be rebound for other materials using this light when rendering the current frame.
+                // Indeed, there is an optimization in Light that binds the light uniforms only once per frame for a given light (if using ubo).
+                // Doing this way assumes that all uses of this light are the same, meaning all parameters passed to Light._bindLlight
+                // are the same, notably useSpecular. However, isReadyForSubMesh (see above) is passing false for this parameter, which may not be
+                // the value the other materials may pass.
+                light._renderId = -1;
+            }
         }
         }
         // View
         // View
-        if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Scene"].FOGMODE_NONE) {
+        if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Scene"].FOGMODE_NONE || defines["SHADOWCSM0"]) {
             this._activeEffect.setMatrix("view", scene.getViewMatrix());
             this._activeEffect.setMatrix("view", scene.getViewMatrix());
         }
         }
         // Fog
         // Fog

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


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


+ 4 - 0
dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts

@@ -879,6 +879,7 @@ declare module "babylonjs-materials/shadowOnly/shadowOnlyMaterial" {
     import "babylonjs-materials/shadowOnly/shadowOnly.vertex";
     import "babylonjs-materials/shadowOnly/shadowOnly.vertex";
     export class ShadowOnlyMaterial extends PushMaterial {
     export class ShadowOnlyMaterial extends PushMaterial {
         private _activeLight;
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: Scene);
         constructor(name: string, scene: Scene);
         shadowColor: Color3;
         shadowColor: Color3;
         needAlphaBlending(): boolean;
         needAlphaBlending(): boolean;
@@ -886,6 +887,7 @@ declare module "babylonjs-materials/shadowOnly/shadowOnlyMaterial" {
         getAlphaTestTexture(): Nullable<BaseTexture>;
         getAlphaTestTexture(): Nullable<BaseTexture>;
         get activeLight(): IShadowLight;
         get activeLight(): IShadowLight;
         set activeLight(light: IShadowLight);
         set activeLight(light: IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;
         clone(name: string): ShadowOnlyMaterial;
@@ -2126,6 +2128,7 @@ declare module BABYLON {
 declare module BABYLON {
 declare module BABYLON {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
         private _activeLight;
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: BABYLON.Scene);
         constructor(name: string, scene: BABYLON.Scene);
         shadowColor: BABYLON.Color3;
         shadowColor: BABYLON.Color3;
         needAlphaBlending(): boolean;
         needAlphaBlending(): boolean;
@@ -2133,6 +2136,7 @@ declare module BABYLON {
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         get activeLight(): BABYLON.IShadowLight;
         get activeLight(): BABYLON.IShadowLight;
         set activeLight(light: BABYLON.IShadowLight);
         set activeLight(light: BABYLON.IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;
         clone(name: string): ShadowOnlyMaterial;

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

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

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


+ 1 - 1
dist/preview release/nodeEditor/babylon.nodeEditor.max.js

@@ -63947,7 +63947,7 @@ var GraphFrame = /** @class */ (function () {
                             var localPort = void 0;
                             var localPort = void 0;
                             if (!portAdded) {
                             if (!portAdded) {
                                 portAdded = true;
                                 portAdded = true;
-                                localPort = _frameNodePort__WEBPACK_IMPORTED_MODULE_3__["FrameNodePort"].CreateFrameNodePortElement(port.connectionPoint, link.nodeB, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
+                                localPort = _frameNodePort__WEBPACK_IMPORTED_MODULE_3__["FrameNodePort"].CreateFrameNodePortElement(port.connectionPoint, link.nodeA, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
                                 this._frameOutPorts.push(localPort);
                                 this._frameOutPorts.push(localPort);
                                 link.isVisible = true;
                                 link.isVisible = true;
                                 var onLinkDisposedObserver = link.onDisposedObservable.add(function (nodeLink) {
                                 var onLinkDisposedObserver = link.onDisposedObservable.add(function (nodeLink) {

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


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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large
+ 865 - 72
dist/preview release/viewer/babylon.module.d.ts


File diff suppressed because it is too large
+ 88 - 80
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


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

@@ -7,7 +7,7 @@
 - Added the `ShadowDepthWrapper` class to support accurate shadow generation for custom as well as node material shaders. [Doc](https://doc.babylonjs.com/babylon101/shadows#custom-shadow-map-shaders) ([Popov72](https://github.com/Popov72))
 - Added the `ShadowDepthWrapper` class to support accurate shadow generation for custom as well as node material shaders. [Doc](https://doc.babylonjs.com/babylon101/shadows#custom-shadow-map-shaders) ([Popov72](https://github.com/Popov72))
 - Added Babylon.js Texture [tools](https://www.babylonjs.com/tools/ibl) to prefilter HDR files ([Sebavan](https://github.com/sebavan/))
 - Added Babylon.js Texture [tools](https://www.babylonjs.com/tools/ibl) to prefilter HDR files ([Sebavan](https://github.com/sebavan/))
 - Added editing of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
 - Added editing of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
-- Added Curve editor to view selected entity's animations in the Inspector ([pixelspace](https://github.com/devpixelspace))
+- Added Curve editor to create and view selected entity's animations in the Inspector ([pixelspace](https://github.com/devpixelspace))
 - Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 - Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 
 
 ## Updates
 ## Updates
@@ -31,6 +31,7 @@
 - Allow logging of shader code when a compilation error occurs ([Popov72](https://github.com/Popov72))
 - Allow logging of shader code when a compilation error occurs ([Popov72](https://github.com/Popov72))
 - Add back support for selecting textures based on engine capabilities ([bghgary](https://github.com/bghgary))
 - Add back support for selecting textures based on engine capabilities ([bghgary](https://github.com/bghgary))
 - Fix Draco decoder when running on IE11 ([bghgary](https://github.com/bghgary))
 - Fix Draco decoder when running on IE11 ([bghgary](https://github.com/bghgary))
+- Change default camera calculations to only include visible and enabled meshes ([bghgary](https://github.com/bghgary))
 
 
 ### NME
 ### NME
 
 
@@ -39,6 +40,7 @@
 - Can now edit Node port names ([belfortk](https://github.com/belfortk))
 - Can now edit Node port names ([belfortk](https://github.com/belfortk))
 - Updated which node ports are shown on frames by default so that only node ports connected to outside nodes are by default exposed on the frame ([belfortk](https://github.com/belfortk))
 - Updated which node ports are shown on frames by default so that only node ports connected to outside nodes are by default exposed on the frame ([belfortk](https://github.com/belfortk))
 - Added a modulo block ([ageneau](https://github.com/ageneau))
 - Added a modulo block ([ageneau](https://github.com/ageneau))
+- Fix bug where frame port labels would be the names of incorrect nodes ([belfortk](https://github.com/belfortk))
 
 
 ### Inspector
 ### Inspector
 
 
@@ -194,6 +196,7 @@
 - Fix display problem with transparent objects and SSAO2 pipeline (bug in the `GeometryBufferRenderer`) ([Popov72](https://github.com/Popov72))
 - Fix display problem with transparent objects and SSAO2 pipeline (bug in the `GeometryBufferRenderer`) ([Popov72](https://github.com/Popov72))
 - Fixed `Sound` not accepting a `TransformNode` as a source for spatial sound ([Poolminer](https://github.com/Poolminer))
 - Fixed `Sound` not accepting a `TransformNode` as a source for spatial sound ([Poolminer](https://github.com/Poolminer))
 - Fixed an issue with transformation set after physics body was created using cannon.js ([#7928](https://github.com/BabylonJS/Babylon.js/issues/7928)) ([RaananW](https://github.com/RaananW))
 - Fixed an issue with transformation set after physics body was created using cannon.js ([#7928](https://github.com/BabylonJS/Babylon.js/issues/7928)) ([RaananW](https://github.com/RaananW))
+- Fix bug when using `ShadowOnlyMaterial` with Cascaded Shadow Map and `autoCalcDepthBounds` is `true` ([Popov72](https://github.com/Popov72))
 
 
 ## Breaking changes
 ## Breaking changes
 
 

+ 238 - 38
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -3,12 +3,13 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { faTimes } from "@fortawesome/free-solid-svg-icons";
 import { faTimes } from "@fortawesome/free-solid-svg-icons";
 import { Animation } from 'babylonjs/Animations/animation';
 import { Animation } from 'babylonjs/Animations/animation';
 import { Vector2 } from 'babylonjs/Maths/math.vector';
 import { Vector2 } from 'babylonjs/Maths/math.vector';
-import { EasingFunction, BezierCurveEase } from 'babylonjs/Animations/easing';
+import { EasingFunction } from 'babylonjs/Animations/easing';
 import { IAnimationKey } from 'babylonjs/Animations/animationKey';
 import { IAnimationKey } from 'babylonjs/Animations/animationKey';
 import { IKeyframeSvgPoint } from './keyframeSvgPoint';
 import { IKeyframeSvgPoint } from './keyframeSvgPoint';
 import { SvgDraggableArea } from './svgDraggableArea';
 import { SvgDraggableArea } from './svgDraggableArea';
 import { Timeline } from './timeline';
 import { Timeline } from './timeline';
 import { Playhead } from './playhead';
 import { Playhead } from './playhead';
+import { GraphActionsBar } from './graphActionsBar';
 import { Scene } from "babylonjs/scene";
 import { Scene } from "babylonjs/scene";
 import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
 import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
@@ -29,7 +30,7 @@ interface ICanvasAxis {
     value: number;
     value: number;
 }
 }
 
 
-export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, animationTargetProperty: string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, svgKeyframes: IKeyframeSvgPoint[] | undefined, currentFrame: number, frameAxisLength: ICanvasAxis[] }> {
+export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, animationType: string, animationTargetProperty: string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, svgKeyframes: IKeyframeSvgPoint[] | undefined, currentFrame: number, currentValue: number, frameAxisLength: ICanvasAxis[], flatTangent: boolean }> {
 
 
     readonly _heightScale: number = 100;
     readonly _heightScale: number = 100;
     readonly _canvasLength: number = 20;
     readonly _canvasLength: number = 20;
@@ -42,7 +43,20 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     constructor(props: IAnimationCurveEditorComponentProps) {
     constructor(props: IAnimationCurveEditorComponentProps) {
         super(props);
         super(props);
         this._graphCanvas = React.createRef();
         this._graphCanvas = React.createRef();
-        this.state = { animations: this._newAnimations, selected: this.props.animations[0], isOpen: true, currentPathData: this.getPathData(this.props.animations[0]), svgKeyframes: this._svgKeyframes, animationTargetProperty: 'position.x', animationName: "", currentFrame: 0, frameAxisLength: (new Array(this._canvasLength)).fill(0).map((s, i) => { return { value: i * 10 } }) }
+        this.state = {
+            animations: this._newAnimations,
+            selected: this.props.animations[0],
+            isOpen: true,
+            currentPathData: this.getPathData(this.props.animations[0]),
+            svgKeyframes: this._svgKeyframes,
+            animationTargetProperty: 'position.x',
+            animationName: "",
+            animationType: "Float",
+            currentFrame: 0,
+            currentValue: 1,
+            flatTangent: false,
+            frameAxisLength: (new Array(this._canvasLength)).fill(0).map((s, i) => { return { value: i * 10 } })
+        }
     }
     }
 
 
     componentDidMount() {
     componentDidMount() {
@@ -56,6 +70,16 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         this.setState({ animationName: event.target.value });
         this.setState({ animationName: event.target.value });
     }
     }
 
 
+    handleValueChange(event: React.ChangeEvent<HTMLInputElement>) {
+        event.preventDefault();
+        this.setState({ currentValue: parseFloat(event.target.value) });
+    }
+
+    handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>) {
+        event.preventDefault();
+        this.setState({ animationType: event.target.value });
+    }
+
     handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>) {
     handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>) {
         event.preventDefault();
         event.preventDefault();
         this.setState({ animationTargetProperty: event.target.value });
         this.setState({ animationTargetProperty: event.target.value });
@@ -63,7 +87,8 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
 
     addAnimation() {
     addAnimation() {
         if (this.state.animationName != "" && this.state.animationTargetProperty != "") {
         if (this.state.animationName != "" && this.state.animationTargetProperty != "") {
-            let animation = new Animation(this.state.animationName, this.state.animationTargetProperty, 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
+
+            let animation = new Animation(this.state.animationName, this.state.animationTargetProperty, 30, this.getAnimationTypeofChange(this.state.animationType), Animation.ANIMATIONLOOPMODE_CYCLE);
 
 
             var keys = [];
             var keys = [];
             keys.push({
             keys.push({
@@ -76,59 +101,76 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                 value: 1
                 value: 1
             });
             });
 
 
-
             animation.setKeys(keys);
             animation.setKeys(keys);
 
 
-            var bezierEase = new BezierCurveEase(10, 0, 10, 0);
-            bezierEase.setEasingMode(EasingFunction.EASINGMODE_EASEINOUT);
-            animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
-
             // Need to redefine/refactor not to update the prop collection
             // Need to redefine/refactor not to update the prop collection
             (this.props.entity as IAnimatable).animations?.push(animation);
             (this.props.entity as IAnimatable).animations?.push(animation);
 
 
         }
         }
     }
     }
 
 
+    addKeyframeClick() {
+
+        let currentAnimation = this.state.selected;
+
+        if (currentAnimation.dataType === Animation.ANIMATIONTYPE_FLOAT) {
+            let keys = currentAnimation.getKeys();
+
+            let x = this.state.currentFrame
+            let y = this.state.currentValue;
+
+            let previousFrame = keys.find(kf => kf.frame <= x);
+
+            console.log(previousFrame);
+
+            keys.push({ frame: x, value: y });
+
+            keys.sort((a, b) => a.frame - b.frame);
+
+            currentAnimation.setKeys(keys);
+
+            this.selectAnimation(currentAnimation);
+        }
+    }
+
     addKeyFrame(event: React.MouseEvent<SVGSVGElement>) {
     addKeyFrame(event: React.MouseEvent<SVGSVGElement>) {
 
 
         event.preventDefault();
         event.preventDefault();
 
 
-        if (event.button === 2) {
+        var svg = event.target as SVGSVGElement;
 
 
-            var svg = event.target as SVGSVGElement;
+        var pt = svg.createSVGPoint();
 
 
-            var pt = svg.createSVGPoint();
+        pt.x = event.clientX;
+        pt.y = event.clientY;
 
 
-            pt.x = event.clientX;
-            pt.y = event.clientY;
+        var inverse = svg.getScreenCTM()?.inverse();
 
 
-            var inverse = svg.getScreenCTM()?.inverse();
+        var cursorpt = pt.matrixTransform(inverse);
 
 
-            var cursorpt = pt.matrixTransform(inverse);
+        var currentAnimation = this.state.selected;
 
 
-            var currentAnimation = this.state.selected;
+        var keys = currentAnimation.getKeys();
 
 
-            var keys = currentAnimation.getKeys();
+        var height = 100;
+        var middle = (height / 2);
 
 
-            var height = 100;
-            var middle = (height / 2);
+        var keyValue;
 
 
-            var keyValue;
+        if (cursorpt.y < middle) {
+            keyValue = 1 + ((100 / cursorpt.y) * .1)
+        }
 
 
-            if (cursorpt.y < middle) {
-                keyValue = 1 + ((100 / cursorpt.y) * .1)
-            }
+        if (cursorpt.y > middle) {
+            keyValue = 1 - ((100 / cursorpt.y) * .1)
+        }
 
 
-            if (cursorpt.y > middle) {
-                keyValue = 1 - ((100 / cursorpt.y) * .1)
-            }
+        keys.push({ frame: cursorpt.x, value: keyValue });
 
 
-            keys.push({ frame: cursorpt.x, value: keyValue });
+        currentAnimation.setKeys(keys);
 
 
-            currentAnimation.setKeys(keys);
+        this.selectAnimation(currentAnimation);
 
 
-            this.selectAnimation(currentAnimation);
-        }
     }
     }
 
 
     updateKeyframe(keyframe: Vector2, index: number) {
     updateKeyframe(keyframe: Vector2, index: number) {
@@ -155,10 +197,10 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                 keyValue = 1 - ((100 / k.keyframePoint.y) * .1)
                 keyValue = 1 - ((100 / k.keyframePoint.y) * .1)
             }
             }
 
 
-
             keys.push({ frame: k.keyframePoint.x, value: keyValue })
             keys.push({ frame: k.keyframePoint.x, value: keyValue })
             return k;
             return k;
         });
         });
+
         anim.setKeys(keys);
         anim.setKeys(keys);
 
 
         this.setState({ svgKeyframes: svgKeyframes })
         this.setState({ svgKeyframes: svgKeyframes })
@@ -183,7 +225,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
 
         const keyframes = animation.getKeys();
         const keyframes = animation.getKeys();
 
 
-
         if (keyframes === undefined) {
         if (keyframes === undefined) {
             return "";
             return "";
         }
         }
@@ -196,12 +237,22 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         // START OF LINE/CURVE
         // START OF LINE/CURVE
         let data: string | undefined = `M${startKey.frame}, ${this._heightScale - (startKey.value * middle)}`;
         let data: string | undefined = `M${startKey.frame}, ${this._heightScale - (startKey.value * middle)}`;
 
 
-        if (easingType === undefined && easingMode === undefined) {
-            data = this.linearInterpolation(keyframes, data, middle);
+        if (this.state && this.state.flatTangent) {
+            data = this.curvePathFlat(keyframes, data, middle, animation.dataType);
         } else {
         } else {
-            let easingFunction = animation.getEasingFunction();
 
 
-            data = this.curvePath(keyframes, data, middle, easingFunction as EasingFunction)
+            if (this.getAnimationData(animation).usesTangents) {
+                data = this.curvePathWithTangents(keyframes, data, middle, animation.dataType);
+            } else {
+                console.log("no tangents in this animation");
+                if (easingType === undefined && easingMode === undefined) {
+                    data = this.linearInterpolation(keyframes, data, middle);
+                } else {
+                    let easingFunction = animation.getEasingFunction();
+
+                    data = this.curvePath(keyframes, data, middle, easingFunction as EasingFunction)
+                }
+            }
         }
         }
 
 
         return data;
         return data;
@@ -223,6 +274,82 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         }
         }
     }
     }
 
 
+    curvePathFlat(keyframes: IAnimationKey[], data: string, middle: number, dataType: number) {
+
+        keyframes.forEach((key, i) => {
+
+            if (dataType === Animation.ANIMATIONTYPE_FLOAT) {
+
+                var pointA = new Vector2(0, 0);
+                if (i === 0) {
+                    pointA.set(key.frame, this._heightScale - (key.value * middle));
+                    this.setKeyframePoint([pointA], i, keyframes.length);
+                } else {
+                    pointA.set(keyframes[i - 1].frame, this._heightScale - (keyframes[i - 1].value * middle));
+
+                    let tangentA = new Vector2(pointA.x + 10, pointA.y);
+
+                    let pointB = new Vector2(key.frame, this._heightScale - (key.value * middle));
+
+                    let tangentB = new Vector2(pointB.x - 10, pointB.y);
+
+                    this.setKeyframePoint([pointA, tangentA, tangentB, pointB], i, keyframes.length);
+
+                    data += ` C${tangentA.x} ${tangentA.y} ${tangentB.x} ${tangentB.y} ${pointB.x} ${pointB.y} `
+
+                }
+            }
+        });
+
+        return data;
+
+    }
+
+    curvePathWithTangents(keyframes: IAnimationKey[], data: string, middle: number, type: number) {
+
+        switch (type) {
+            case Animation.ANIMATIONTYPE_FLOAT:
+                // value float
+                break;
+            case Animation.ANIMATIONTYPE_VECTOR3:
+                // value float
+                break;
+        }
+
+
+        keyframes.forEach((key, i) => {
+
+            var inTangent = key.inTangent;
+            var outTangent = key.outTangent;
+
+            let svgKeyframe;
+
+            if (i === 0) {
+
+                svgKeyframe = { keyframePoint: new Vector2(key.frame, this._heightScale - (key.value * middle)), rightControlPoint: outTangent, leftControlPoint: null, id: i.toString() }
+
+                data += ` C${svgKeyframe.keyframePoint.x} ${svgKeyframe.keyframePoint.y} ${outTangent.x} ${outTangent.y}`
+
+            } else {
+
+                svgKeyframe = { keyframePoint: new Vector2(keyframes[i - 1].frame, this._heightScale - (keyframes[i - 1].value * middle)), rightControlPoint: outTangent, leftControlPoint: inTangent, id: i.toString() }
+
+                if (outTangent) {
+                    data += `${inTangent.x} ${inTangent.y} C${svgKeyframe.keyframePoint.x} ${svgKeyframe.keyframePoint.y} ${outTangent.x} ${outTangent.y} `
+                } else {
+                    data += `${inTangent.x} ${inTangent.y} C${svgKeyframe.keyframePoint.x} ${svgKeyframe.keyframePoint.y} `
+                }
+
+            }
+
+            this._svgKeyframes.push(svgKeyframe);
+
+        });
+
+        return data;
+
+    }
+
     curvePath(keyframes: IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction) {
     curvePath(keyframes: IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction) {
 
 
         // This will get 1/4 and 3/4 of points in eased curve
         // This will get 1/4 and 3/4 of points in eased curve
@@ -293,6 +420,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) {
     renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) {
 
 
         let animation = this.state.selected as Animation;
         let animation = this.state.selected as Animation;
+        // Bug: After play/stop we get an extra keyframe at 0
 
 
         let keys = [...animation.getKeys()];
         let keys = [...animation.getKeys()];
 
 
@@ -372,6 +500,60 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
 
     }
     }
 
 
+    getAnimationData(animation: Animation) {
+
+        // General Props
+        let loopMode = animation.loopMode;
+        let name = animation.name;
+        let blendingSpeed = animation.blendingSpeed;
+        let targetProperty = animation.targetProperty;
+        let targetPropertyPath = animation.targetPropertyPath;
+        let framesPerSecond = animation.framePerSecond;
+        let highestFrame = animation.getHighestFrame();
+
+        // Should we use this for export?
+        let serialized = animation.serialize();
+
+        let usesTangents = animation.getKeys().find(kf => kf.inTangent);
+
+        return { loopMode, name, blendingSpeed, targetPropertyPath, targetProperty, framesPerSecond, highestFrame, serialized, usesTangents }
+
+    }
+
+    getAnimationTypeofChange(selected: string) {
+        let dataType;
+        switch (selected) {
+            // Float
+            case "Float":
+                dataType = Animation.ANIMATIONTYPE_FLOAT;
+                break;
+            // Quaternion
+            case "Quaternion":
+                dataType = Animation.ANIMATIONTYPE_QUATERNION;
+                break;
+            // Vector3
+            case "Vector3":
+                dataType = Animation.ANIMATIONTYPE_VECTOR3;
+            // Vector2
+            case "Vector2":
+                dataType = Animation.ANIMATIONTYPE_VECTOR2;
+            // Size
+            case "Size":
+                dataType = Animation.ANIMATIONTYPE_SIZE;
+            // Color3
+            case "Color3":
+                dataType = Animation.ANIMATIONTYPE_COLOR3;
+            // Color4
+            case "Color4":
+                dataType = Animation.ANIMATIONTYPE_COLOR4;
+            default:
+                dataType = 0;
+                break;
+        }
+        return dataType;
+
+    }
+
     interpolateControlPoints(p0: Vector2, p1: Vector2, u: number, p2: Vector2, v: number, p3: Vector2): Vector2[] | undefined {
     interpolateControlPoints(p0: Vector2, p1: Vector2, u: number, p2: Vector2, v: number, p3: Vector2): Vector2[] | undefined {
 
 
         let a = 0.0;
         let a = 0.0;
@@ -417,6 +599,11 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         this.setState({ currentFrame: frame });
         this.setState({ currentFrame: frame });
     }
     }
 
 
+    setFlatTangent() {
+        this.setState({ flatTangent: !this.state.flatTangent }, () => this.selectAnimation(this.state.selected));
+        ;
+    }
+
     render() {
     render() {
         return (
         return (
             <div id="animation-curve-editor">
             <div id="animation-curve-editor">
@@ -426,6 +613,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                         <FontAwesomeIcon icon={faTimes} />
                         <FontAwesomeIcon icon={faTimes} />
                     </div>
                     </div>
                 </div>
                 </div>
+                <GraphActionsBar currentValue={this.state.currentValue} handleValueChange={(e) => this.handleValueChange(e)} addKeyframe={() => this.addKeyframeClick()} flatTangent={() => this.setFlatTangent()} />
                 <div className="content">
                 <div className="content">
 
 
                     <div className="row">
                     <div className="row">
@@ -437,6 +625,19 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                                     <input type="text" value={this.state.animationName} onChange={(e) => this.handleNameChange(e)}></input>
                                     <input type="text" value={this.state.animationName} onChange={(e) => this.handleNameChange(e)}></input>
                                 </div>
                                 </div>
                                 <div className="label-input">
                                 <div className="label-input">
+                                    <label>Type</label>
+                                    <select onChange={(e) => this.handleTypeChange(e)} value={this.state.animationType}>
+                                        <option value="Float">Float</option>
+                                        {/* Uncomment this when we use other types */}
+                                        {/* <option value="Vector3">Vector3</option>
+                                        <option value="Vector2">Vector2</option>
+                                        <option value="Quaternion">Quaternion</option>
+                                        <option value="Color3">Color3</option>
+                                        <option value="Color4">Color4</option>
+                                        <option value="Size">Size</option> */}
+                                    </select>
+                                </div>
+                                <div className="label-input">
                                     <label>Target Property</label>
                                     <label>Target Property</label>
                                     <input type="text" value={this.state.animationTargetProperty} onChange={(e) => this.handlePropertyChange(e)}></input>
                                     <input type="text" value={this.state.animationTargetProperty} onChange={(e) => this.handlePropertyChange(e)}></input>
                                 </div>
                                 </div>
@@ -497,7 +698,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                                     <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
                                     <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
                                         <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
                                         <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
                                     </svg>
                                     </svg>
-
                                 )}
                                 )}
 
 
                             </SvgDraggableArea>
                             </SvgDraggableArea>

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

@@ -199,7 +199,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                                 this._isCurveEditorOpen && <PopupComponent
                                 this._isCurveEditorOpen && <PopupComponent
                                     id="curve-editor"
                                     id="curve-editor"
                                     title="Curve Animation Editor"
                                     title="Curve Animation Editor"
-                                    size={{ width: 950, height: 512 }}
+                                    size={{ width: 950, height: 540 }}
                                     onOpen={(window: Window) => { window.console.log("Window opened!!") }}
                                     onOpen={(window: Window) => { window.console.log("Window opened!!") }}
                                     onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
                                     onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
 
 

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

@@ -22,6 +22,26 @@
         }   
         }   
     }
     }
 
 
+    .actions-wrapper {
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-end;
+        align-items: flex-end;
+        padding: 6px;
+
+        .action-input{
+            display: flex;
+            justify-content: center;
+            flex-direction: row;
+            align-items: center;
+            label { margin-right: 0.5em }
+            input {
+                width: 4em;
+                height: 2em;
+            }
+        }
+    }
+
     .content{
     .content{
         display: flex;
         display: flex;
         align-items: flex-start;
         align-items: flex-start;
@@ -34,7 +54,7 @@
             justify-content: flex-start;
             justify-content: flex-start;
             flex-direction: row;
             flex-direction: row;
             width: 100vw;
             width: 100vw;
-            height: 85vh;
+            height: 78.5vh;
 
 
             .timeline{
             .timeline{
                 width: 100vw;
                 width: 100vw;

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

@@ -0,0 +1,29 @@
+
+import * as React from "react";
+import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
+
+interface IGraphActionsBarProps {
+   addKeyframe: () => void;
+   handleValueChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+   flatTangent: () => void;
+   currentValue: number;
+}
+
+export class GraphActionsBar extends React.Component<IGraphActionsBarProps>{ 
+    constructor(props: IGraphActionsBarProps) {
+        super(props);
+    }
+     
+    render() { 
+       return (
+           <div className="actions-wrapper">
+               <div className="action-input">
+               <label>Value</label>
+               <input type="number" value={this.props.currentValue} onChange={this.props.handleValueChange}/>
+               </div>
+              <ButtonLineComponent label={"Add Keyframe"} onClick={this.props.addKeyframe} />
+              <ButtonLineComponent label={"Flat Tangent"} onClick={this.props.flatTangent} />
+           </div>
+        )
+    }
+} 

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx

@@ -316,7 +316,7 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
                     }} />
                     }} />
                 </LineContainerComponent>
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="FILE">
                 <LineContainerComponent globalState={this.props.globalState} title="FILE">
-                <FileButtonLineComponent label="Load" onClick={(file) => this.loadFromFile(file)} accept=".json" />
+                    <FileButtonLineComponent label="Load" onClick={(file) => this.loadFromFile(file)} accept=".json" />
                     <ButtonLineComponent label="Save" onClick={() => this.saveToFile()} />
                     <ButtonLineComponent label="Save" onClick={() => this.saveToFile()} />
                 </LineContainerComponent>
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="SNIPPET">
                 <LineContainerComponent globalState={this.props.globalState} title="SNIPPET">

+ 143 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spriteManagerPropertyGridComponent.tsx

@@ -14,6 +14,10 @@ import { FloatLineComponent } from '../../../lines/floatLineComponent';
 import { SliderLineComponent } from '../../../lines/sliderLineComponent';
 import { SliderLineComponent } from '../../../lines/sliderLineComponent';
 import { RenderingManager } from 'babylonjs/Rendering/renderingManager';
 import { RenderingManager } from 'babylonjs/Rendering/renderingManager';
 import { TextureLinkLineComponent } from '../../../lines/textureLinkLineComponent';
 import { TextureLinkLineComponent } from '../../../lines/textureLinkLineComponent';
+import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
+import { Sprite } from 'babylonjs/Sprites/sprite';
+import { Tools } from 'babylonjs/Misc/tools';
+import { FileButtonLineComponent } from '../../../lines/fileButtonLineComponent';
 
 
 interface ISpriteManagerPropertyGridComponentProps {
 interface ISpriteManagerPropertyGridComponentProps {
     globalState: GlobalState;
     globalState: GlobalState;
@@ -24,10 +28,131 @@ interface ISpriteManagerPropertyGridComponentProps {
 }
 }
 
 
 export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
 export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+    private _snippetUrl = "https://snippet.babylonjs.com";
+
+
     constructor(props: ISpriteManagerPropertyGridComponentProps) {
     constructor(props: ISpriteManagerPropertyGridComponentProps) {
         super(props);
         super(props);
     }
     }
 
 
+    addNewSprite() {
+        const spriteManager = this.props.spriteManager;
+        var newSprite = new Sprite("new sprite", spriteManager);
+
+        this.props.onSelectionChangedObservable?.notifyObservers(newSprite);
+
+        this.props.globalState.onCodeChangedObservable.notifyObservers({
+            object: spriteManager,
+            code: `new BABYLON.Sprite("new sprite", TARGET);`
+        });
+    }
+
+    disposeManager() {
+        const spriteManager = this.props.spriteManager;
+        spriteManager.dispose();
+
+        this.props.globalState.onCodeChangedObservable.notifyObservers({
+            object: spriteManager,
+            code: `TARGET.dispose();`
+        });
+
+        this.props.onSelectionChangedObservable?.notifyObservers(null);
+    }
+
+    saveToFile() {        
+        const spriteManager = this.props.spriteManager;
+        let content = JSON.stringify(spriteManager.serialize(true));
+
+        Tools.Download(new Blob([content]), "spriteManager.json");
+    }
+
+    loadFromFile(file: File) {
+        const spriteManager = this.props.spriteManager;
+        const scene = spriteManager.scene;
+
+        Tools.ReadFile(file, (data) => {
+            let decoder = new TextDecoder("utf-8");
+            let jsonObject = JSON.parse(decoder.decode(data));
+            
+            spriteManager.dispose();            
+            this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
+
+            let newManager = SpriteManager.Parse(jsonObject, scene, "");
+            this.props.globalState.onSelectionChangedObservable.notifyObservers(newManager);
+        }, undefined, true);
+    }
+
+    loadFromSnippet() {
+        const spriteManager = this.props.spriteManager;
+        const scene = spriteManager.scene;
+
+        let snippedID = window.prompt("Please enter the snippet ID to use");
+
+        if (!snippedID) {
+            return;
+        }
+        
+        spriteManager.dispose();            
+        this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
+
+        SpriteManager.CreateFromSnippetAsync(snippedID, scene).then((newManager) => {
+            this.props.globalState.onSelectionChangedObservable.notifyObservers(newManager);
+        }).catch(err => {
+            alert("Unable to load your sprite manager: " + err);
+        });
+    }
+
+    saveToSnippet() {
+        const spriteManager = this.props.spriteManager;
+        let content = JSON.stringify(spriteManager.serialize(true));
+
+        var xmlHttp = new XMLHttpRequest();
+        xmlHttp.onreadystatechange = () => {
+            if (xmlHttp.readyState == 4) {
+                if (xmlHttp.status == 200) {
+                    var snippet = JSON.parse(xmlHttp.responseText);
+                    const oldId = spriteManager.snippetId ;
+                    spriteManager.snippetId = snippet.id;
+                    if (snippet.version && snippet.version != "0") {
+                        spriteManager.snippetId += "#" + snippet.version;
+                    }
+                    this.forceUpdate();
+                    if (navigator.clipboard) {
+                        navigator.clipboard.writeText(spriteManager.snippetId);
+                    }
+
+                    let windowAsAny = window as any;
+
+                    if (windowAsAny.Playground && oldId) {
+                        windowAsAny.Playground.onRequestCodeChangeObservable.notifyObservers({
+                            regex: new RegExp(oldId, "g"),
+                            replace: spriteManager.snippetId
+                        });
+                    }
+
+                    alert("Sprite manager saved with ID: " + spriteManager.snippetId + " (please note that the id was also saved to your clipboard)");
+                }
+                else {
+                    alert("Unable to save your sprite manager");
+                }
+            }
+        }
+
+        xmlHttp.open("POST", this._snippetUrl + (spriteManager.snippetId ? "/" + spriteManager.snippetId : ""), true);
+        xmlHttp.setRequestHeader("Content-Type", "application/json");
+
+        var dataToSend = {
+            payload : JSON.stringify({
+                particleSystem: content
+            }),
+            name: "",
+            description: "",
+            tags: ""
+        };
+
+        xmlHttp.send(JSON.stringify(dataToSend));
+    }
+
     render() {
     render() {
         const spriteManager = this.props.spriteManager;
         const spriteManager = this.props.spriteManager;
 
 
@@ -36,9 +161,26 @@ export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteM
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={spriteManager} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
                     <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={spriteManager} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
                     <TextLineComponent label="Unique ID" value={spriteManager.uniqueId.toString()} />
                     <TextLineComponent label="Unique ID" value={spriteManager.uniqueId.toString()} />
-                    <FloatLineComponent label="Capacity" isInteger={true} target={spriteManager} propertyName="capacity" />
+                    <TextLineComponent label="Capacity" value={spriteManager.capacity.toString()} />
                     <TextureLinkLineComponent label="Texture" texture={spriteManager.texture} onSelectionChangedObservable={this.props.onSelectionChangedObservable}/>
                     <TextureLinkLineComponent label="Texture" texture={spriteManager.texture} onSelectionChangedObservable={this.props.onSelectionChangedObservable}/>
+                    {
+                        spriteManager.sprites.length < spriteManager.capacity &&
+                        <ButtonLineComponent label="Add new sprite" onClick={() => this.addNewSprite()} />
+                    }
+                    <ButtonLineComponent label="Dispose" onClick={() => this.disposeManager()} />
                 </LineContainerComponent>
                 </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="FILE">
+                    <FileButtonLineComponent label="Load" onClick={(file) => this.loadFromFile(file)} accept=".json" />
+                    <ButtonLineComponent label="Save" onClick={() => this.saveToFile()} />
+                </LineContainerComponent>                
+                <LineContainerComponent globalState={this.props.globalState} title="SNIPPET">
+                    {
+                        spriteManager.snippetId &&
+                        <TextLineComponent label="Snippet ID" value={spriteManager.snippetId} />
+                    }
+                    <ButtonLineComponent label="Load from snippet server" onClick={() => this.loadFromSnippet()} />
+                    <ButtonLineComponent label="Save to snippet server" onClick={() => this.saveToSnippet()} />
+                </LineContainerComponent>  
                 <LineContainerComponent globalState={this.props.globalState} title="PROPERTIES">
                 <LineContainerComponent globalState={this.props.globalState} title="PROPERTIES">
                     <CheckBoxLineComponent label="Pickable" target={spriteManager} propertyName="isPickable" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Pickable" target={spriteManager} propertyName="isPickable" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Fog enabled" target={spriteManager} propertyName="fogEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Fog enabled" target={spriteManager} propertyName="fogEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 13 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spritePropertyGridComponent.tsx

@@ -51,6 +51,18 @@ export class SpritePropertyGridComponent extends React.Component<ISpriteProperty
         this.forceUpdate();
         this.forceUpdate();
     }
     }
 
 
+    disposeSprite() {
+        const sprite = this.props.sprite;
+        sprite.dispose();
+
+        this.props.globalState.onCodeChangedObservable.notifyObservers({
+            object: sprite,
+            code: `TARGET.dispose();`
+        });
+
+        this.props.onSelectionChangedObservable?.notifyObservers(null);
+    }
+
     render() {
     render() {
         const sprite = this.props.sprite;
         const sprite = this.props.sprite;
         const manager = sprite.manager;
         const manager = sprite.manager;
@@ -70,6 +82,7 @@ export class SpritePropertyGridComponent extends React.Component<ISpriteProperty
                     <TextLineComponent label="Unique ID" value={sprite.uniqueId.toString()} />
                     <TextLineComponent label="Unique ID" value={sprite.uniqueId.toString()} />
                     <TextLineComponent label="Link to manager" value={manager.name} onLink={() => this.onManagerLink()} />
                     <TextLineComponent label="Link to manager" value={manager.name} onLink={() => this.onManagerLink()} />
                     <CheckBoxLineComponent label="Visible" target={sprite} propertyName="isVisible" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Visible" target={sprite} propertyName="isVisible" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <ButtonLineComponent label="Dispose" onClick={() => this.disposeSprite()} />
                 </LineContainerComponent>
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="PROPERTIES">
                 <LineContainerComponent globalState={this.props.globalState} title="PROPERTIES">
                     <Vector3LineComponent label="Position" target={sprite} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Position" target={sprite} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 1 - 1
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -361,7 +361,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
         spriteManagersContextMenus.push({
         spriteManagersContextMenus.push({
             label: "Add new sprite manager",
             label: "Add new sprite manager",
             action: () => {
             action: () => {
-                let newSpriteManager = new SpriteManager("Default sprite manager", "//playground.babylonjs.com/textures/player.png", 2, 64, scene);
+                let newSpriteManager = new SpriteManager("Default sprite manager", "//playground.babylonjs.com/textures/player.png", 500, 64, scene);
                 this.props.globalState.onSelectionChangedObservable.notifyObservers(newSpriteManager);
                 this.props.globalState.onSelectionChangedObservable.notifyObservers(newSpriteManager);
             }
             }
         });            
         });            

+ 57 - 51
loaders/src/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.ts

@@ -7,6 +7,7 @@ import { Nullable } from "babylonjs/types";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { INode } from "../glTFLoaderInterfaces";
 import { INode } from "../glTFLoaderInterfaces";
+import { AbstractMesh } from 'babylonjs/Meshes/abstractMesh';
 
 
 const NAME = "EXT_mesh_gpu_instancing";
 const NAME = "EXT_mesh_gpu_instancing";
 
 
@@ -47,61 +48,66 @@ export class EXT_mesh_gpu_instancing implements IGLTFLoaderExtension {
     /** @hidden */
     /** @hidden */
     public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
     public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
         return GLTFLoader.LoadExtensionAsync<IEXTMeshGpuInstancing, TransformNode>(context, node, this.name, (extensionContext, extension) => {
         return GLTFLoader.LoadExtensionAsync<IEXTMeshGpuInstancing, TransformNode>(context, node, this.name, (extensionContext, extension) => {
-            return this._loader.loadNodeAsync(`#/nodes/${node.index}`, node, (babylonTransformNode) => {
-                const promises = new Array<Promise<any>>();
-                let instanceCount: Nullable<number> = null;
-                const loadAttribute = (attribute: string, assignBufferFunc: (data: Float32Array) => void) => {
-                    if (extension.attributes[attribute] == undefined) {
-                        return;
-                    }
-                    const accessor = ArrayItem.Get(`${extensionContext}/attributes/${attribute}`, this._loader.gltf.accessors, extension.attributes[attribute]);
-                    if (instanceCount === null) {
-                        instanceCount = accessor.count;
-                    } else if (instanceCount !== accessor.count) {
-                        throw new Error(`${extensionContext}/attributes: Instance buffer accessors do not have the same count.`);
-                    }
-                    promises.push(this._loader._loadFloatAccessorAsync(`/accessors/${accessor.bufferView}`, accessor).then((data) => {
-                        assignBufferFunc(data);
-                    }));
-                };
-                let translationBuffer: Nullable<Float32Array> = null;
-                let rotationBuffer: Nullable<Float32Array> = null;
-                let scaleBuffer: Nullable<Float32Array> = null;
-
-                loadAttribute("TRANSLATION", (data) => { translationBuffer = data; });
-                loadAttribute("ROTATION", (data) => { rotationBuffer = data; });
-                loadAttribute("SCALE", (data) => { scaleBuffer = data; });
-
-                return Promise.all(promises).then(() => {
-                    if (instanceCount) {
-                        let instanceName: string = "";
-                        let instance: Nullable<TransformNode> = null;
-                        const digitLength = instanceCount.toString().length;
+            const promise = this._loader.loadNodeAsync(`#/nodes/${node.index}`, node, assign);
+
+            if (!node._primitiveBabylonMeshes) {
+                return promise;
+            }
+
+            // Hide the source meshes.
+            for (const babylonMesh of node._primitiveBabylonMeshes) {
+                babylonMesh.isVisible = false;
+            }
+
+            const promises = new Array<Promise<Nullable<Float32Array>>>();
+            let instanceCount = 0;
+
+            const loadAttribute = (attribute: string) => {
+                if (extension.attributes[attribute] == undefined) {
+                    promises.push(Promise.resolve(null));
+                    return;
+                }
 
 
+                const accessor = ArrayItem.Get(`${extensionContext}/attributes/${attribute}`, this._loader.gltf.accessors, extension.attributes[attribute]);
+                promises.push(this._loader._loadFloatAccessorAsync(`/accessors/${accessor.bufferView}`, accessor));
+
+                if (instanceCount === 0) {
+                    instanceCount = accessor.count;
+                } else if (instanceCount !== accessor.count) {
+                    throw new Error(`${extensionContext}/attributes: Instance buffer accessors do not have the same count.`);
+                }
+            };
+
+            loadAttribute("TRANSLATION");
+            loadAttribute("ROTATION");
+            loadAttribute("SCALE");
+
+            if (instanceCount == 0) {
+                return promise;
+            }
+
+            const digitLength = instanceCount.toString().length;
+            for (let i = 0; i < instanceCount; ++i) {
+                for (const babylonMesh of node._primitiveBabylonMeshes!) {
+                    const instanceName = `${babylonMesh.name || babylonMesh.id}_${StringTools.PadNumber(i, digitLength)}`;
+                    const babylonInstancedMesh = (babylonMesh as (InstancedMesh | Mesh)).createInstance(instanceName);
+                    babylonInstancedMesh.setParent(babylonMesh);
+                }
+            }
+
+            return promise.then((babylonTransformNode) => {
+                return Promise.all(promises).then(([translationBuffer, rotationBuffer, scaleBuffer]) => {
+                    for (const babylonMesh of node._primitiveBabylonMeshes!) {
+                        const babylonInstancedMeshes = babylonMesh.getChildMeshes(true, (node) => (node as AbstractMesh).isAnInstance);
                         for (let i = 0; i < instanceCount; ++i) {
                         for (let i = 0; i < instanceCount; ++i) {
-                            if (node._primitiveBabylonMeshes) {
-                                for (let j = 0; j < node._primitiveBabylonMeshes.length; ++j) {
-                                    const babylonMeshPrimitive = node._primitiveBabylonMeshes[j];
-                                    instanceName = (babylonMeshPrimitive.name || babylonMeshPrimitive.id) + "_" + StringTools.PadNumber(i, digitLength);
-                                    if (babylonMeshPrimitive.isAnInstance) {
-                                        instance = (babylonMeshPrimitive as InstancedMesh).sourceMesh.createInstance(instanceName);
-                                    } else if ((babylonMeshPrimitive as Mesh).createInstance) {
-                                        instance = (babylonMeshPrimitive as Mesh).createInstance(instanceName);
-                                    }
-                                    if (instance) {
-                                        instance.setParent(babylonMeshPrimitive);
-                                        translationBuffer ? Vector3.FromArrayToRef(translationBuffer, i * 3, instance.position)
-                                            : instance.position.set(0, 0, 0);
-                                        rotationBuffer ? Quaternion.FromArrayToRef(rotationBuffer, i * 4, instance.rotationQuaternion!)
-                                            : instance.rotationQuaternion!.set(0, 0, 0, 1);
-                                        scaleBuffer ? Vector3.FromArrayToRef(scaleBuffer, i * 3, instance.scaling)
-                                            : instance.scaling.set(1, 1, 1);
-                                    }
-                                }
-                            }
+                            const babylonInstancedMesh = babylonInstancedMeshes[i];
+                            translationBuffer && Vector3.FromArrayToRef(translationBuffer, i * 3, babylonInstancedMesh.position);
+                            rotationBuffer && Quaternion.FromArrayToRef(rotationBuffer, i * 4, babylonInstancedMesh.rotationQuaternion!);
+                            scaleBuffer && Vector3.FromArrayToRef(scaleBuffer, i * 3, babylonInstancedMesh.scaling);
+                            babylonInstancedMesh.refreshBoundingInfo();
                         }
                         }
                     }
                     }
-                    assign(babylonTransformNode);
+
                     return babylonTransformNode;
                     return babylonTransformNode;
                 });
                 });
             });
             });

+ 34 - 2
materialsLibrary/src/shadowOnly/shadowOnlyMaterial.ts

@@ -18,6 +18,7 @@ import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import "./shadowOnly.fragment";
 import "./shadowOnly.fragment";
 import "./shadowOnly.vertex";
 import "./shadowOnly.vertex";
 import { EffectFallbacks } from 'babylonjs/Materials/effectFallbacks';
 import { EffectFallbacks } from 'babylonjs/Materials/effectFallbacks';
+import { CascadedShadowGenerator } from 'babylonjs/Lights/Shadows/cascadedShadowGenerator';
 
 
 class ShadowOnlyMaterialDefines extends MaterialDefines {
 class ShadowOnlyMaterialDefines extends MaterialDefines {
     public CLIPPLANE = false;
     public CLIPPLANE = false;
@@ -41,6 +42,7 @@ class ShadowOnlyMaterialDefines extends MaterialDefines {
 
 
 export class ShadowOnlyMaterial extends PushMaterial {
 export class ShadowOnlyMaterial extends PushMaterial {
     private _activeLight: IShadowLight;
     private _activeLight: IShadowLight;
+    private _needAlphaBlending = true;
 
 
     constructor(name: string, scene: Scene) {
     constructor(name: string, scene: Scene) {
         super(name, scene);
         super(name, scene);
@@ -49,7 +51,7 @@ export class ShadowOnlyMaterial extends PushMaterial {
     public shadowColor = Color3.Black();
     public shadowColor = Color3.Black();
 
 
     public needAlphaBlending(): boolean {
     public needAlphaBlending(): boolean {
-        return true;
+        return this._needAlphaBlending;
     }
     }
 
 
     public needAlphaTesting(): boolean {
     public needAlphaTesting(): boolean {
@@ -68,6 +70,15 @@ export class ShadowOnlyMaterial extends PushMaterial {
         this._activeLight = light;
         this._activeLight = light;
     }
     }
 
 
+    private _getFirstShadowLightForMesh(mesh: AbstractMesh): Nullable<IShadowLight> {
+        for (var light of mesh.lightSources) {
+            if (light.shadowEnabled) {
+                return light as IShadowLight;
+            }
+        }
+        return null;
+    }
+
     // Methods
     // Methods
     public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {
     public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {
         if (this.isFrozen) {
         if (this.isFrozen) {
@@ -114,6 +125,16 @@ export class ShadowOnlyMaterial extends PushMaterial {
 
 
         defines._needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, false, 1);
         defines._needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, false, 1);
 
 
+        const shadowGenerator = this._getFirstShadowLightForMesh(mesh)?.getShadowGenerator();
+
+        this._needAlphaBlending = true;
+
+        if (shadowGenerator && (shadowGenerator as any).getClassName && (shadowGenerator as any).getClassName() === 'CascadedShadowGenerator') {
+            const csg = shadowGenerator as CascadedShadowGenerator;
+
+            this._needAlphaBlending = !csg.autoCalcDepthBounds;
+        }
+
         // Attribs
         // Attribs
         MaterialHelper.PrepareDefinesForAttributes(mesh, defines, false, true);
         MaterialHelper.PrepareDefinesForAttributes(mesh, defines, false, true);
 
 
@@ -226,10 +247,21 @@ export class ShadowOnlyMaterial extends PushMaterial {
         // Lights
         // Lights
         if (scene.lightsEnabled) {
         if (scene.lightsEnabled) {
             MaterialHelper.BindLights(scene, mesh, this._activeEffect, defines, 1);
             MaterialHelper.BindLights(scene, mesh, this._activeEffect, defines, 1);
+
+            const light = this._getFirstShadowLightForMesh(mesh);
+
+            if (light) {
+                // Make sure the uniforms for this light will be rebound for other materials using this light when rendering the current frame.
+                // Indeed, there is an optimization in Light that binds the light uniforms only once per frame for a given light (if using ubo).
+                // Doing this way assumes that all uses of this light are the same, meaning all parameters passed to Light._bindLlight
+                // are the same, notably useSpecular. However, isReadyForSubMesh (see above) is passing false for this parameter, which may not be
+                // the value the other materials may pass.
+                light._renderId = -1;
+            }
         }
         }
 
 
         // View
         // View
-        if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+        if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE || defines["SHADOWCSM0"]) {
             this._activeEffect.setMatrix("view", scene.getViewMatrix());
             this._activeEffect.setMatrix("view", scene.getViewMatrix());
         }
         }
 
 

+ 1 - 1
nodeEditor/src/diagram/graphFrame.ts

@@ -139,7 +139,7 @@ export class GraphFrame {
 
 
                             if (!portAdded) {
                             if (!portAdded) {
                                 portAdded = true;
                                 portAdded = true;
-                                localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, link.nodeB!, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
+                                localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, link.nodeA!, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
                                 this._frameOutPorts.push(localPort);
                                 this._frameOutPorts.push(localPort);
 
 
                                 link.isVisible = true;
                                 link.isVisible = true;

+ 1 - 1
package.json

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

+ 4 - 1
sandbox/index.js

@@ -165,8 +165,11 @@ if (BABYLON.Engine.isSupported()) {
                 framingBehavior.elevationReturnTime = -1;
                 framingBehavior.elevationReturnTime = -1;
 
 
                 if (currentScene.meshes.length) {
                 if (currentScene.meshes.length) {
-                    var worldExtends = currentScene.getWorldExtends();
                     currentScene.activeCamera.lowerRadiusLimit = null;
                     currentScene.activeCamera.lowerRadiusLimit = null;
+
+                    var worldExtends = currentScene.getWorldExtends(function (mesh) {
+                        return mesh.isVisible && mesh.isEnabled();
+                    });
                     framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max);
                     framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max);
                 }
                 }
             }
             }

+ 1 - 1
src/DeviceInput/InputDevices/deviceSourceManager.ts

@@ -108,7 +108,7 @@ export class DeviceSourceManager implements IDisposable {
      * @param deviceSlot "Slot" or index that device is referenced in
      * @param deviceSlot "Slot" or index that device is referenced in
      * @returns DeviceSource object
      * @returns DeviceSource object
      */
      */
-    public getDeviceSource<T extends DeviceType>(deviceType: DeviceType, deviceSlot?: number): Nullable<DeviceSource<T>> {
+    public getDeviceSource<T extends DeviceType>(deviceType: T, deviceSlot?: number): Nullable<DeviceSource<T>> {
         if (deviceSlot === undefined) {
         if (deviceSlot === undefined) {
             if (this._firstDevice[deviceType] === undefined) {
             if (this._firstDevice[deviceType] === undefined) {
                 return null;
                 return null;

+ 2 - 1
src/Engines/Processors/index.ts

@@ -5,4 +5,5 @@ export * from "./shaderCodeCursor";
 export * from "./shaderCodeNode";
 export * from "./shaderCodeNode";
 export * from "./shaderCodeTestNode";
 export * from "./shaderCodeTestNode";
 export * from "./shaderProcessingOptions";
 export * from "./shaderProcessingOptions";
-export * from "./shaderProcessor";
+export * from "./shaderProcessor";
+export * from "./shaderCodeInliner";

+ 408 - 0
src/Engines/Processors/shaderCodeInliner.ts

@@ -0,0 +1,408 @@
+interface IInlineFunctionDescr {
+    name: string;
+    type: string;
+    parameters: string[];
+    body: string;
+    callIndex: number;
+}
+
+/** @hidden */
+export class ShaderCodeInliner {
+
+    static readonly InlineToken = "#define inline";
+    static readonly RegexpFindFunctionNameAndType = /(?<=\s+?(\w+)\s+(\w+)\s*?)$/;
+
+    private _sourceCode: string;
+    private _functionDescr: IInlineFunctionDescr[];
+    private _numMaxIterations: number;
+
+    public debug: boolean = false;
+
+    public get code(): string {
+        return this._sourceCode;
+    }
+
+    constructor(sourceCode: string, numMaxIterations = 20) {
+        this._sourceCode = sourceCode;
+        this._numMaxIterations = numMaxIterations;
+        this._functionDescr = [];
+    }
+
+    public processCode() {
+        if (this.debug) {
+            console.log(`Start inlining process (code size=${this._sourceCode.length})...`);
+        }
+        this._collectFunctions();
+        this._processInlining(this._numMaxIterations);
+        if (this.debug) {
+            console.log("End of inlining process.");
+        }
+    }
+
+    private _collectFunctions() {
+        let startIndex = 0;
+
+        while (startIndex < this._sourceCode.length) {
+            // locate the function to inline and extract its name
+            const inlineTokenIndex = this._sourceCode.indexOf(ShaderCodeInliner.InlineToken, startIndex);
+            if (inlineTokenIndex < 0) {
+                break;
+            }
+
+            const funcParamsStartIndex = this._sourceCode.indexOf("(", inlineTokenIndex + ShaderCodeInliner.InlineToken.length);
+            if (funcParamsStartIndex < 0) {
+                if (this.debug) {
+                    console.warn(`Could not find the opening parenthesis after the token. startIndex=${startIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+
+            const funcNameMatch = ShaderCodeInliner.RegexpFindFunctionNameAndType.exec(this._sourceCode.substring(inlineTokenIndex + ShaderCodeInliner.InlineToken.length, funcParamsStartIndex));
+            if (!funcNameMatch) {
+                if (this.debug) {
+                    console.warn(`Could not extract the name/type of the function from: ${this._sourceCode.substring(inlineTokenIndex + ShaderCodeInliner.InlineToken.length, funcParamsStartIndex)}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+            const [funcType, funcName] = [funcNameMatch[1], funcNameMatch[2]];
+
+            // extract the parameters of the function as a whole string (without the leading / trailing parenthesis)
+            const funcParamsEndIndex = this._extractBetweenMarkers('(', ')', this._sourceCode, funcParamsStartIndex);
+            if (funcParamsEndIndex < 0) {
+                if (this.debug) {
+                    console.warn(`Could not extract the parameters the function '${funcName}' (type=${funcType}). funcParamsStartIndex=${funcParamsStartIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+            const funcParams = this._sourceCode.substring(funcParamsStartIndex + 1, funcParamsEndIndex);
+
+            // extract the body of the function (with the curly brackets)
+            const funcBodyStartIndex = this._skipWhitespaces(this._sourceCode, funcParamsEndIndex + 1);
+            if (funcBodyStartIndex === this._sourceCode.length) {
+                if (this.debug) {
+                    console.warn(`Could not extract the body of the function '${funcName}' (type=${funcType}). funcParamsEndIndex=${funcParamsEndIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+
+            const funcBodyEndIndex = this._extractBetweenMarkers('{', '}', this._sourceCode, funcBodyStartIndex);
+            if (funcBodyEndIndex < 0) {
+                if (this.debug) {
+                    console.warn(`Could not extract the body of the function '${funcName}' (type=${funcType}). funcBodyStartIndex=${funcBodyStartIndex}`);
+                }
+                startIndex = inlineTokenIndex + ShaderCodeInliner.InlineToken.length;
+                continue;
+            }
+            const funcBody = this._sourceCode.substring(funcBodyStartIndex, funcBodyEndIndex + 1);
+
+            // process the parameters: extract each names
+            const params = this._removeComments(funcParams).split(",");
+            const paramNames = [];
+
+            for (let p = 0; p < params.length; ++p) {
+                const param = params[p].trim();
+                const idx = param.lastIndexOf(" ");
+
+                if (idx >= 0) {
+                    paramNames.push(param.substring(idx + 1));
+                }
+            }
+
+            if (funcType !== 'void') {
+                // for functions that return a value, we will replace "return" by "tempvarname = ", tempvarname being a unique generated name
+                paramNames.push('return');
+            }
+
+            // collect the function
+            this._functionDescr.push({
+                "name": funcName,
+                "type": funcType,
+                "parameters": paramNames,
+                "body": funcBody,
+                "callIndex": 0,
+            });
+
+            startIndex = funcBodyEndIndex + 1;
+
+            // remove the function from the source code
+            const partBefore = inlineTokenIndex > 0 ? this._sourceCode.substring(0, inlineTokenIndex) : "";
+            const partAfter = funcBodyEndIndex + 1 < this._sourceCode.length - 1 ? this._sourceCode.substring(funcBodyEndIndex + 1) : "";
+
+            this._sourceCode = partBefore + partAfter;
+
+            startIndex -= funcBodyEndIndex + 1 - inlineTokenIndex;
+        }
+
+        if (this.debug) {
+            console.log(`Collect functions: ${this._functionDescr.length} functions found. functionDescr=`, this._functionDescr);
+        }
+    }
+
+    private _processInlining(numMaxIterations: number = 20): boolean {
+        while (numMaxIterations-- >= 0) {
+            if (!this._replaceFunctionCallsByCode()) {
+                break;
+            }
+        }
+
+        if (this.debug) {
+            console.log(`numMaxIterations is ${numMaxIterations} after inlining process`);
+        }
+
+        return numMaxIterations >= 0;
+    }
+
+    private _extractBetweenMarkers(markerOpen: string, markerClose: string, block: string, startIndex: number): number {
+        let currPos = startIndex,
+            openMarkers = 0,
+            waitForChar = '';
+
+        while (currPos < block.length) {
+            let currChar = block.charAt(currPos);
+
+            if (!waitForChar) {
+                switch (currChar) {
+                    case markerOpen:
+                        openMarkers++;
+                        break;
+                    case markerClose:
+                        openMarkers--;
+                        break;
+                    case '"':
+                    case "'":
+                    case "`":
+                        waitForChar = currChar;
+                        break;
+                    case '/':
+                        if (currPos + 1 < block.length) {
+                            const nextChar = block.charAt(currPos + 1);
+                            if (nextChar === '/') {
+                                waitForChar = '\n';
+                            } else if (nextChar === '*') {
+                                waitForChar = '*/';
+                            }
+                        }
+                        break;
+                }
+            } else {
+                if (currChar === waitForChar) {
+                    if (waitForChar === '"' || waitForChar === "'") {
+                        block.charAt(currPos - 1) !== '\\' && (waitForChar = '');
+                    } else {
+                        waitForChar = '';
+                    }
+                } else if (waitForChar === '*/' && currChar === '*' && currPos + 1 < block.length) {
+                    block.charAt(currPos + 1) === '/' && (waitForChar = '');
+                    if (waitForChar === '') {
+                        currPos++;
+                    }
+                }
+            }
+
+            currPos++ ;
+            if (openMarkers === 0) {
+                break;
+            }
+        }
+
+        return openMarkers === 0 ? currPos - 1 : -1;
+    }
+
+    private _skipWhitespaces(s: string, index: number): number {
+        while (index < s.length) {
+            const c = s[index];
+            if (c !== ' ' && c !== '\n' && c !== '\r' && c !== '\t' && c !== '\u000a' && c !== '\u00a0') {
+                break;
+            }
+            index++;
+        }
+
+        return index;
+    }
+
+    private _removeComments(block: string): string {
+        let currPos = 0,
+            waitForChar = '',
+            inComments = false,
+            s = [];
+
+        while (currPos < block.length) {
+            let currChar = block.charAt(currPos);
+
+            if (!waitForChar) {
+                switch (currChar) {
+                    case '"':
+                    case "'":
+                    case "`":
+                        waitForChar = currChar;
+                        break;
+                    case '/':
+                        if (currPos + 1 < block.length) {
+                            const nextChar = block.charAt(currPos + 1);
+                            if (nextChar === '/') {
+                                waitForChar = '\n';
+                                inComments = true;
+                            } else if (nextChar === '*') {
+                                waitForChar = '*/';
+                                inComments = true;
+                            }
+                        }
+                        break;
+                }
+                if (!inComments) {
+                    s.push(currChar);
+                }
+            } else {
+                if (currChar === waitForChar) {
+                    if (waitForChar === '"' || waitForChar === "'") {
+                        block.charAt(currPos - 1) !== '\\' && (waitForChar = '');
+                        s.push(currChar);
+                    } else {
+                        waitForChar = '';
+                        inComments = false;
+                    }
+                } else if (waitForChar === '*/' && currChar === '*' && currPos + 1 < block.length) {
+                    block.charAt(currPos + 1) === '/' && (waitForChar = '');
+                    if (waitForChar === '') {
+                        inComments = false;
+                        currPos++;
+                    }
+                } else {
+                    if (!inComments) {
+                        s.push(currChar);
+                    }
+                }
+            }
+
+            currPos++ ;
+        }
+
+        return s.join('');
+    }
+
+    private _replaceFunctionCallsByCode(): boolean {
+        let doAgain = false;
+
+        for (const func of this._functionDescr) {
+            const { name, type, parameters, body } = func;
+
+            let startIndex = 0;
+
+            while (startIndex < this._sourceCode.length) {
+                // Look for the function name in the source code
+                const functionCallIndex = this._sourceCode.indexOf(name, startIndex);
+
+                if (functionCallIndex < 0) {
+                    break;
+                }
+
+                // Find the opening parenthesis
+                const callParamsStartIndex = this._skipWhitespaces(this._sourceCode, functionCallIndex + name.length);
+                if (callParamsStartIndex === this._sourceCode.length || this._sourceCode.charAt(callParamsStartIndex) !== '(') {
+                    startIndex = functionCallIndex + name.length;
+                    continue;
+                }
+
+                // extract the parameters of the function call as a whole string (without the leading / trailing parenthesis)
+                const callParamsEndIndex = this._extractBetweenMarkers('(', ')', this._sourceCode, callParamsStartIndex);
+                if (callParamsEndIndex < 0) {
+                    if (this.debug) {
+                        console.warn(`Could not extract the parameters of the function call. Function '${name}' (type=${type}). callParamsStartIndex=${callParamsStartIndex}`);
+                    }
+                    startIndex = functionCallIndex + name.length;
+                    continue;
+                }
+                const callParams = this._sourceCode.substring(callParamsStartIndex + 1, callParamsEndIndex);
+
+                // process the parameter call: extract each names
+                const params = this._removeComments(callParams).split(",");
+                const paramNames = [];
+
+                for (let p = 0; p < params.length; ++p) {
+                    const param = params[p].trim();
+                    paramNames.push(param);
+                }
+
+                const retParamName = type !== 'void' ? name + '_' + (func.callIndex++) : null;
+
+                if (retParamName) {
+                    paramNames.push(retParamName + ' =');
+                }
+
+                if (paramNames.length !== parameters.length) {
+                    if (this.debug) {
+                        console.warn(`Invalid function call: not the same number of parameters for the call than the number expected by the function. Function '${name}' (type=${type}). function parameters=${parameters}, call parameters=${paramNames}`);
+                    }
+                    startIndex = functionCallIndex + name.length;
+                    continue;
+                }
+
+                startIndex = callParamsEndIndex + 1;
+
+                // replace the function call by the body function
+                const funcBody = this._replaceNames(body, parameters, paramNames);
+
+                let partBefore = functionCallIndex > 0 ? this._sourceCode.substring(0, functionCallIndex) : "";
+                let partAfter = callParamsEndIndex + 1 < this._sourceCode.length - 1 ? this._sourceCode.substring(callParamsEndIndex + 1) : "";
+
+                if (retParamName) {
+                    // case where the function returns a value. We generate:
+                    // FUNCTYPE retParamName;
+                    // {function body}
+                    // and replace the function call by retParamName
+                    const injectDeclarationIndex = this._findBackward(this._sourceCode, functionCallIndex - 1, '\n');
+
+                    partBefore = this._sourceCode.substring(0, injectDeclarationIndex + 1);
+                    let partBetween = this._sourceCode.substring(injectDeclarationIndex + 1, functionCallIndex);
+
+                    this._sourceCode = partBefore + type + " " + retParamName + ";\n" + funcBody + "\n" + partBetween + retParamName + partAfter;
+
+                    if (this.debug) {
+                        console.log(`Replace function call by code. Function '${name}' (type=${type}). injectDeclarationIndex=${injectDeclarationIndex}`);
+                    }
+                } else {
+                    // simple case where the return value of the function is "void"
+                    this._sourceCode = partBefore + funcBody + partAfter;
+
+                    startIndex += funcBody.length - (callParamsEndIndex + 1 - functionCallIndex);
+
+                    if (this.debug) {
+                        console.log(`Replace function call by code. Function '${name}' (type=${type}). functionCallIndex=${functionCallIndex}`);
+                    }
+                }
+
+                doAgain = true;
+            }
+        }
+
+        return doAgain;
+    }
+
+    private _findBackward(s: string, index: number, c: string): number {
+        while (index >= 0 && s.charAt(index) !== c) {
+            index--;
+        }
+
+        return index;
+    }
+
+    private _escapeRegExp(s: string): string {
+        return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+    }
+
+    private _replaceNames(code: string, sources: string[], destinations: string[]): string {
+
+        for (let i = 0; i < sources.length; ++i) {
+            const source = new RegExp(this._escapeRegExp(sources[i]), 'g'),
+                  destination = destinations[i];
+
+            code = code.replace(source, destination);
+        }
+
+        return code;
+    }
+}

+ 29 - 10
src/Engines/nativeEngine.ts

@@ -33,13 +33,15 @@ interface INativeEngine {
     deleteVertexArray(vertexArray: any): void;
     deleteVertexArray(vertexArray: any): void;
     bindVertexArray(vertexArray: any): void;
     bindVertexArray(vertexArray: any): void;
 
 
-    createIndexBuffer(data: ArrayBufferView): any;
+    createIndexBuffer(data: ArrayBufferView, dynamic: boolean): any;
     deleteIndexBuffer(buffer: any): void;
     deleteIndexBuffer(buffer: any): void;
     recordIndexBuffer(vertexArray: any, buffer: any): void;
     recordIndexBuffer(vertexArray: any, buffer: any): void;
+    updateDynamicIndexBuffer(buffer: any, data: ArrayBufferView, startingIndex: number): void;
 
 
-    createVertexBuffer(data: ArrayBufferView): any;
+    createVertexBuffer(data: ArrayBufferView, dynamic: boolean): any;
     deleteVertexBuffer(buffer: any): void;
     deleteVertexBuffer(buffer: any): void;
     recordVertexBuffer(vertexArray: any, buffer: any, location: number, byteOffset: number, byteStride: number, numElements: number, type: number, normalized: boolean): void;
     recordVertexBuffer(vertexArray: any, buffer: any, location: number, byteOffset: number, byteStride: number, numElements: number, type: number, normalized: boolean): void;
+    updateDynamicVertexBuffer(buffer: any, data: ArrayBufferView, byteOffset: number, byteLength: number): void;
 
 
     createProgram(vertexShader: string, fragmentShader: string): any;
     createProgram(vertexShader: string, fragmentShader: string): any;
     getUniforms(shaderProgram: any, uniformsNames: string[]): WebGLUniformLocation[];
     getUniforms(shaderProgram: any, uniformsNames: string[]): WebGLUniformLocation[];
@@ -334,22 +336,22 @@ export class NativeEngine extends Engine {
         this._native.clear(mode);
         this._native.clear(mode);
     }
     }
 
 
-    public createIndexBuffer(indices: IndicesArray): NativeDataBuffer {
+    public createIndexBuffer(indices: IndicesArray, updateable?: boolean): NativeDataBuffer {
         const data = this._normalizeIndexData(indices);
         const data = this._normalizeIndexData(indices);
         const buffer = new NativeDataBuffer();
         const buffer = new NativeDataBuffer();
         buffer.references = 1;
         buffer.references = 1;
         buffer.is32Bits = (data.BYTES_PER_ELEMENT === 4);
         buffer.is32Bits = (data.BYTES_PER_ELEMENT === 4);
-        buffer.nativeIndexBuffer = this._native.createIndexBuffer(data);
+        buffer.nativeIndexBuffer = this._native.createIndexBuffer(data, updateable ?? false);
         if (buffer.nativeVertexBuffer === this.INVALID_HANDLE) {
         if (buffer.nativeVertexBuffer === this.INVALID_HANDLE) {
             throw new Error("Could not create a native index buffer.");
             throw new Error("Could not create a native index buffer.");
         }
         }
         return buffer;
         return buffer;
     }
     }
 
 
-    public createVertexBuffer(data: DataArray): NativeDataBuffer {
+    public createVertexBuffer(data: DataArray, updateable?: boolean): NativeDataBuffer {
         const buffer = new NativeDataBuffer();
         const buffer = new NativeDataBuffer();
         buffer.references = 1;
         buffer.references = 1;
-        buffer.nativeVertexBuffer = this._native.createVertexBuffer(ArrayBuffer.isView(data) ? data : new Float32Array(data));
+        buffer.nativeVertexBuffer = this._native.createVertexBuffer(ArrayBuffer.isView(data) ? data : new Float32Array(data), updateable ?? false);
         if (buffer.nativeVertexBuffer === this.INVALID_HANDLE) {
         if (buffer.nativeVertexBuffer === this.INVALID_HANDLE) {
             throw new Error("Could not create a native vertex buffer.");
             throw new Error("Could not create a native vertex buffer.");
         }
         }
@@ -372,7 +374,15 @@ export class NativeEngine extends Engine {
                 if (vertexBuffer) {
                 if (vertexBuffer) {
                     const buffer = vertexBuffer.getBuffer() as Nullable<NativeDataBuffer>;
                     const buffer = vertexBuffer.getBuffer() as Nullable<NativeDataBuffer>;
                     if (buffer) {
                     if (buffer) {
-                        this._native.recordVertexBuffer(vertexArray, buffer.nativeVertexBuffer, location, vertexBuffer.byteOffset, vertexBuffer.byteStride, vertexBuffer.getSize(), vertexBuffer.type, vertexBuffer.normalized);
+                        this._native.recordVertexBuffer(
+                            vertexArray,
+                            buffer.nativeVertexBuffer,
+                            location,
+                            vertexBuffer.byteOffset,
+                            vertexBuffer.byteStride,
+                            vertexBuffer.getSize(),
+                            vertexBuffer.type,
+                            vertexBuffer.normalized);
                     }
                     }
                 }
                 }
             }
             }
@@ -1280,11 +1290,14 @@ export class NativeEngine extends Engine {
     }
     }
 
 
     public createDynamicVertexBuffer(data: DataArray): DataBuffer {
     public createDynamicVertexBuffer(data: DataArray): DataBuffer {
-        throw new Error("createDynamicVertexBuffer not yet implemented.");
+        return this.createVertexBuffer(data, true);
     }
     }
 
 
     public updateDynamicIndexBuffer(indexBuffer: DataBuffer, indices: IndicesArray, offset: number = 0): void {
     public updateDynamicIndexBuffer(indexBuffer: DataBuffer, indices: IndicesArray, offset: number = 0): void {
-        throw new Error("updateDynamicIndexBuffer not yet implemented.");
+        const buffer = indexBuffer as NativeDataBuffer;
+        const data = this._normalizeIndexData(indices);
+        buffer.is32Bits = (data.BYTES_PER_ELEMENT === 4);
+        this._native.updateDynamicIndexBuffer(buffer.nativeIndexBuffer, data, offset);
     }
     }
 
 
     /**
     /**
@@ -1295,7 +1308,13 @@ export class NativeEngine extends Engine {
      * @param byteLength the byte length of the data (optional)
      * @param byteLength the byte length of the data (optional)
      */
      */
     public updateDynamicVertexBuffer(vertexBuffer: DataBuffer, data: DataArray, byteOffset?: number, byteLength?: number): void {
     public updateDynamicVertexBuffer(vertexBuffer: DataBuffer, data: DataArray, byteOffset?: number, byteLength?: number): void {
-        throw new Error("updateDynamicVertexBuffer not yet implemented.");
+        const buffer = vertexBuffer as NativeDataBuffer;
+        const dataView = ArrayBuffer.isView(data) ? data : new Float32Array(data);
+        this._native.updateDynamicVertexBuffer(
+            buffer.nativeVertexBuffer,
+            dataView,
+            byteOffset ?? 0,
+            byteLength ?? dataView.byteLength);
     }
     }
 
 
     // TODO: Refactor to share more logic with base Engine implementation.
     // TODO: Refactor to share more logic with base Engine implementation.

+ 2 - 2
src/Engines/thinEngine.ts

@@ -132,14 +132,14 @@ export class ThinEngine {
      */
      */
     // Not mixed with Version for tooling purpose.
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
     public static get NpmPackage(): string {
-        return "babylonjs@4.2.0-alpha.15";
+        return "babylonjs@4.2.0-alpha.16";
     }
     }
 
 
     /**
     /**
      * Returns the current version of the framework
      * Returns the current version of the framework
      */
      */
     public static get Version(): string {
     public static get Version(): string {
-        return "4.2.0-alpha.15";
+        return "4.2.0-alpha.16";
     }
     }
 
 
     /**
     /**

+ 1 - 1
src/Helpers/sceneHelpers.ts

@@ -115,7 +115,7 @@ Scene.prototype.createDefaultCamera = function(createArcRotateCamera = false, re
 
 
     // Camera
     // Camera
     if (!this.activeCamera) {
     if (!this.activeCamera) {
-        var worldExtends = this.getWorldExtends();
+        var worldExtends = this.getWorldExtends((mesh) => mesh.isVisible && mesh.isEnabled());
         var worldSize = worldExtends.max.subtract(worldExtends.min);
         var worldSize = worldExtends.max.subtract(worldExtends.min);
         var worldCenter = worldExtends.min.add(worldSize.scale(0.5));
         var worldCenter = worldExtends.min.add(worldSize.scale(0.5));
 
 

+ 2 - 2
src/Inputs/scene.inputManager.ts

@@ -794,7 +794,7 @@ export class InputManager {
         });
         });
 
 
         // Pointer events
         // Pointer events
-        var eventPrefix = Tools.GetPointerPrefix();
+        var eventPrefix = Tools.GetPointerPrefix(engine);
 
 
         if (attachMove) {
         if (attachMove) {
             elementToAttachTo.addEventListener(eventPrefix + "move", <any>this._onPointerMove, false);
             elementToAttachTo.addEventListener(eventPrefix + "move", <any>this._onPointerMove, false);
@@ -823,9 +823,9 @@ export class InputManager {
      * Detaches all event handlers
      * Detaches all event handlers
      */
      */
     public detachControl() {
     public detachControl() {
-        const eventPrefix = Tools.GetPointerPrefix();
         const canvas = this._scene.getEngine().getInputElement();
         const canvas = this._scene.getEngine().getInputElement();
         const engine = this._scene.getEngine();
         const engine = this._scene.getEngine();
+        const eventPrefix = Tools.GetPointerPrefix(engine);
 
 
         if (!canvas) {
         if (!canvas) {
             return;
             return;

+ 37 - 12
src/Materials/PBR/pbrBaseMaterial.ts

@@ -640,6 +640,30 @@ export abstract class PBRBaseMaterial extends PushMaterial {
      */
      */
     protected _forceIrradianceInFragment = false;
     protected _forceIrradianceInFragment = false;
 
 
+    private _realTimeFiltering: boolean = false;
+    /**
+     * Enables realtime filtering on the texture.
+     */
+    public get realTimeFiltering() {
+        return this._realTimeFiltering;
+    }
+    public set realTimeFiltering(b: boolean) {
+        this._realTimeFiltering = b;
+        this.markAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
+
+    private _realTimeFilteringQuality: number = Constants.TEXTURE_FILTERING_QUALITY_LOW;
+    /**
+     * Quality switch for realtime filtering
+     */
+    public get realTimeFilteringQuality() : number {
+        return this._realTimeFilteringQuality;
+    }
+    public set realTimeFilteringQuality(n: number) {
+        this._realTimeFilteringQuality = n;
+        this.markAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
+
     /**
     /**
      * Force normal to face away from face.
      * Force normal to face away from face.
      */
      */
@@ -1184,7 +1208,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
 
         var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vMetallicReflectanceFactors", "vEmissiveColor", "visibility", "vReflectionColor",
         var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vMetallicReflectanceFactors", "vEmissiveColor", "visibility", "vReflectionColor",
             "vFogInfos", "vFogColor", "pointSize",
             "vFogInfos", "vFogColor", "pointSize",
-            "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos", "vMetallicReflectanceInfos",
+            "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos", "vReflectionFilteringInfo", "vMetallicReflectanceInfos",
             "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos",
             "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos",
             "mBones",
             "mBones",
             "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "metallicReflectanceMatrix",
             "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "metallicReflectanceMatrix",
@@ -1198,8 +1222,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             "vSphericalL2_2", "vSphericalL2_1", "vSphericalL20", "vSphericalL21", "vSphericalL22",
             "vSphericalL2_2", "vSphericalL2_1", "vSphericalL20", "vSphericalL21", "vSphericalL22",
             "vReflectionMicrosurfaceInfos",
             "vReflectionMicrosurfaceInfos",
             "vTangentSpaceParams", "boneTextureWidth",
             "vTangentSpaceParams", "boneTextureWidth",
-            "vDebugMode",
-            "vFilteringInfo", "linearRoughness"
+            "vDebugMode"
         ];
         ];
 
 
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
@@ -1302,8 +1325,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
                     defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
                     defines.LINEARSPECULARREFLECTION = reflectionTexture.linearSpecularLOD;
                     defines.LINEARSPECULARREFLECTION = reflectionTexture.linearSpecularLOD;
 
 
-                    if (reflectionTexture.realTimeFiltering && reflectionTexture.realTimeFilteringQuality > 0) {
-                        defines.NUM_SAMPLES = reflectionTexture.realTimeFilteringQuality + "u";
+                    if (this.realTimeFiltering && this.realTimeFilteringQuality > 0) {
+                        defines.NUM_SAMPLES = "" + this.realTimeFilteringQuality;
+                        if (engine.webGLVersion > 1) {
+                            defines.NUM_SAMPLES = defines.NUM_SAMPLES + "u";
+                        }
+
                         defines.REALTIME_FILTERING = true;
                         defines.REALTIME_FILTERING = true;
                     } else {
                     } else {
                         defines.REALTIME_FILTERING = false;
                         defines.REALTIME_FILTERING = false;
@@ -1367,7 +1394,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         else if (reflectionTexture.isCube) {
                         else if (reflectionTexture.isCube) {
                             defines.USESPHERICALFROMREFLECTIONMAP = true;
                             defines.USESPHERICALFROMREFLECTIONMAP = true;
                             defines.USEIRRADIANCEMAP = false;
                             defines.USEIRRADIANCEMAP = false;
-                            if (this._forceIrradianceInFragment || reflectionTexture.realTimeFiltering || scene.getEngine().getCaps().maxVaryingVectors <= 8) {
+                            if (this._forceIrradianceInFragment || this.realTimeFiltering || scene.getEngine().getCaps().maxVaryingVectors <= 8) {
                                 defines.USESPHERICALINVERTEX = false;
                                 defines.USESPHERICALINVERTEX = false;
                             }
                             }
                             else {
                             else {
@@ -1588,6 +1615,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         ubo.addUniform("vReflectivityInfos", 3);
         ubo.addUniform("vReflectivityInfos", 3);
         ubo.addUniform("vMicroSurfaceSamplerInfos", 2);
         ubo.addUniform("vMicroSurfaceSamplerInfos", 2);
         ubo.addUniform("vReflectionInfos", 2);
         ubo.addUniform("vReflectionInfos", 2);
+        ubo.addUniform("vReflectionFilteringInfo", 2);
         ubo.addUniform("vReflectionPosition", 3);
         ubo.addUniform("vReflectionPosition", 3);
         ubo.addUniform("vReflectionSize", 3);
         ubo.addUniform("vReflectionSize", 3);
         ubo.addUniform("vBumpInfos", 3);
         ubo.addUniform("vBumpInfos", 3);
@@ -1723,12 +1751,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize);
                             ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize);
                         }
                         }
 
 
-                        if (reflectionTexture.realTimeFiltering) {
+                        if (this.realTimeFiltering) {
                             const width = reflectionTexture.getSize().width;
                             const width = reflectionTexture.getSize().width;
-                            const alpha = this._roughness! * this._roughness!;
-
-                            this._activeEffect.setFloat2("vFilteringInfo", width, Scalar.Log2(width));
-                            this._activeEffect.setFloat("linearRoughness", alpha);
+                            ubo.updateFloat2("vReflectionFilteringInfo", width, Scalar.Log2(width));
                         }
                         }
 
 
                         if (!defines.USEIRRADIANCEMAP) {
                         if (!defines.USEIRRADIANCEMAP) {
@@ -1926,7 +1951,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 }
                 }
             }
             }
 
 
-            this.subSurface.bindForSubMesh(ubo, scene, engine, this.isFrozen, defines.LODBASEDMICROSFURACE);
+            this.subSurface.bindForSubMesh(ubo, scene, engine, this.isFrozen, defines.LODBASEDMICROSFURACE, this.realTimeFiltering);
             this.clearCoat.bindForSubMesh(ubo, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
             this.clearCoat.bindForSubMesh(ubo, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
             this.anisotropy.bindForSubMesh(ubo, scene, this.isFrozen);
             this.anisotropy.bindForSubMesh(ubo, scene, this.isFrozen);
             this.sheen.bindForSubMesh(ubo, scene, this.isFrozen);
             this.sheen.bindForSubMesh(ubo, scene, this.isFrozen);

+ 15 - 5
src/Materials/PBR/pbrSubSurfaceConfiguration.ts

@@ -10,6 +10,7 @@ import { MaterialFlags } from "../materialFlags";
 import { UniformBuffer } from "../../Materials/uniformBuffer";
 import { UniformBuffer } from "../../Materials/uniformBuffer";
 import { MaterialHelper } from "../../Materials/materialHelper";
 import { MaterialHelper } from "../../Materials/materialHelper";
 import { EffectFallbacks } from '../effectFallbacks';
 import { EffectFallbacks } from '../effectFallbacks';
+import { Scalar } from "../../Maths/math.scalar";
 
 
 declare type Engine = import("../../Engines/engine").Engine;
 declare type Engine = import("../../Engines/engine").Engine;
 declare type Scene = import("../../scene").Scene;
 declare type Scene = import("../../scene").Scene;
@@ -290,10 +291,11 @@ export class PBRSubSurfaceConfiguration {
      * @param uniformBuffer defines the Uniform buffer to fill in.
      * @param uniformBuffer defines the Uniform buffer to fill in.
      * @param scene defines the scene the material belongs to.
      * @param scene defines the scene the material belongs to.
      * @param engine defines the engine the material belongs to.
      * @param engine defines the engine the material belongs to.
-     * @param isFrozen defines wether the material is frozen or not.
-     * @param lodBasedMicrosurface defines wether the material relies on lod based microsurface or not.
+     * @param isFrozen defines whether the material is frozen or not.
+     * @param lodBasedMicrosurface defines whether the material relies on lod based microsurface or not.
+     * @param realTimeFiltering defines whether the textures should be filtered on the fly.
      */
      */
-    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean): void {
+    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean, realTimeFiltering: boolean): void {
         var refractionTexture = this._getRefractionTexture(scene);
         var refractionTexture = this._getRefractionTexture(scene);
 
 
         if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
         if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
@@ -313,11 +315,18 @@ export class PBRSubSurfaceConfiguration {
                         depth = (<any>refractionTexture).depth;
                         depth = (<any>refractionTexture).depth;
                     }
                     }
                 }
                 }
+
+                var width = refractionTexture.getSize().width;
+
                 uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, 1 / this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
                 uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, 1 / this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
                 uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos",
                 uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos",
-                    refractionTexture.getSize().width,
+                    width,
                     refractionTexture.lodGenerationScale,
                     refractionTexture.lodGenerationScale,
                     refractionTexture.lodGenerationOffset);
                     refractionTexture.lodGenerationOffset);
+
+                if (realTimeFiltering) {
+                    uniformBuffer.updateFloat2("vRefractionFilteringInfo", width, Scalar.Log2(width));
+                }
             }
             }
 
 
             uniformBuffer.updateColor3("vDiffusionDistance", this.diffusionDistance);
             uniformBuffer.updateColor3("vDiffusionDistance", this.diffusionDistance);
@@ -503,7 +512,7 @@ export class PBRSubSurfaceConfiguration {
     public static AddUniforms(uniforms: string[]): void {
     public static AddUniforms(uniforms: string[]): void {
         uniforms.push(
         uniforms.push(
             "vDiffusionDistance", "vTintColor", "vSubSurfaceIntensity",
             "vDiffusionDistance", "vTintColor", "vSubSurfaceIntensity",
-            "vRefractionMicrosurfaceInfos",
+            "vRefractionMicrosurfaceInfos", "vRefractionFilteringInfo",
             "vRefractionInfos", "vThicknessInfos", "vThicknessParam",
             "vRefractionInfos", "vThicknessInfos", "vThicknessParam",
             "refractionMatrix", "thicknessMatrix");
             "refractionMatrix", "thicknessMatrix");
     }
     }
@@ -523,6 +532,7 @@ export class PBRSubSurfaceConfiguration {
      */
      */
     public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
     public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
         uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
         uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
+        uniformBuffer.addUniform("vRefractionFilteringInfo", 2);
         uniformBuffer.addUniform("vRefractionInfos", 4);
         uniformBuffer.addUniform("vRefractionInfos", 4);
         uniformBuffer.addUniform("refractionMatrix", 16);
         uniformBuffer.addUniform("refractionMatrix", 16);
         uniformBuffer.addUniform("vThicknessInfos", 2);
         uniformBuffer.addUniform("vThicknessInfos", 2);

+ 8 - 2
src/Materials/Textures/Filtering/hdrFiltering.ts

@@ -10,6 +10,7 @@ import { Nullable } from '../../../types';
 
 
 import "../../../Shaders/hdrFiltering.vertex";
 import "../../../Shaders/hdrFiltering.vertex";
 import "../../../Shaders/hdrFiltering.fragment";
 import "../../../Shaders/hdrFiltering.fragment";
+import { Logger } from '../../../Misc/logger';
 
 
 /**
 /**
  * Options for texture filtering
  * Options for texture filtering
@@ -133,7 +134,7 @@ export class HDRFiltering {
                     alpha = 0;
                     alpha = 0;
                 }
                 }
 
 
-                effect.setFloat("linearRoughness", alpha);
+                effect.setFloat("alphaG", alpha);
 
 
                 this._effectRenderer.draw();
                 this._effectRenderer.draw();
             }
             }
@@ -164,7 +165,7 @@ export class HDRFiltering {
             vertexShader: "hdrFiltering",
             vertexShader: "hdrFiltering",
             fragmentShader: "hdrFiltering",
             fragmentShader: "hdrFiltering",
             samplerNames: ["inputTexture"],
             samplerNames: ["inputTexture"],
-            uniformNames: ["vSampleDirections", "vWeights", "up", "right", "front", "vFilteringInfo", "hdrScale", "linearRoughness"],
+            uniformNames: ["vSampleDirections", "vWeights", "up", "right", "front", "vFilteringInfo", "hdrScale", "alphaG"],
             useShaderStore: true,
             useShaderStore: true,
             defines,
             defines,
             onCompiled: onCompiled
             onCompiled: onCompiled
@@ -192,6 +193,11 @@ export class HDRFiltering {
       * @return Promise called when prefiltering is done
       * @return Promise called when prefiltering is done
       */
       */
     public prefilter(texture: BaseTexture, onFinished: Nullable<() => void> = null) {
     public prefilter(texture: BaseTexture, onFinished: Nullable<() => void> = null) {
+        if (this._engine.webGLVersion === 1) {
+            Logger.Warn("HDR prefiltering is not available in WebGL 1., you can use real time filtering instead.");
+            return;
+        }
+
         return new Promise((resolve) => {
         return new Promise((resolve) => {
             this._effectRenderer = new EffectRenderer(this._engine);
             this._effectRenderer = new EffectRenderer(this._engine);
             this._effectWrapper = this._createEffect(texture);
             this._effectWrapper = this._createEffect(texture);

+ 0 - 30
src/Materials/Textures/baseTexture.ts

@@ -345,36 +345,6 @@ export class BaseTexture implements IAnimatable {
         return "BaseTexture";
         return "BaseTexture";
     }
     }
 
 
-    private _realTimeFiltering: boolean = false;
-    /**
-     * Enables realtime filtering on the texture.
-     */
-    public get realTimeFiltering() {
-        return this._realTimeFiltering;
-    }
-    public set realTimeFiltering(b: boolean) {
-        this._realTimeFiltering = b;
-        let scene = this.getScene();
-        if (scene) {
-            scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
-        }
-    }
-
-    private _realTimeFilteringQuality: number = Constants.TEXTURE_FILTERING_QUALITY_LOW;
-    /**
-     * Quality switch for realtime filtering
-     */
-    public get realTimeFilteringQuality() : number {
-        return this._realTimeFilteringQuality;
-    }
-    public set realTimeFilteringQuality(n: number) {
-        this._realTimeFilteringQuality = n;
-        let scene = this.getScene();
-        if (scene) {
-            scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
-        }
-    }
-
     /**
     /**
      * Define the list of animation attached to the texture.
      * Define the list of animation attached to the texture.
      */
      */

+ 10 - 0
src/Meshes/instancedMesh.ts

@@ -160,6 +160,16 @@ export class InstancedMesh extends AbstractMesh {
     }
     }
 
 
     /**
     /**
+     * Creates a new InstancedMesh object from the mesh model.
+     * @see http://doc.babylonjs.com/how_to/how_to_use_instances
+     * @param name defines the name of the new instance
+     * @returns a new InstancedMesh
+     */
+    public createInstance(name: string): InstancedMesh {
+        return this._sourceMesh.createInstance(name);
+    }
+
+    /**
      * Is this node ready to be used/rendered
      * Is this node ready to be used/rendered
      * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
      * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
      * @return {boolean} is it ready
      * @return {boolean} is it ready

+ 7 - 1
src/Misc/tools.ts

@@ -281,9 +281,10 @@ export class Tools {
 
 
     /**
     /**
      * Gets the pointer prefix to use
      * Gets the pointer prefix to use
+     * @param engine defines the engine we are finding the prefix for
      * @returns "pointer" if touch is enabled. Else returns "mouse"
      * @returns "pointer" if touch is enabled. Else returns "mouse"
      */
      */
-    public static GetPointerPrefix(): string {
+    public static GetPointerPrefix(engine: Engine): string {
         var eventPrefix = "pointer";
         var eventPrefix = "pointer";
 
 
         // Check if pointer events are supported
         // Check if pointer events are supported
@@ -291,6 +292,11 @@ export class Tools {
             eventPrefix = "mouse";
             eventPrefix = "mouse";
         }
         }
 
 
+        // Special Fallback...
+        if (engine._badDesktopOS) {
+            eventPrefix = "mouse";
+        }
+
         return eventPrefix;
         return eventPrefix;
     }
     }
 
 

+ 0 - 2
src/Probes/reflectionProbe.ts

@@ -103,8 +103,6 @@ export class ReflectionProbe {
         }
         }
         this._renderTargetTexture = new RenderTargetTexture(name, size, scene, generateMipMaps, true, textureType, true);
         this._renderTargetTexture = new RenderTargetTexture(name, size, scene, generateMipMaps, true, textureType, true);
 
 
-        this._renderTargetTexture.realTimeFiltering = true;
-
         this._renderTargetTexture.onBeforeRenderObservable.add((faceIndex: number) => {
         this._renderTargetTexture.onBeforeRenderObservable.add((faceIndex: number) => {
             switch (faceIndex) {
             switch (faceIndex) {
                 case 0:
                 case 0:

+ 1 - 0
src/Shaders/ShadersInclude/bonesDeclaration.fx

@@ -14,6 +14,7 @@
 	#endif
 	#endif
 
 
 	#ifdef BONETEXTURE
 	#ifdef BONETEXTURE
+        #define inline
 		mat4 readMatrixFromRawSampler(sampler2D smp, float index)
 		mat4 readMatrixFromRawSampler(sampler2D smp, float index)
 		{
 		{
 			float offset = index  * 4.0;	
 			float offset = index  * 4.0;	

+ 22 - 14
src/Shaders/ShadersInclude/hdrFilteringFunctions.fx

@@ -1,8 +1,5 @@
-#if defined(WEBGL2) && defined(NUM_SAMPLES)
-	#if NUM_SAMPLES > 0u
-		uniform vec2 vFilteringInfo;
-		uniform float linearRoughness;
-
+#ifdef NUM_SAMPLES
+	#if NUM_SAMPLES > 0
 		const float NUM_SAMPLES_FLOAT = float(NUM_SAMPLES);
 		const float NUM_SAMPLES_FLOAT = float(NUM_SAMPLES);
 		const float NUM_SAMPLES_FLOAT_INVERSED = 1. / NUM_SAMPLES_FLOAT;
 		const float NUM_SAMPLES_FLOAT_INVERSED = 1. / NUM_SAMPLES_FLOAT;
 
 
@@ -124,18 +121,23 @@
 		//
 		//
 		//
 		//
 
 
-		vec3 irradiance(samplerCube inputTexture, vec3 n) {
+		vec3 irradiance(samplerCube inputTexture, vec3 N, vec2 filteringInfo) {
+			vec3 n = normalize(N);
 		    vec3 result = vec3(0.0);
 		    vec3 result = vec3(0.0);
 			vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
 			vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
 			tangent = normalize(cross(tangent, n));
 			tangent = normalize(cross(tangent, n));
 			vec3 bitangent = cross(n, tangent);
 			vec3 bitangent = cross(n, tangent);
 			mat3 tbn = mat3(tangent, bitangent, n);
 			mat3 tbn = mat3(tangent, bitangent, n);
 
 
-		    float maxLevel = vFilteringInfo.y;
-		    float dim0 = vFilteringInfo.x;
+		    float maxLevel = filteringInfo.y;
+		    float dim0 = filteringInfo.x;
 		    float omegaP = (4. * PI) / (6. * dim0 * dim0);
 		    float omegaP = (4. * PI) / (6. * dim0 * dim0);
 
 
+		    #ifdef WEBGL2
 		    for(uint i = 0u; i < NUM_SAMPLES; ++i)
 		    for(uint i = 0u; i < NUM_SAMPLES; ++i)
+		    #else
+		    for(int i = 0; i < NUM_SAMPLES; ++i)
+		    #endif
 		    {
 		    {
 		        vec2 Xi = hammersley(i, NUM_SAMPLES);
 		        vec2 Xi = hammersley(i, NUM_SAMPLES);
 		        vec3 Ls = hemisphereCosSample(Xi);
 		        vec3 Ls = hemisphereCosSample(Xi);
@@ -166,8 +168,10 @@
 		    return result;
 		    return result;
 		}
 		}
 
 
-		vec3 radiance(samplerCube inputTexture, vec3 n) {
-			if (linearRoughness == 0.) {
+		vec3 radiance(float alphaG, samplerCube inputTexture, vec3 N, vec2 filteringInfo) {
+			vec3 n = normalize(N);
+
+			if (alphaG == 0.) {
 				vec3 c = textureCube(inputTexture, n).rgb;
 				vec3 c = textureCube(inputTexture, n).rgb;
 				#ifdef GAMMA_INPUT
 				#ifdef GAMMA_INPUT
 				    c = toLinearSpace(c);
 				    c = toLinearSpace(c);
@@ -181,15 +185,19 @@
 			vec3 bitangent = cross(n, tangent);
 			vec3 bitangent = cross(n, tangent);
 			mat3 tbn = mat3(tangent, bitangent, n);
 			mat3 tbn = mat3(tangent, bitangent, n);
 
 
-			float maxLevel = vFilteringInfo.y;
-			float dim0 = vFilteringInfo.x;
+			float maxLevel = filteringInfo.y;
+			float dim0 = filteringInfo.x;
 			float omegaP = (4. * PI) / (6. * dim0 * dim0);
 			float omegaP = (4. * PI) / (6. * dim0 * dim0);
 
 
 			float weight = 0.;
 			float weight = 0.;
+			#ifdef WEBGL2
 			for(uint i = 0u; i < NUM_SAMPLES; ++i)
 			for(uint i = 0u; i < NUM_SAMPLES; ++i)
+			#else
+			for(int i = 0; i < NUM_SAMPLES; ++i)
+			#endif
 			{
 			{
 			    vec2 Xi = hammersley(i, NUM_SAMPLES);
 			    vec2 Xi = hammersley(i, NUM_SAMPLES);
-			    vec3 H = hemisphereImportanceSampleDggx(Xi, linearRoughness);
+			    vec3 H = hemisphereImportanceSampleDggx(Xi, alphaG);
 
 
 			    float NoV = 1.;
 			    float NoV = 1.;
 			    float NoH = H.z;
 			    float NoH = H.z;
@@ -199,7 +207,7 @@
 			    L = normalize(L);
 			    L = normalize(L);
 
 
 			    if (NoL > 0.) {
 			    if (NoL > 0.) {
-			        float pdf_inversed = 4. / normalDistributionFunction_TrowbridgeReitzGGX(NoH, linearRoughness);
+			        float pdf_inversed = 4. / normalDistributionFunction_TrowbridgeReitzGGX(NoH, alphaG);
 
 
 			        float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
 			        float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
 			        float l = log4(omegaS) - log4(omegaP) + log4(K);
 			        float l = log4(omegaS) - log4(omegaP) + log4(K);

+ 24 - 24
src/Shaders/ShadersInclude/helperFunctions.fx

@@ -91,30 +91,30 @@ float square(float value)
         return vec2(float(i)/float(N), radicalInverse_VdC(i));
         return vec2(float(i)/float(N), radicalInverse_VdC(i));
     }
     }
 #else
 #else
-    // float vanDerCorpus(uint n, uint base)
-    // {
-    //     float invBase = 1.0 / float(base);
-    //     float denom   = 1.0;
-    //     float result  = 0.0;
-
-    //     for(uint i = 0u; i < 32u; ++i)
-    //     {
-    //         if(n > 0u)
-    //         {
-    //             denom   = mod(float(n), 2.0);
-    //             result += denom * invBase;
-    //             invBase = invBase / 2.0;
-    //             n       = uint(float(n) / 2.0);
-    //         }
-    //     }
-
-    //     return result;
-    // }
-
-    // vec2 hammersley(uint i, uint N)
-    // {
-    //     return vec2(float(i)/float(N), vanDerCorpus(i, 2u));
-    // }
+    float vanDerCorpus(int n, int base)
+    {
+        float invBase = 1.0 / float(base);
+        float denom   = 1.0;
+        float result  = 0.0;
+
+        for(int i = 0; i < 32; ++i)
+        {
+            if(n > 0)
+            {
+                denom   = mod(float(n), 2.0);
+                result += denom * invBase;
+                invBase = invBase / 2.0;
+                n       = int(float(n) / 2.0);
+            }
+        }
+
+        return result;
+    }
+
+    vec2 hammersley(int i, int N)
+    {
+        return vec2(float(i)/float(N), vanDerCorpus(i, 2));
+    }
 #endif
 #endif
 
 
 float log4(float x) {
 float log4(float x) {

+ 1 - 0
src/Shaders/ShadersInclude/imageProcessingFunctions.fx

@@ -4,6 +4,7 @@
 	* sampler3dSetting.x = textureOffset (0.5 / textureSize).
 	* sampler3dSetting.x = textureOffset (0.5 / textureSize).
 	* sampler3dSetting.y = textureSize.
 	* sampler3dSetting.y = textureSize.
 	*/
 	*/
+    #define inline
 	vec3 sampleTexture3D(sampler2D colorTransform, vec3 color, vec2 sampler3dSetting)
 	vec3 sampleTexture3D(sampler2D colorTransform, vec3 color, vec2 sampler3dSetting)
 	{
 	{
 		float sliceSize = 2.0 * sampler3dSetting.x; // Size of 1 slice relative to the texture, for example 1/8
 		float sliceSize = 2.0 * sampler3dSetting.x; // Size of 1 slice relative to the texture, for example 1/8

+ 1 - 0
src/Shaders/ShadersInclude/lightsFragmentFunctions.fx

@@ -109,6 +109,7 @@ lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4
 		return result;
 		return result;
 }
 }
 
 
+#define inline
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
 	vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
 	vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
 	strq /= strq.w;
 	strq /= strq.w;

+ 1 - 0
src/Shaders/ShadersInclude/pbrBlockClearcoat.fx

@@ -29,6 +29,7 @@ struct clearcoatOutParams
 };
 };
 
 
 #ifdef CLEARCOAT
 #ifdef CLEARCOAT
+    #define inline
     void clearcoatBlock(
     void clearcoatBlock(
         const in vec3 vPositionW,
         const in vec3 vPositionW,
         const in vec3 geometricNormalW,
         const in vec3 geometricNormalW,

+ 17 - 4
src/Shaders/ShadersInclude/pbrBlockReflection.fx

@@ -52,6 +52,7 @@
         #endif
         #endif
     }
     }
 
 
+    #define inline
     void sampleReflectionTexture(
     void sampleReflectionTexture(
         const in float alphaG,
         const in float alphaG,
         const in vec3 vReflectionMicrosurfaceInfos,
         const in vec3 vReflectionMicrosurfaceInfos,
@@ -111,8 +112,15 @@
             #else
             #else
                 float requestedReflectionLOD = reflectionLOD;
                 float requestedReflectionLOD = reflectionLOD;
             #endif
             #endif
-
-            environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, reflectionLOD);
+            #ifdef REALTIME_FILTERING
+                #ifdef LINEARSPECULARREFLECTION
+                    environmentRadiance = vec4(radiance(roughness, reflectionSampler, reflectionCoords, vReflectionFilteringInfo), 1.0);
+                #else
+                    environmentRadiance = vec4(radiance(alphaG, reflectionSampler, reflectionCoords, vReflectionFilteringInfo), 1.0);
+                #endif
+            #else
+                environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, reflectionLOD);
+            #endif
         #else
         #else
             float lodReflectionNormalized = saturate(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x));
             float lodReflectionNormalized = saturate(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x));
             float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
             float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
@@ -146,6 +154,7 @@
         environmentRadiance.rgb *= vReflectionColor.rgb;
         environmentRadiance.rgb *= vReflectionColor.rgb;
     }
     }
 
 
+    #define inline
     void reflectionBlock(
     void reflectionBlock(
         const in vec3 vPositionW,
         const in vec3 vPositionW,
         const in vec3 normalW,
         const in vec3 normalW,
@@ -254,8 +263,12 @@
                     irradianceVector.z *= -1.0;
                     irradianceVector.z *= -1.0;
                 #endif
                 #endif
 
 
-                #if defined(WEBGL2) && defined(REALTIME_FILTERING)
-                    environmentIrradiance = irradiance(reflectionSampler, irradianceVector);
+                #ifdef INVERTCUBICMAP
+                    irradianceVector.y *= -1.0;
+                #endif
+
+                #if defined(REALTIME_FILTERING)
+                    environmentIrradiance = irradiance(reflectionSampler, irradianceVector, vReflectionFilteringInfo);
                 #else
                 #else
                     environmentIrradiance = computeEnvironmentIrradiance(irradianceVector);
                     environmentIrradiance = computeEnvironmentIrradiance(irradianceVector);
                 #endif
                 #endif

+ 1 - 0
src/Shaders/ShadersInclude/pbrBlockSheen.fx

@@ -19,6 +19,7 @@
     #endif
     #endif
     };
     };
 
 
+    #define inline
     void sheenBlock(
     void sheenBlock(
         const in vec4 vSheenColor,
         const in vec4 vSheenColor,
     #ifdef SHEEN_ROUGHNESS
     #ifdef SHEEN_ROUGHNESS

+ 18 - 2
src/Shaders/ShadersInclude/pbrBlockSubSurface.fx

@@ -25,6 +25,7 @@ struct subSurfaceOutParams
 };
 };
 
 
 #ifdef SUBSURFACE
 #ifdef SUBSURFACE
+    #define inline
     void subSurfaceBlock(
     void subSurfaceBlock(
         const in vec3 vSubSurfaceIntensity,
         const in vec3 vSubSurfaceIntensity,
         const in vec2 vThicknessParam,
         const in vec2 vThicknessParam,
@@ -202,7 +203,15 @@ struct subSurfaceOutParams
                 float requestedRefractionLOD = refractionLOD;
                 float requestedRefractionLOD = refractionLOD;
             #endif
             #endif
 
 
-            environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD);
+            #ifdef REALTIME_FILTERING
+                #ifdef SS_LINEARSPECULARREFRACTION
+                    environmentRefraction = vec4(radiance(roughness, refractionSampler, refractionCoords, vRefractionFilteringInfo), 1.0);
+                #else
+                    environmentRefraction = vec4(radiance(alphaG, refractionSampler, refractionCoords, vRefractionFilteringInfo), 1.0);
+                #endif
+            #else
+                environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD);
+            #endif
         #else
         #else
             float lodRefractionNormalized = saturate(refractionLOD / log2(vRefractionMicrosurfaceInfos.x));
             float lodRefractionNormalized = saturate(refractionLOD / log2(vRefractionMicrosurfaceInfos.x));
             float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
             float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
@@ -300,12 +309,19 @@ struct subSurfaceOutParams
             #ifdef REFLECTIONMAP_OPPOSITEZ
             #ifdef REFLECTIONMAP_OPPOSITEZ
                 irradianceVector.z *= -1.0;
                 irradianceVector.z *= -1.0;
             #endif
             #endif
+            #ifdef INVERTCUBICMAP
+                irradianceVector.y *= -1.0;
+            #endif
         #else
         #else
             vec3 irradianceVector = irradianceVector_;
             vec3 irradianceVector = irradianceVector_;
         #endif
         #endif
 
 
         #if defined(USESPHERICALFROMREFLECTIONMAP)
         #if defined(USESPHERICALFROMREFLECTIONMAP)
-            vec3 refractionIrradiance = computeEnvironmentIrradiance(-irradianceVector);
+            #if defined(REALTIME_FILTERING)
+                vec3 refractionIrradiance = irradiance(reflectionSampler, -irradianceVector, vReflectionFilteringInfo);
+            #else
+                vec3 refractionIrradiance = computeEnvironmentIrradiance(-irradianceVector);
+            #endif
         #elif defined(USEIRRADIANCEMAP)
         #elif defined(USEIRRADIANCEMAP)
             #ifdef REFLECTIONMAP_3D
             #ifdef REFLECTIONMAP_3D
                 vec3 irradianceCoords = irradianceVector;
                 vec3 irradianceCoords = irradianceVector;

+ 1 - 0
src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx

@@ -39,6 +39,7 @@ vec3 computeDiffuseLighting(preLightingInfo info, vec3 lightColor) {
     return diffuseTerm * info.attenuation * info.NdotL * lightColor;
     return diffuseTerm * info.attenuation * info.NdotL * lightColor;
 }
 }
 
 
+#define inline
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
     vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
     vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
     strq /= strq.w;
     strq /= strq.w;

+ 6 - 0
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -52,6 +52,9 @@ uniform mat4 view;
 // Reflection
 // Reflection
 #ifdef REFLECTION
 #ifdef REFLECTION
     uniform vec2 vReflectionInfos;
     uniform vec2 vReflectionInfos;
+    #ifdef REALTIME_FILTERING
+        uniform vec2 vReflectionFilteringInfo;
+    #endif
     uniform mat4 reflectionMatrix;
     uniform mat4 reflectionMatrix;
     uniform vec3 vReflectionMicrosurfaceInfos;
     uniform vec3 vReflectionMicrosurfaceInfos;
 
 
@@ -117,6 +120,9 @@ uniform mat4 view;
         uniform vec3 vRefractionMicrosurfaceInfos;
         uniform vec3 vRefractionMicrosurfaceInfos;
         uniform vec4 vRefractionInfos;
         uniform vec4 vRefractionInfos;
         uniform mat4 refractionMatrix;
         uniform mat4 refractionMatrix;
+        #ifdef REALTIME_FILTERING
+            uniform vec2 vRefractionFilteringInfo;
+        #endif
     #endif
     #endif
 
 
     #ifdef SS_THICKNESSANDMASK_TEXTURE
     #ifdef SS_THICKNESSANDMASK_TEXTURE

+ 1 - 5
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -155,11 +155,7 @@
         uniform samplerCube reflectionSampler;
         uniform samplerCube reflectionSampler;
         
         
         #ifdef LODBASEDMICROSFURACE
         #ifdef LODBASEDMICROSFURACE
-            #if defined(WEBGL2) && defined(REALTIME_FILTERING)
-                #define sampleReflectionLod(s, c, l) vec4(radiance(s, c), 1.0)
-            #else
-                #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
-            #endif
+            #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
         #else
         #else
             uniform samplerCube reflectionSamplerLow;
             uniform samplerCube reflectionSamplerLow;
             uniform samplerCube reflectionSamplerHigh;
             uniform samplerCube reflectionSamplerHigh;

+ 2 - 0
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -10,6 +10,7 @@ uniform Material
     uniform vec3 vReflectivityInfos;
     uniform vec3 vReflectivityInfos;
     uniform vec2 vMicroSurfaceSamplerInfos;
     uniform vec2 vMicroSurfaceSamplerInfos;
     uniform vec2 vReflectionInfos;
     uniform vec2 vReflectionInfos;
+    uniform vec2 vReflectionFilteringInfo;
     uniform vec3 vReflectionPosition;
     uniform vec3 vReflectionPosition;
     uniform vec3 vReflectionSize;
     uniform vec3 vReflectionSize;
     uniform vec3 vBumpInfos;
     uniform vec3 vBumpInfos;
@@ -59,6 +60,7 @@ uniform Material
     uniform mat4 sheenMatrix;
     uniform mat4 sheenMatrix;
 
 
     uniform vec3 vRefractionMicrosurfaceInfos;
     uniform vec3 vRefractionMicrosurfaceInfos;
+    uniform vec2 vRefractionFilteringInfo;
     uniform vec4 vRefractionInfos;
     uniform vec4 vRefractionInfos;
     uniform mat4 refractionMatrix;
     uniform mat4 refractionMatrix;
     uniform vec2 vThicknessInfos;
     uniform vec2 vThicknessInfos;

+ 234 - 201
src/Shaders/ShadersInclude/shadowsFragmentFunctions.fx

@@ -14,6 +14,7 @@
         return mix(value, 1.0, mask);
         return mix(value, 1.0, mask);
     }
     }
 
 
+    #define inline
     float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, vec2 depthValues)
     float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, vec2 depthValues)
     {
     {
         vec3 directionToLight = vPositionW - lightPosition;
         vec3 directionToLight = vPositionW - lightPosition;
@@ -30,13 +31,10 @@
             float shadow = textureCube(shadowSampler, directionToLight).x;
             float shadow = textureCube(shadowSampler, directionToLight).x;
         #endif
         #endif
 
 
-        if (depth > shadow)
-        {
-            return darkness;
-        }
-        return 1.0;
+        return depth > shadow ? darkness : 1.0;
     }
     }
 
 
+    #define inline
     float computeShadowWithPoissonSamplingCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float darkness, vec2 depthValues)
     float computeShadowWithPoissonSamplingCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float darkness, vec2 depthValues)
     {
     {
         vec3 directionToLight = vPositionW - lightPosition;
         vec3 directionToLight = vPositionW - lightPosition;
@@ -72,6 +70,7 @@
         return  min(1.0, visibility + darkness);
         return  min(1.0, visibility + darkness);
     }
     }
 
 
+    #define inline
     float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     {
     {
         vec3 directionToLight = vPositionW - lightPosition;
         vec3 directionToLight = vPositionW - lightPosition;
@@ -92,6 +91,7 @@
         return esm;
         return esm;
     }
     }
 
 
+    #define inline
     float computeShadowWithCloseESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     float computeShadowWithCloseESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     {
     {
         vec3 directionToLight = vPositionW - lightPosition;
         vec3 directionToLight = vPositionW - lightPosition;
@@ -114,6 +114,7 @@
     }
     }
 
 
     #ifdef WEBGL2
     #ifdef WEBGL2
+        #define inline
         float computeShadowCSM(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray shadowSampler, float darkness, float frustumEdgeFalloff)
         float computeShadowCSM(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray shadowSampler, float darkness, float frustumEdgeFalloff)
         {
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -128,14 +129,11 @@
                 float shadow = texture2D(shadowSampler, uvLayer).x;
                 float shadow = texture2D(shadowSampler, uvLayer).x;
             #endif
             #endif
 
 
-            if (shadowPixelDepth > shadow)
-            {
-                return computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);
-            }
-            return 1.;
+            return shadowPixelDepth > shadow ? computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff) : 1.;
         }
         }
     #endif
     #endif
 
 
+    #define inline
     float computeShadow(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float frustumEdgeFalloff)
     float computeShadow(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float frustumEdgeFalloff)
     {
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -145,22 +143,21 @@
         {
         {
             return 1.0;
             return 1.0;
         }
         }
+        else
+        {
+            float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
 
 
-        float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
-
-        #ifndef SHADOWFLOAT
-            float shadow = unpack(texture2D(shadowSampler, uv));
-        #else
-            float shadow = texture2D(shadowSampler, uv).x;
-        #endif
+            #ifndef SHADOWFLOAT
+                float shadow = unpack(texture2D(shadowSampler, uv));
+            #else
+                float shadow = texture2D(shadowSampler, uv).x;
+            #endif
 
 
-        if (shadowPixelDepth > shadow)
-        {
-            return computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);
+            return shadowPixelDepth > shadow ? computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff) : 1.;
         }
         }
-        return 1.;
     }
     }
 
 
+    #define inline
     float computeShadowWithPoissonSampling(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float mapSize, float darkness, float frustumEdgeFalloff)
     float computeShadowWithPoissonSampling(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float mapSize, float darkness, float frustumEdgeFalloff)
     {
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -170,34 +167,37 @@
         {
         {
             return 1.0;
             return 1.0;
         }
         }
+        else
+        {
+            float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
 
 
-        float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
-
-        float visibility = 1.;
+            float visibility = 1.;
 
 
-        vec2 poissonDisk[4];
-        poissonDisk[0] = vec2(-0.94201624, -0.39906216);
-        poissonDisk[1] = vec2(0.94558609, -0.76890725);
-        poissonDisk[2] = vec2(-0.094184101, -0.92938870);
-        poissonDisk[3] = vec2(0.34495938, 0.29387760);
+            vec2 poissonDisk[4];
+            poissonDisk[0] = vec2(-0.94201624, -0.39906216);
+            poissonDisk[1] = vec2(0.94558609, -0.76890725);
+            poissonDisk[2] = vec2(-0.094184101, -0.92938870);
+            poissonDisk[3] = vec2(0.34495938, 0.29387760);
 
 
-        // Poisson Sampling
+            // Poisson Sampling
 
 
-        #ifndef SHADOWFLOAT
-            if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
-            if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
-            if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
-            if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
-        #else
-            if (texture2D(shadowSampler, uv + poissonDisk[0] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
-            if (texture2D(shadowSampler, uv + poissonDisk[1] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
-            if (texture2D(shadowSampler, uv + poissonDisk[2] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
-            if (texture2D(shadowSampler, uv + poissonDisk[3] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
-        #endif
+            #ifndef SHADOWFLOAT
+                if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
+                if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
+                if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
+                if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < shadowPixelDepth) visibility -= 0.25;
+            #else
+                if (texture2D(shadowSampler, uv + poissonDisk[0] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
+                if (texture2D(shadowSampler, uv + poissonDisk[1] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
+                if (texture2D(shadowSampler, uv + poissonDisk[2] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
+                if (texture2D(shadowSampler, uv + poissonDisk[3] * mapSize).x < shadowPixelDepth) visibility -= 0.25;
+            #endif
 
 
-        return computeFallOff(min(1.0, visibility + darkness), clipSpace.xy, frustumEdgeFalloff);
+            return computeFallOff(min(1.0, visibility + darkness), clipSpace.xy, frustumEdgeFalloff);
+        }
     }
     }
 
 
+    #define inline
     float computeShadowWithESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
     float computeShadowWithESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
     {
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -207,20 +207,23 @@
         {
         {
             return 1.0;
             return 1.0;
         }
         }
+        else
+        {
+            float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
 
 
-        float shadowPixelDepth = clamp(depthMetric, 0., 1.0);
-
-        #ifndef SHADOWFLOAT
-            float shadowMapSample = unpack(texture2D(shadowSampler, uv));
-        #else
-            float shadowMapSample = texture2D(shadowSampler, uv).x;
-        #endif
-        
-        float esm = 1.0 - clamp(exp(min(87., depthScale * shadowPixelDepth)) * shadowMapSample, 0., 1. - darkness);
+            #ifndef SHADOWFLOAT
+                float shadowMapSample = unpack(texture2D(shadowSampler, uv));
+            #else
+                float shadowMapSample = texture2D(shadowSampler, uv).x;
+            #endif
+            
+            float esm = 1.0 - clamp(exp(min(87., depthScale * shadowPixelDepth)) * shadowMapSample, 0., 1. - darkness);
 
 
-        return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+            return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+        }
     }
     }
 
 
+    #define inline
     float computeShadowWithCloseESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
     float computeShadowWithCloseESM(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float depthScale, float frustumEdgeFalloff)
     {
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -230,24 +233,27 @@
         {
         {
             return 1.0;
             return 1.0;
         }
         }
+        else
+        {
+            float shadowPixelDepth = clamp(depthMetric, 0., 1.0);		
+            
+            #ifndef SHADOWFLOAT
+                float shadowMapSample = unpack(texture2D(shadowSampler, uv));
+            #else
+                float shadowMapSample = texture2D(shadowSampler, uv).x;
+            #endif
+            
+            float esm = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))), darkness, 1.);
 
 
-        float shadowPixelDepth = clamp(depthMetric, 0., 1.0);		
-        
-        #ifndef SHADOWFLOAT
-            float shadowMapSample = unpack(texture2D(shadowSampler, uv));
-        #else
-            float shadowMapSample = texture2D(shadowSampler, uv).x;
-        #endif
-        
-        float esm = clamp(exp(min(87., -depthScale * (shadowPixelDepth - shadowMapSample))), darkness, 1.);
-
-        return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+            return computeFallOff(esm, clipSpace.xy, frustumEdgeFalloff);
+        }
     }
     }
 
 
     #ifdef WEBGL2
     #ifdef WEBGL2
         #define GREATEST_LESS_THAN_ONE 0.99999994
         #define GREATEST_LESS_THAN_ONE 0.99999994
 
 
         // Shadow PCF kernel size 1 with a single tap (lowest quality)
         // Shadow PCF kernel size 1 with a single tap (lowest quality)
+        #define inline
         float computeShadowWithCSMPCF1(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, float darkness, float frustumEdgeFalloff)
         float computeShadowWithCSMPCF1(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, float darkness, float frustumEdgeFalloff)
         {
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -265,6 +271,7 @@
         // Shadow PCF kernel 3*3 in only 4 taps (medium quality)
         // Shadow PCF kernel 3*3 in only 4 taps (medium quality)
         // This uses a well distributed taps to allow a gaussian distribution covering a 3*3 kernel
         // This uses a well distributed taps to allow a gaussian distribution covering a 3*3 kernel
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
+        #define inline
         float computeShadowWithCSMPCF3(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         float computeShadowWithCSMPCF3(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -301,6 +308,7 @@
         // Shadow PCF kernel 5*5 in only 9 taps (high quality)
         // Shadow PCF kernel 5*5 in only 9 taps (high quality)
         // This uses a well distributed taps to allow a gaussian distribution covering a 5*5 kernel
         // This uses a well distributed taps to allow a gaussian distribution covering a 5*5 kernel
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
+        #define inline
         float computeShadowWithCSMPCF5(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         float computeShadowWithCSMPCF5(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArrayShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -340,99 +348,108 @@
         }
         }
 
 
         // Shadow PCF kernel size 1 with a single tap (lowest quality)
         // Shadow PCF kernel size 1 with a single tap (lowest quality)
+        #define inline
         float computeShadowWithPCF1(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, float darkness, float frustumEdgeFalloff)
         float computeShadowWithPCF1(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, float darkness, float frustumEdgeFalloff)
         {
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
                 return 1.0;
                 return 1.0;
             }
             }
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
 
 
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            float shadow = texture2D(shadowSampler, uvDepth);
-            shadow = mix(darkness, 1., shadow);
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+                float shadow = texture2D(shadowSampler, uvDepth);
+                shadow = mix(darkness, 1., shadow);
+                return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            }
         }
         }
 
 
         // Shadow PCF kernel 3*3 in only 4 taps (medium quality)
         // Shadow PCF kernel 3*3 in only 4 taps (medium quality)
         // This uses a well distributed taps to allow a gaussian distribution covering a 3*3 kernel
         // This uses a well distributed taps to allow a gaussian distribution covering a 3*3 kernel
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
+        #define inline
         float computeShadowWithPCF3(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         float computeShadowWithPCF3(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
                 return 1.0;
                 return 1.0;
             }
             }
-
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            vec2 uv = uvDepth.xy * shadowMapSizeAndInverse.x;	// uv in texel units
-            uv += 0.5;											// offset of half to be in the center of the texel
-            vec2 st = fract(uv);								// how far from the center
-            vec2 base_uv = floor(uv) - 0.5;						// texel coord
-            base_uv *= shadowMapSizeAndInverse.y;				// move back to uv coords
-
-            // Equation resolved to fit in a 3*3 distribution like 
-            // 1 2 1
-            // 2 4 2 
-            // 1 2 1
-            vec2 uvw0 = 3. - 2. * st;
-            vec2 uvw1 = 1. + 2. * st;
-            vec2 u = vec2((2. - st.x) / uvw0.x - 1., st.x / uvw1.x + 1.) * shadowMapSizeAndInverse.y;
-            vec2 v = vec2((2. - st.y) / uvw0.y - 1., st.y / uvw1.y + 1.) * shadowMapSizeAndInverse.y;
-
-            float shadow = 0.;
-            shadow += uvw0.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
-            shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
-            shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
-            shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), uvDepth.z));
-            shadow = shadow / 16.;
-
-            shadow = mix(darkness, 1., shadow);
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+                vec2 uv = uvDepth.xy * shadowMapSizeAndInverse.x;	// uv in texel units
+                uv += 0.5;											// offset of half to be in the center of the texel
+                vec2 st = fract(uv);								// how far from the center
+                vec2 base_uv = floor(uv) - 0.5;						// texel coord
+                base_uv *= shadowMapSizeAndInverse.y;				// move back to uv coords
+
+                // Equation resolved to fit in a 3*3 distribution like 
+                // 1 2 1
+                // 2 4 2 
+                // 1 2 1
+                vec2 uvw0 = 3. - 2. * st;
+                vec2 uvw1 = 1. + 2. * st;
+                vec2 u = vec2((2. - st.x) / uvw0.x - 1., st.x / uvw1.x + 1.) * shadowMapSizeAndInverse.y;
+                vec2 v = vec2((2. - st.y) / uvw0.y - 1., st.y / uvw1.y + 1.) * shadowMapSizeAndInverse.y;
+
+                float shadow = 0.;
+                shadow += uvw0.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
+                shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
+                shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
+                shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), uvDepth.z));
+                shadow = shadow / 16.;
+
+                shadow = mix(darkness, 1., shadow);
+                return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            }
         }
         }
         
         
         // Shadow PCF kernel 5*5 in only 9 taps (high quality)
         // Shadow PCF kernel 5*5 in only 9 taps (high quality)
         // This uses a well distributed taps to allow a gaussian distribution covering a 5*5 kernel
         // This uses a well distributed taps to allow a gaussian distribution covering a 5*5 kernel
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
         // https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/
+        #define inline
         float computeShadowWithPCF5(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         float computeShadowWithPCF5(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
                 return 1.0;
                 return 1.0;
             }
             }
-
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            vec2 uv = uvDepth.xy * shadowMapSizeAndInverse.x;	// uv in texel units
-            uv += 0.5;											// offset of half to be in the center of the texel
-            vec2 st = fract(uv);								// how far from the center
-            vec2 base_uv = floor(uv) - 0.5;						// texel coord
-            base_uv *= shadowMapSizeAndInverse.y;				// move back to uv coords
-
-            // Equation resolved to fit in a 5*5 distribution like 
-            // 1 2 4 2 1
-            vec2 uvw0 = 4. - 3. * st;
-            vec2 uvw1 = vec2(7.);
-            vec2 uvw2 = 1. + 3. * st;
-
-            vec3 u = vec3((3. - 2. * st.x) / uvw0.x - 2., (3. + st.x) / uvw1.x, st.x / uvw2.x + 2.) * shadowMapSizeAndInverse.y;
-            vec3 v = vec3((3. - 2. * st.y) / uvw0.y - 2., (3. + st.y) / uvw1.y, st.y / uvw2.y + 2.) * shadowMapSizeAndInverse.y;
-
-            float shadow = 0.;
-            shadow += uvw0.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
-            shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
-            shadow += uvw2.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[0]), uvDepth.z));
-            shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
-            shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), uvDepth.z));
-            shadow += uvw2.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[1]), uvDepth.z));
-            shadow += uvw0.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[2]), uvDepth.z));
-            shadow += uvw1.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[2]), uvDepth.z));
-            shadow += uvw2.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[2]), uvDepth.z));
-            shadow = shadow / 144.;
-
-            shadow = mix(darkness, 1., shadow);
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+                vec2 uv = uvDepth.xy * shadowMapSizeAndInverse.x;	// uv in texel units
+                uv += 0.5;											// offset of half to be in the center of the texel
+                vec2 st = fract(uv);								// how far from the center
+                vec2 base_uv = floor(uv) - 0.5;						// texel coord
+                base_uv *= shadowMapSizeAndInverse.y;				// move back to uv coords
+
+                // Equation resolved to fit in a 5*5 distribution like 
+                // 1 2 4 2 1
+                vec2 uvw0 = 4. - 3. * st;
+                vec2 uvw1 = vec2(7.);
+                vec2 uvw2 = 1. + 3. * st;
+
+                vec3 u = vec3((3. - 2. * st.x) / uvw0.x - 2., (3. + st.x) / uvw1.x, st.x / uvw2.x + 2.) * shadowMapSizeAndInverse.y;
+                vec3 v = vec3((3. - 2. * st.y) / uvw0.y - 2., (3. + st.y) / uvw1.y, st.y / uvw2.y + 2.) * shadowMapSizeAndInverse.y;
+
+                float shadow = 0.;
+                shadow += uvw0.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[0]), uvDepth.z));
+                shadow += uvw1.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[0]), uvDepth.z));
+                shadow += uvw2.x * uvw0.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[0]), uvDepth.z));
+                shadow += uvw0.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[1]), uvDepth.z));
+                shadow += uvw1.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[1]), uvDepth.z));
+                shadow += uvw2.x * uvw1.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[1]), uvDepth.z));
+                shadow += uvw0.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[0], v[2]), uvDepth.z));
+                shadow += uvw1.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[1], v[2]), uvDepth.z));
+                shadow += uvw2.x * uvw2.y * texture2D(shadowSampler, vec3(base_uv.xy + vec2(u[2], v[2]), uvDepth.z));
+                shadow = shadow / 144.;
+
+                shadow = mix(darkness, 1., shadow);
+                return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+            }
         }
         }
 
 
         const vec3 PoissonSamplers32[64] = vec3[64](
         const vec3 PoissonSamplers32[64] = vec3[64](
@@ -575,6 +592,7 @@
         // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc.
         // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc.
         // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf
         // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf
         // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf
         // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf
+        #define inline
         float computeShadowWithCSMPCSS(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, int searchTapCount, int pcfTapCount, vec3[64] poissonSamplers, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         float computeShadowWithCSMPCSS(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, int searchTapCount, int pcfTapCount, vec3[64] poissonSamplers, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         {
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -598,36 +616,39 @@
             if (numBlocker < 1.0) {
             if (numBlocker < 1.0) {
                 return 1.0;
                 return 1.0;
             }
             }
-            float avgBlockerDepth = sumBlockerDepth / numBlocker;
+            else
+            {
+                float avgBlockerDepth = sumBlockerDepth / numBlocker;
+
+                // Offset preventing aliasing on contact.
+                float AAOffset = shadowMapSizeInverse * 10.;
+                // Do not dividing by z despite being physically incorrect looks better due to the limited kernel size.
+                // float penumbraRatio = (depthMetric - avgBlockerDepth) / avgBlockerDepth;
+                float penumbraRatio = ((depthMetric - avgBlockerDepth) * depthCorrection + AAOffset);
+                vec4 filterRadius = vec4(penumbraRatio * lightSizeUV * lightSizeUVCorrection * shadowMapSizeInverse, 0., 0.);
+
+                float random = getRand(vPositionFromLight.xy);
+                float rotationAngle = random * 3.1415926;
+                vec2 rotationVector = vec2(cos(rotationAngle), sin(rotationAngle));
+
+                float shadow = 0.;
+                for (int i = 0; i < pcfTapCount; i++) {
+                    vec4 offset = vec4(poissonSamplers[i], 0.);
+                    // Rotated offset.
+                    offset = vec4(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0., 0.);
+                    shadow += texture2D(shadowSampler, uvDepthLayer + offset * filterRadius);
+                }
+                shadow /= float(pcfTapCount);
 
 
-            // Offset preventing aliasing on contact.
-            float AAOffset = shadowMapSizeInverse * 10.;
-            // Do not dividing by z despite being physically incorrect looks better due to the limited kernel size.
-            // float penumbraRatio = (depthMetric - avgBlockerDepth) / avgBlockerDepth;
-            float penumbraRatio = ((depthMetric - avgBlockerDepth) * depthCorrection + AAOffset);
-            vec4 filterRadius = vec4(penumbraRatio * lightSizeUV * lightSizeUVCorrection * shadowMapSizeInverse, 0., 0.);
+                // Blocker distance falloff
+                shadow = mix(shadow, 1., min((depthMetric - avgBlockerDepth) * depthCorrection * penumbraDarkness, 1.));
 
 
-            float random = getRand(vPositionFromLight.xy);
-            float rotationAngle = random * 3.1415926;
-            vec2 rotationVector = vec2(cos(rotationAngle), sin(rotationAngle));
+                // Apply darkness
+                shadow = mix(darkness, 1., shadow);
 
 
-            float shadow = 0.;
-            for (int i = 0; i < pcfTapCount; i++) {
-                vec4 offset = vec4(poissonSamplers[i], 0.);
-                // Rotated offset.
-                offset = vec4(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0., 0.);
-                shadow += texture2D(shadowSampler, uvDepthLayer + offset * filterRadius);
+                // Apply light frustrum fallof
+                return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
             }
             }
-            shadow /= float(pcfTapCount);
-
-            // Blocker distance falloff
-            shadow = mix(shadow, 1., min((depthMetric - avgBlockerDepth) * depthCorrection * penumbraDarkness, 1.));
-
-            // Apply darkness
-            shadow = mix(darkness, 1., shadow);
-
-            // Apply light frustrum fallof
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
         }
         }
 
 
         // PCSS
         // PCSS
@@ -635,86 +656,98 @@
         // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc.
         // It uses 16 Taps for search and a 32 PCF taps in a randomly rotating poisson sampling disc.
         // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf
         // This is heavily inspired from http://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf
         // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf
         // and http://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf
+        #define inline
         float computeShadowWithPCSS(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, int searchTapCount, int pcfTapCount, vec3[64] poissonSamplers)
         float computeShadowWithPCSS(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, int searchTapCount, int pcfTapCount, vec3[64] poissonSamplers)
         {
         {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
             if (depthMetric > 1.0 || depthMetric < 0.0) {
                 return 1.0;
                 return 1.0;
             }
             }
-
-            vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
-            vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
-
-            float blockerDepth = 0.0;
-            float sumBlockerDepth = 0.0;
-            float numBlocker = 0.0;
-            for (int i = 0; i < searchTapCount; i ++) {
-                blockerDepth = texture(depthSampler, uvDepth.xy + (lightSizeUV * shadowMapSizeInverse * PoissonSamplers32[i].xy)).r;
-                if (blockerDepth < depthMetric) {
-                    sumBlockerDepth += blockerDepth;
-                    numBlocker++;
+            else
+            {
+                vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
+                vec3 uvDepth = vec3(0.5 * clipSpace.xyz + vec3(0.5));
+
+                float blockerDepth = 0.0;
+                float sumBlockerDepth = 0.0;
+                float numBlocker = 0.0;
+                for (int i = 0; i < searchTapCount; i ++) {
+                    blockerDepth = texture(depthSampler, uvDepth.xy + (lightSizeUV * shadowMapSizeInverse * PoissonSamplers32[i].xy)).r;
+                    if (blockerDepth < depthMetric) {
+                        sumBlockerDepth += blockerDepth;
+                        numBlocker++;
+                    }
                 }
                 }
-            }
-
-            if (numBlocker < 1.0) {
-                return 1.0;
-            }
-            float avgBlockerDepth = sumBlockerDepth / numBlocker;
-
-            // Offset preventing aliasing on contact.
-            float AAOffset = shadowMapSizeInverse * 10.;
-            // Do not dividing by z despite being physically incorrect looks better due to the limited kernel size.
-            // float penumbraRatio = (depthMetric - avgBlockerDepth) / avgBlockerDepth;
-            float penumbraRatio = ((depthMetric - avgBlockerDepth) + AAOffset);
-            float filterRadius = penumbraRatio * lightSizeUV * shadowMapSizeInverse;
-
-            float random = getRand(vPositionFromLight.xy);
-            float rotationAngle = random * 3.1415926;
-            vec2 rotationVector = vec2(cos(rotationAngle), sin(rotationAngle));
 
 
-            float shadow = 0.;
-            for (int i = 0; i < pcfTapCount; i++) {
-                vec3 offset = poissonSamplers[i];
-                // Rotated offset.
-                offset = vec3(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0.);
-                shadow += texture2D(shadowSampler, uvDepth + offset * filterRadius);
+                if (numBlocker < 1.0) {
+                    return 1.0;
+                }
+                else
+                {
+                    float avgBlockerDepth = sumBlockerDepth / numBlocker;
+
+                    // Offset preventing aliasing on contact.
+                    float AAOffset = shadowMapSizeInverse * 10.;
+                    // Do not dividing by z despite being physically incorrect looks better due to the limited kernel size.
+                    // float penumbraRatio = (depthMetric - avgBlockerDepth) / avgBlockerDepth;
+                    float penumbraRatio = ((depthMetric - avgBlockerDepth) + AAOffset);
+                    float filterRadius = penumbraRatio * lightSizeUV * shadowMapSizeInverse;
+
+                    float random = getRand(vPositionFromLight.xy);
+                    float rotationAngle = random * 3.1415926;
+                    vec2 rotationVector = vec2(cos(rotationAngle), sin(rotationAngle));
+
+                    float shadow = 0.;
+                    for (int i = 0; i < pcfTapCount; i++) {
+                        vec3 offset = poissonSamplers[i];
+                        // Rotated offset.
+                        offset = vec3(offset.x * rotationVector.x - offset.y * rotationVector.y, offset.y * rotationVector.x + offset.x * rotationVector.y, 0.);
+                        shadow += texture2D(shadowSampler, uvDepth + offset * filterRadius);
+                    }
+                    shadow /= float(pcfTapCount);
+
+                    // Blocker distance falloff
+                    shadow = mix(shadow, 1., depthMetric - avgBlockerDepth);
+
+                    // Apply darkness
+                    shadow = mix(darkness, 1., shadow);
+
+                    // Apply light frustrum fallof
+                    return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
+                }
             }
             }
-            shadow /= float(pcfTapCount);
-
-            // Blocker distance falloff
-            shadow = mix(shadow, 1., depthMetric - avgBlockerDepth);
-
-            // Apply darkness
-            shadow = mix(darkness, 1., shadow);
-
-            // Apply light frustrum fallof
-            return computeFallOff(shadow, clipSpace.xy, frustumEdgeFalloff);
         }
         }
 
 
+        #define inline
         float computeShadowWithPCSS16(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         float computeShadowWithPCSS16(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         {
         {
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32);
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32);
         }
         }
 
 
+        #define inline
         float computeShadowWithPCSS32(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         float computeShadowWithPCSS32(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         {
         {
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32);
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32);
         }
         }
 
 
+        #define inline
         float computeShadowWithPCSS64(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         float computeShadowWithPCSS64(vec4 vPositionFromLight, float depthMetric, sampler2D depthSampler, sampler2DShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff)
         {
         {
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64);
             return computeShadowWithPCSS(vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64);
         }
         }
 
 
+        #define inline
         float computeShadowWithCSMPCSS16(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         float computeShadowWithCSMPCSS16(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         {
         {
             return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
             return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 16, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
         }
         }
 
 
+        #define inline
         float computeShadowWithCSMPCSS32(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         float computeShadowWithCSMPCSS32(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         {
         {
             return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
             return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 16, 32, PoissonSamplers32, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
         }
         }
 
 
+        #define inline
         float computeShadowWithCSMPCSS64(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         float computeShadowWithCSMPCSS64(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray depthSampler, highp sampler2DArrayShadow shadowSampler, float shadowMapSizeInverse, float lightSizeUV, float darkness, float frustumEdgeFalloff, vec2 lightSizeUVCorrection, float depthCorrection, float penumbraDarkness)
         {
         {
             return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64, lightSizeUVCorrection, depthCorrection, penumbraDarkness);
             return computeShadowWithCSMPCSS(layer, vPositionFromLight, depthMetric, depthSampler, shadowSampler, shadowMapSizeInverse, lightSizeUV, darkness, frustumEdgeFalloff, 32, 64, PoissonSamplers64, lightSizeUVCorrection, depthCorrection, penumbraDarkness);

+ 3 - 1
src/Shaders/hdrFiltering.fragment.fx

@@ -3,13 +3,15 @@
 #include<pbrBRDFFunctions>
 #include<pbrBRDFFunctions>
 #include<hdrFilteringFunctions>
 #include<hdrFilteringFunctions>
 
 
+uniform float alphaG;
 uniform samplerCube inputTexture;
 uniform samplerCube inputTexture;
+uniform vec2 vFilteringInfo;
 uniform float hdrScale;
 uniform float hdrScale;
 
 
 varying vec3 direction;
 varying vec3 direction;
 
 
 void main() {
 void main() {
-    vec3 color = radiance(inputTexture, direction);
+    vec3 color = radiance(alphaG, inputTexture, direction, vFilteringInfo);
 
 
     gl_FragColor = vec4(color * hdrScale, 1.0);
     gl_FragColor = vec4(color * hdrScale, 1.0);
 }
 }

+ 69 - 3
src/Sprites/sprite.ts

@@ -1,7 +1,7 @@
 import { Vector3 } from "../Maths/math.vector";
 import { Vector3 } from "../Maths/math.vector";
 import { Nullable } from "../types";
 import { Nullable } from "../types";
 import { ActionManager } from "../Actions/actionManager";
 import { ActionManager } from "../Actions/actionManager";
-import { ISpriteManager } from "./spriteManager";
+import { ISpriteManager, SpriteManager } from "./spriteManager";
 import { Color4 } from '../Maths/math.color';
 import { Color4 } from '../Maths/math.color';
 import { Observable } from '../Misc/observable';
 import { Observable } from '../Misc/observable';
 import { IAnimatable } from '../Animations/animatable.interface';
 import { IAnimatable } from '../Animations/animatable.interface';
@@ -66,7 +66,7 @@ export class Sprite implements IAnimatable {
     private _direction = 1;
     private _direction = 1;
     private _manager: ISpriteManager;
     private _manager: ISpriteManager;
     private _time = 0;
     private _time = 0;
-    private _onAnimationEnd: () => void;
+    private _onAnimationEnd: Nullable<() => void> = null;
     /**
     /**
      * Gets or sets a boolean indicating if the sprite is visible (renderable). Default is true
      * Gets or sets a boolean indicating if the sprite is visible (renderable). Default is true
      */
      */
@@ -172,7 +172,7 @@ export class Sprite implements IAnimatable {
      * @param delay defines the start delay (in ms)
      * @param delay defines the start delay (in ms)
      * @param onAnimationEnd defines a callback to call when animation ends
      * @param onAnimationEnd defines a callback to call when animation ends
      */
      */
-    public playAnimation(from: number, to: number, loop: boolean, delay: number, onAnimationEnd: () => void): void {
+    public playAnimation(from: number, to: number, loop: boolean, delay: number, onAnimationEnd: Nullable<() => void> = null): void {
         this._fromIndex = from;
         this._fromIndex = from;
         this._toIndex = to;
         this._toIndex = to;
         this._loopAnimation = loop;
         this._loopAnimation = loop;
@@ -237,4 +237,70 @@ export class Sprite implements IAnimatable {
         this.onDisposeObservable.notifyObservers(this);
         this.onDisposeObservable.notifyObservers(this);
         this.onDisposeObservable.clear();
         this.onDisposeObservable.clear();
     }
     }
+
+    /**
+     * Serializes the sprite to a JSON object
+     * @returns the JSON object
+     */
+    public serialize(): any {
+        var serializationObject: any = {};
+
+        serializationObject.name = this.name;
+        serializationObject.position = this.position.asArray();
+        serializationObject.color = this.color.asArray();
+        serializationObject.width = this.width;
+        serializationObject.height = this.height;
+        serializationObject.angle = this.angle;
+        serializationObject.cellIndex = this.cellIndex;
+        serializationObject.cellRef = this.cellRef;
+        serializationObject.invertU = this.invertU;
+        serializationObject.invertV = this.invertV;
+        serializationObject.disposeWhenFinishedAnimating = this.disposeWhenFinishedAnimating;
+        serializationObject.isPickable = this.isPickable;
+        serializationObject.isVisible = this.isVisible;
+        serializationObject.useAlphaForPicking = this.useAlphaForPicking;
+
+        serializationObject.animationStarted = this.animationStarted;
+        serializationObject.fromIndex = this.fromIndex;
+        serializationObject.toIndex = this.toIndex;
+        serializationObject.loopAnimation = this.loopAnimation;
+        serializationObject.delay = this.delay;
+
+        return serializationObject;
+    }
+
+    /**
+     * Parses a JSON object to create a new sprite
+     * @param parsedSprite The JSON object to parse
+     * @param manager defines the hosting manager
+     * @returns the new sprite
+     */
+    public static Parse(parsedSprite: any, manager: SpriteManager): Sprite {
+        var sprite = new Sprite(parsedSprite.name, manager);
+
+        sprite.position = Vector3.FromArray(parsedSprite.position);
+        sprite.color = Color4.FromArray(parsedSprite.color);
+        sprite.width = parsedSprite.width;
+        sprite.height = parsedSprite.height;
+        sprite.angle = parsedSprite.angle;
+        sprite.cellIndex = parsedSprite.cellIndex;
+        sprite.cellRef = parsedSprite.cellRef;
+        sprite.invertU = parsedSprite.invertU;
+        sprite.invertV = parsedSprite.invertV;
+        sprite.disposeWhenFinishedAnimating = parsedSprite.disposeWhenFinishedAnimating;
+        sprite.isPickable = parsedSprite.isPickable;
+        sprite.isVisible = parsedSprite.isVisible;
+        sprite.useAlphaForPicking = parsedSprite.useAlphaForPicking;
+
+        sprite.fromIndex = parsedSprite.fromIndex;
+        sprite.toIndex = parsedSprite.toIndex;
+        sprite.loopAnimation = parsedSprite.loopAnimation;
+        sprite.delay = parsedSprite.delay;
+
+        if (parsedSprite.animationStarted) {
+            sprite.playAnimation(sprite.fromIndex, sprite.toIndex, sprite.loopAnimation, sprite.delay);
+        }
+
+        return sprite;
+    }
 }
 }

+ 104 - 8
src/Sprites/spriteManager.ts

@@ -19,6 +19,7 @@ import "../Shaders/sprites.fragment";
 import "../Shaders/sprites.vertex";
 import "../Shaders/sprites.vertex";
 import { DataBuffer } from '../Meshes/dataBuffer';
 import { DataBuffer } from '../Meshes/dataBuffer';
 import { Engine } from '../Engines/engine';
 import { Engine } from '../Engines/engine';
+import { WebRequest } from '../Misc/webRequest';
 declare type Ray = import("../Culling/ray").Ray;
 declare type Ray = import("../Culling/ray").Ray;
 
 
 /**
 /**
@@ -98,6 +99,12 @@ export interface ISpriteManager extends IDisposable {
  * @see http://doc.babylonjs.com/babylon101/sprites
  * @see http://doc.babylonjs.com/babylon101/sprites
  */
  */
 export class SpriteManager implements ISpriteManager {
 export class SpriteManager implements ISpriteManager {
+    /** Define the Url to load snippets */
+    public static SnippetUrl = "https://snippet.babylonjs.com";
+
+    /** Snippet ID if the manager was created from the snippet server */
+    public snippetId: string;
+
     /** Gets the list of sprites */
     /** Gets the list of sprites */
     public sprites = new Array<Sprite>();
     public sprites = new Array<Sprite>();
     /** Gets or sets the rendering group id (0 by default) */
     /** Gets or sets the rendering group id (0 by default) */
@@ -173,16 +180,12 @@ export class SpriteManager implements ISpriteManager {
     }
     }
 
 
     /**
     /**
-     * Gets or sets the capacity of the manager
+     * Gets the capacity of the manager
      */
      */
     public get capacity() {
     public get capacity() {
         return this._capacity;
         return this._capacity;
     }
     }
 
 
-    public set capacity(value: number) {
-        this._capacity = value;
-    }
-
     /**
     /**
      * Gets or sets the spritesheet texture
      * Gets or sets the spritesheet texture
      */
      */
@@ -192,6 +195,8 @@ export class SpriteManager implements ISpriteManager {
 
 
     public set texture(value: Texture) {
     public set texture(value: Texture) {
         this._spriteTexture = value;
         this._spriteTexture = value;
+        this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
+        this._spriteTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
         this._textureContent = null;
         this._textureContent = null;
     }
     }
 
 
@@ -233,9 +238,12 @@ export class SpriteManager implements ISpriteManager {
         }
         }
         this._capacity = capacity;
         this._capacity = capacity;
         this._fromPacked = fromPacked;
         this._fromPacked = fromPacked;
-        this._spriteTexture = new Texture(imgUrl, scene, true, false, samplingMode);
-        this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
-        this._spriteTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+
+        if (imgUrl) {
+            this._spriteTexture = new Texture(imgUrl, scene, true, false, samplingMode);
+            this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._spriteTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+        }
 
 
         if (cellSize.width && cellSize.height) {
         if (cellSize.width && cellSize.height) {
             this.cellWidth = cellSize.width;
             this.cellWidth = cellSize.width;
@@ -757,4 +765,92 @@ export class SpriteManager implements ISpriteManager {
         this.onDisposeObservable.notifyObservers(this);
         this.onDisposeObservable.notifyObservers(this);
         this.onDisposeObservable.clear();
         this.onDisposeObservable.clear();
     }
     }
+
+    /**
+     * Serializes the sprite manager to a JSON object
+     * @param serializeTexture defines if the texture must be serialized as well
+     * @returns the JSON object
+     */
+    public serialize(serializeTexture = false): any {
+        var serializationObject: any = {};
+
+        serializationObject.name = this.name;
+        serializationObject.capacity = this.capacity;
+        serializationObject.cellWidth = this.cellWidth;
+        serializationObject.cellHeight = this.cellHeight;
+
+        if (this.texture) {
+            if (serializeTexture) {
+                serializationObject.texture = this.texture.serialize();
+            } else {
+                serializationObject.textureUrl = this.texture.name;
+                serializationObject.invertY = this.texture._invertY;
+            }
+        }
+
+        serializationObject.sprites = [];
+
+        for (var sprite of this.sprites) {
+            serializationObject.sprites.push(sprite.serialize());
+        }
+
+        return serializationObject;
+    }
+
+    /**
+     * Parses a JSON object to create a new sprite manager.
+     * @param parsedManager The JSON object to parse
+     * @param scene The scene to create the sprite managerin
+     * @param rootUrl The root url to use to load external dependencies like texture
+     * @returns the new sprite manager
+     */
+    public static Parse(parsedManager: any, scene: Scene, rootUrl: string): SpriteManager {
+        var manager = new SpriteManager(parsedManager.name, "", parsedManager.capacity, {
+            width: parsedManager.cellWidth,
+            height: parsedManager.cellHeight,
+        }, scene);
+
+        if (parsedManager.texture) {
+            manager.texture = Texture.Parse(parsedManager.texture, scene, rootUrl) as Texture;
+        } else if (parsedManager.textureName) {
+            manager.texture = new Texture(rootUrl + parsedManager.textureUrl, scene, false, parsedManager.invertY !== undefined ? parsedManager.invertY : true);
+        }
+
+        for (var parsedSprite of parsedManager.sprites) {
+            Sprite.Parse(parsedSprite, manager);
+        }
+
+        return manager;
+    }
+
+    /**
+     * Creates a sprite manager from a snippet saved by the sprite editor
+     * @param snippetId defines the snippet to load
+     * @param scene defines the hosting scene
+     * @param rootUrl defines the root URL to use to load textures and relative dependencies
+     * @returns a promise that will resolve to the new sprite manager
+     */
+    public static CreateFromSnippetAsync(snippetId: string, scene: Scene, rootUrl: string = ""): Promise<SpriteManager> {
+        return new Promise((resolve, reject) => {
+            var request = new WebRequest();
+            request.addEventListener("readystatechange", () => {
+                if (request.readyState == 4) {
+                    if (request.status == 200) {
+                        var snippet = JSON.parse(JSON.parse(request.responseText).jsonPayload);
+                        let serializationObject = JSON.parse(snippet.spriteManager);
+                        let output = SpriteManager.Parse(serializationObject, scene, rootUrl);
+
+                        output.snippetId = snippetId;
+
+                        resolve(output);
+                    } else {
+                        reject("Unable to load the snippet " + snippetId);
+                    }
+                }
+            });
+
+            request.open("GET", this.SnippetUrl + "/" + snippetId.replace(/#/g, "/"));
+            request.send();
+        });
+    }
 }
 }

BIN
tests/validation/ReferenceImages/gltfExtensionExtMeshGpuInstancingTest.png


+ 5 - 0
tests/validation/config.json

@@ -827,6 +827,11 @@
             "title": "Visibility",
             "title": "Visibility",
             "playgroundId": "#PXC9CF#2",
             "playgroundId": "#PXC9CF#2",
             "referenceImage": "visibility.png"
             "referenceImage": "visibility.png"
+        },
+        {
+            "title": "GLTF Extension EXT_mesh_gpu_instancing",
+            "playgroundId": "#QFIGLW#9",
+            "referenceImage": "gltfExtensionExtMeshGpuInstancingTest.png"
         }
         }
     ]
     ]
 }
 }