소스 검색

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

Raanan Weber 5 년 전
부모
커밋
ed483cefec
93개의 변경된 파일186816개의 추가작업 그리고 162392개의 파일을 삭제
  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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 180950 - 161222
Playground/js/babylonWebGpu.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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 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) */
         stopAnimation(): void;
         /** @hidden */
         _animate(deltaTime: number): void;
         /** Release associated resources */
         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 {
@@ -10898,6 +10910,10 @@ declare module BABYLON {
     export class SpriteManager implements ISpriteManager {
         /** defines the manager's name */
         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 */
         sprites: Sprite[];
         /** Gets or sets the rendering group id (0 by default) */
@@ -10952,10 +10968,9 @@ declare module BABYLON {
          */
         get scene(): Scene;
         /**
-         * Gets or sets the capacity of the manager
+         * Gets the capacity of the manager
          */
         get capacity(): number;
-        set capacity(value: number);
         /**
          * Gets or sets the spritesheet texture
          */
@@ -11022,6 +11037,28 @@ declare module BABYLON {
          * Release associated resources
          */
         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 {
@@ -16126,6 +16163,13 @@ declare module BABYLON {
          */
         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
          * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
          * @return {boolean} is it ready
@@ -32757,18 +32801,6 @@ declare module BABYLON {
          * @returns "BaseTexture"
          */
         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.
          */
@@ -37079,9 +37111,10 @@ declare module BABYLON {
         static MakeArray(obj: any, allowsNullUndefined?: boolean): Nullable<Array<any>>;
         /**
          * 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"
          */
-        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.
          * @param url define the url we are trying
@@ -49476,24 +49509,209 @@ declare module BABYLON.Debug {
 }
 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
      * any Gamepads and provide a polling system that all devices
      * will use.  This class assumes that there will only be one
      * pointer device and one keyboard.
      */
     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 _gamepads;
         private _keyboardActive;
@@ -49519,20 +49737,29 @@ declare module BABYLON {
          * @param inputIndex Index of device 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;
         /**
-         * 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
          */
         private _registerDevice;
         /**
          * 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;
         /**
@@ -49549,8 +49776,135 @@ declare module BABYLON {
         private _handleGamepadActions;
         /**
          * 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;
+        /**
+         * 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 {
@@ -50686,8 +51040,8 @@ declare module BABYLON {
          */
         getHostDocument(): Nullable<Document>;
         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: {
             [key: string]: VertexBuffer;
         }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject;
@@ -53711,10 +54065,11 @@ declare module BABYLON {
          * @param uniformBuffer defines the Uniform buffer to fill in.
          * @param scene defines the scene 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.
          * @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.
          */
         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.
          */
@@ -58107,7 +58474,7 @@ declare module BABYLON {
           * @param onFinished Callback when filtering 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 {
@@ -72456,6 +72823,29 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @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: {
         name: string;
         shader: string;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/babylon.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 850 - 180
dist/preview release/babylon.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/babylon.max.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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 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) */
         stopAnimation(): void;
         /** @hidden */
         _animate(deltaTime: number): void;
         /** Release associated resources */
         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 {
@@ -10898,6 +10910,10 @@ declare module BABYLON {
     export class SpriteManager implements ISpriteManager {
         /** defines the manager's name */
         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 */
         sprites: Sprite[];
         /** Gets or sets the rendering group id (0 by default) */
@@ -10952,10 +10968,9 @@ declare module BABYLON {
          */
         get scene(): Scene;
         /**
-         * Gets or sets the capacity of the manager
+         * Gets the capacity of the manager
          */
         get capacity(): number;
-        set capacity(value: number);
         /**
          * Gets or sets the spritesheet texture
          */
@@ -11022,6 +11037,28 @@ declare module BABYLON {
          * Release associated resources
          */
         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 {
@@ -16126,6 +16163,13 @@ declare module BABYLON {
          */
         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
          * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
          * @return {boolean} is it ready
@@ -32757,18 +32801,6 @@ declare module BABYLON {
          * @returns "BaseTexture"
          */
         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.
          */
@@ -37079,9 +37111,10 @@ declare module BABYLON {
         static MakeArray(obj: any, allowsNullUndefined?: boolean): Nullable<Array<any>>;
         /**
          * 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"
          */
-        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.
          * @param url define the url we are trying
@@ -49476,24 +49509,209 @@ declare module BABYLON.Debug {
 }
 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
      * any Gamepads and provide a polling system that all devices
      * will use.  This class assumes that there will only be one
      * pointer device and one keyboard.
      */
     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 _gamepads;
         private _keyboardActive;
@@ -49519,20 +49737,29 @@ declare module BABYLON {
          * @param inputIndex Index of device 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;
         /**
-         * 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
          */
         private _registerDevice;
         /**
          * 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;
         /**
@@ -49549,8 +49776,135 @@ declare module BABYLON {
         private _handleGamepadActions;
         /**
          * 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;
+        /**
+         * 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 {
@@ -50686,8 +51040,8 @@ declare module BABYLON {
          */
         getHostDocument(): Nullable<Document>;
         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: {
             [key: string]: VertexBuffer;
         }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject;
@@ -53711,10 +54065,11 @@ declare module BABYLON {
          * @param uniformBuffer defines the Uniform buffer to fill in.
          * @param scene defines the scene 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.
          * @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.
          */
         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.
          */
@@ -58107,7 +58474,7 @@ declare module BABYLON {
           * @param onFinished Callback when filtering 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 {
@@ -72456,6 +72823,29 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /** @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: {
         name: string;
         shader: string;
@@ -82364,6 +82754,7 @@ declare module BABYLON {
 declare module BABYLON {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: BABYLON.Scene);
         shadowColor: BABYLON.Color3;
         needAlphaBlending(): boolean;
@@ -82371,6 +82762,7 @@ declare module BABYLON {
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         get activeLight(): BABYLON.IShadowLight;
         set activeLight(light: BABYLON.IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;

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

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

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

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 5 - 5
dist/preview release/inspector/babylon.inspector.bundle.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 372 - 54
dist/preview release/inspector/babylon.inspector.bundle.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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 {
+    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 {
         close: (event: any) => void;
         playOrPause: () => void;
@@ -619,13 +631,16 @@ declare module INSPECTOR {
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: BABYLON.Animation[];
         animationName: string;
+        animationType: string;
         animationTargetProperty: string;
         isOpen: boolean;
         selected: BABYLON.Animation;
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
+        currentValue: number;
         frameAxisLength: ICanvasAxis[];
+        flatTangent: boolean;
     }> {
         readonly _heightScale: number;
         readonly _canvasLength: number;
@@ -638,8 +653,11 @@ declare module INSPECTOR {
         constructor(props: IAnimationCurveEditorComponentProps);
         componentDidMount(): 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;
         addAnimation(): void;
+        addKeyframeClick(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {
@@ -648,6 +666,8 @@ declare module INSPECTOR {
         };
         getPathData(animation: BABYLON.Animation): string;
         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;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         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;
         isAnimationPlaying(): 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;
         changeCurrentFrame(frame: number): void;
+        setFlatTangent(): void;
         render(): JSX.Element;
     }
 }
@@ -1819,7 +1852,14 @@ declare module INSPECTOR {
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        private _snippetUrl;
         constructor(props: ISpriteManagerPropertyGridComponentProps);
+        addNewSprite(): void;
+        disposeManager(): void;
+        saveToFile(): void;
+        loadFromFile(file: File): void;
+        loadFromSnippet(): void;
+        saveToSnippet(): void;
         render(): JSX.Element;
     }
 }
@@ -1835,6 +1875,7 @@ declare module INSPECTOR {
         constructor(props: ISpritePropertyGridComponentProps);
         onManagerLink(): void;
         switchPlayStopState(): void;
+        disposeSprite(): void;
         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;
     }
 }
+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" {
     import * as React from "react";
     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, {
         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;
         readonly _canvasLength: number;
@@ -725,8 +741,11 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         constructor(props: IAnimationCurveEditorComponentProps);
         componentDidMount(): 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;
         addAnimation(): void;
+        addKeyframeClick(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: Vector2, index: number): void;
         getAnimationProperties(animation: Animation): {
@@ -735,6 +754,8 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         };
         getPathData(animation: Animation): string;
         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;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         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;
         isAnimationPlaying(): 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;
         changeCurrentFrame(frame: number): void;
+        setFlatTangent(): void;
         render(): JSX.Element;
     }
 }
@@ -2317,7 +2351,14 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/spr
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
     }
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        private _snippetUrl;
         constructor(props: ISpriteManagerPropertyGridComponentProps);
+        addNewSprite(): void;
+        disposeManager(): void;
+        saveToFile(): void;
+        loadFromFile(file: File): void;
+        loadFromSnippet(): void;
+        saveToSnippet(): void;
         render(): JSX.Element;
     }
 }
@@ -2339,6 +2380,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/spr
         constructor(props: ISpritePropertyGridComponentProps);
         onManagerLink(): void;
         switchPlayStopState(): void;
+        disposeSprite(): void;
         render(): JSX.Element;
     }
 }
@@ -3649,6 +3691,18 @@ 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 {
         close: (event: any) => void;
         playOrPause: () => void;
@@ -3664,13 +3718,16 @@ declare module INSPECTOR {
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: BABYLON.Animation[];
         animationName: string;
+        animationType: string;
         animationTargetProperty: string;
         isOpen: boolean;
         selected: BABYLON.Animation;
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
+        currentValue: number;
         frameAxisLength: ICanvasAxis[];
+        flatTangent: boolean;
     }> {
         readonly _heightScale: number;
         readonly _canvasLength: number;
@@ -3683,8 +3740,11 @@ declare module INSPECTOR {
         constructor(props: IAnimationCurveEditorComponentProps);
         componentDidMount(): 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;
         addAnimation(): void;
+        addKeyframeClick(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {
@@ -3693,6 +3753,8 @@ declare module INSPECTOR {
         };
         getPathData(animation: BABYLON.Animation): string;
         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;
         renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
         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;
         isAnimationPlaying(): 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;
         changeCurrentFrame(frame: number): void;
+        setFlatTangent(): void;
         render(): JSX.Element;
     }
 }
@@ -4864,7 +4939,14 @@ declare module INSPECTOR {
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
     }
     export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        private _snippetUrl;
         constructor(props: ISpriteManagerPropertyGridComponentProps);
+        addNewSprite(): void;
+        disposeManager(): void;
+        saveToFile(): void;
+        loadFromFile(file: File): void;
+        loadFromSnippet(): void;
+        saveToSnippet(): void;
         render(): JSX.Element;
     }
 }
@@ -4880,6 +4962,7 @@ declare module INSPECTOR {
         constructor(props: ISpritePropertyGridComponentProps);
         onManagerLink(): void;
         switchPlayStopState(): void;
+        disposeSprite(): void;
         render(): JSX.Element;
     }
 }

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.2.0-alpha.15",
+    "version": "4.2.0-alpha.16",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,12 +29,12 @@
     ],
     "license": "Apache-2.0",
     "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": {
         "@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) {
         var _this = this;
         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) {
-                            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;
                 });
             });

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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) {
         var _this = this;
         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) {
-                            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;
                 });
             });

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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) {
         var _this = this;
         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) {
-                            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;
                 });
             });

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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",
     "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": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "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": {
         "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);
     function ShadowOnlyMaterial(name, scene) {
         var _this = _super.call(this, name, scene) || this;
+        _this._needAlphaBlending = true;
         _this.shadowColor = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Color3"].Black();
         return _this;
     }
     ShadowOnlyMaterial.prototype.needAlphaBlending = function () {
-        return true;
+        return this._needAlphaBlending;
     };
     ShadowOnlyMaterial.prototype.needAlphaTesting = function () {
         return false;
@@ -541,8 +542,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         enumerable: 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
     ShadowOnlyMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
+        var _a;
         if (this.isFrozen) {
             if (subMesh.effect && subMesh.effect._wasPreviouslyReady) {
                 return true;
@@ -559,8 +570,8 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         var engine = scene.getEngine();
         // Ensure that active light is the first shadow light
         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 (this._activeLight === light) {
                         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"].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);
+        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
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForAttributes(mesh, defines, false, true);
         // Get correct effect
@@ -664,9 +681,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         // Lights
         if (scene.lightsEnabled) {
             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
-        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());
         }
         // Fog

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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 {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: BABYLON.Scene);
         shadowColor: BABYLON.Color3;
         needAlphaBlending(): boolean;
@@ -566,6 +567,7 @@ declare module BABYLON {
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         get activeLight(): BABYLON.IShadowLight;
         set activeLight(light: BABYLON.IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         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);
     function ShadowOnlyMaterial(name, scene) {
         var _this = _super.call(this, name, scene) || this;
+        _this._needAlphaBlending = true;
         _this.shadowColor = babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["Color3"].Black();
         return _this;
     }
     ShadowOnlyMaterial.prototype.needAlphaBlending = function () {
-        return true;
+        return this._needAlphaBlending;
     };
     ShadowOnlyMaterial.prototype.needAlphaTesting = function () {
         return false;
@@ -4705,8 +4706,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         enumerable: 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
     ShadowOnlyMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
+        var _a;
         if (this.isFrozen) {
             if (subMesh.effect && subMesh.effect._wasPreviouslyReady) {
                 return true;
@@ -4723,8 +4734,8 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         var engine = scene.getEngine();
         // Ensure that active light is the first shadow light
         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 (this._activeLight === light) {
                         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"].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);
+        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
         babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__["MaterialHelper"].PrepareDefinesForAttributes(mesh, defines, false, true);
         // Get correct effect
@@ -4828,9 +4845,18 @@ var ShadowOnlyMaterial = /** @class */ (function (_super) {
         // Lights
         if (scene.lightsEnabled) {
             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
-        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());
         }
         // Fog

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/materialsLibrary/babylonjs.materials.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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";
     export class ShadowOnlyMaterial extends PushMaterial {
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: Scene);
         shadowColor: Color3;
         needAlphaBlending(): boolean;
@@ -886,6 +887,7 @@ declare module "babylonjs-materials/shadowOnly/shadowOnlyMaterial" {
         getAlphaTestTexture(): Nullable<BaseTexture>;
         get activeLight(): IShadowLight;
         set activeLight(light: IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;
@@ -2126,6 +2128,7 @@ declare module BABYLON {
 declare module BABYLON {
     export class ShadowOnlyMaterial extends BABYLON.PushMaterial {
         private _activeLight;
+        private _needAlphaBlending;
         constructor(name: string, scene: BABYLON.Scene);
         shadowColor: BABYLON.Color3;
         needAlphaBlending(): boolean;
@@ -2133,6 +2136,7 @@ declare module BABYLON {
         getAlphaTestTexture(): BABYLON.Nullable<BABYLON.BaseTexture>;
         get activeLight(): BABYLON.IShadowLight;
         set activeLight(light: BABYLON.IShadowLight);
+        private _getFirstShadowLightForMesh;
         isReadyForSubMesh(mesh: BABYLON.AbstractMesh, subMesh: BABYLON.SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: BABYLON.Matrix, mesh: BABYLON.Mesh, subMesh: BABYLON.SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;

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

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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;
                             if (!portAdded) {
                                 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);
                                 link.isVisible = true;
                                 var onLinkDisposedObserver = link.onDisposedObservable.add(function (nodeLink) {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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",
     "description": "The Babylon.js node material editor.",
-    "version": "4.2.0-alpha.15",
+    "version": "4.2.0-alpha.16",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.15"
+        "babylonjs": "4.2.0-alpha.16"
     },
     "files": [
         "babylon.nodeEditor.max.js.map",

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

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.2.0-alpha.15",
+    "version": "4.2.0-alpha.16",
     "repository": {
         "type": "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",
     "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": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.15"
+        "babylonjs": "4.2.0-alpha.16"
     },
     "engines": {
         "node": "*"

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

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

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

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 865 - 72
dist/preview release/viewer/babylon.module.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 88 - 80
dist/preview release/viewer/babylon.viewer.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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 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 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))
 
 ## Updates
@@ -31,6 +31,7 @@
 - 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))
 - 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
 
@@ -39,6 +40,7 @@
 - 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))
 - 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
 
@@ -194,6 +196,7 @@
 - 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 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
 

+ 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 { Animation } from 'babylonjs/Animations/animation';
 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 { IKeyframeSvgPoint } from './keyframeSvgPoint';
 import { SvgDraggableArea } from './svgDraggableArea';
 import { Timeline } from './timeline';
 import { Playhead } from './playhead';
+import { GraphActionsBar } from './graphActionsBar';
 import { Scene } from "babylonjs/scene";
 import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
@@ -29,7 +30,7 @@ interface ICanvasAxis {
     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 _canvasLength: number = 20;
@@ -42,7 +43,20 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     constructor(props: IAnimationCurveEditorComponentProps) {
         super(props);
         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() {
@@ -56,6 +70,16 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         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>) {
         event.preventDefault();
         this.setState({ animationTargetProperty: event.target.value });
@@ -63,7 +87,8 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
     addAnimation() {
         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 = [];
             keys.push({
@@ -76,59 +101,76 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                 value: 1
             });
 
-
             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
             (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>) {
 
         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) {
@@ -155,10 +197,10 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                 keyValue = 1 - ((100 / k.keyframePoint.y) * .1)
             }
 
-
             keys.push({ frame: k.keyframePoint.x, value: keyValue })
             return k;
         });
+
         anim.setKeys(keys);
 
         this.setState({ svgKeyframes: svgKeyframes })
@@ -183,7 +225,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
         const keyframes = animation.getKeys();
 
-
         if (keyframes === undefined) {
             return "";
         }
@@ -196,12 +237,22 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         // START OF LINE/CURVE
         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 {
-            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;
@@ -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) {
 
         // 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) {
 
         let animation = this.state.selected as Animation;
+        // Bug: After play/stop we get an extra keyframe at 0
 
         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 {
 
         let a = 0.0;
@@ -417,6 +599,11 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         this.setState({ currentFrame: frame });
     }
 
+    setFlatTangent() {
+        this.setState({ flatTangent: !this.state.flatTangent }, () => this.selectAnimation(this.state.selected));
+        ;
+    }
+
     render() {
         return (
             <div id="animation-curve-editor">
@@ -426,6 +613,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                         <FontAwesomeIcon icon={faTimes} />
                     </div>
                 </div>
+                <GraphActionsBar currentValue={this.state.currentValue} handleValueChange={(e) => this.handleValueChange(e)} addKeyframe={() => this.addKeyframeClick()} flatTangent={() => this.setFlatTangent()} />
                 <div className="content">
 
                     <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>
                                 </div>
                                 <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>
                                     <input type="text" value={this.state.animationTargetProperty} onChange={(e) => this.handlePropertyChange(e)}></input>
                                 </div>
@@ -497,7 +698,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                                     <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
                                         <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
                                     </svg>
-
                                 )}
 
                             </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
                                     id="curve-editor"
                                     title="Curve Animation Editor"
-                                    size={{ width: 950, height: 512 }}
+                                    size={{ width: 950, height: 540 }}
                                     onOpen={(window: Window) => { window.console.log("Window opened!!") }}
                                     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{
         display: flex;
         align-items: flex-start;
@@ -34,7 +54,7 @@
             justify-content: flex-start;
             flex-direction: row;
             width: 100vw;
-            height: 85vh;
+            height: 78.5vh;
 
             .timeline{
                 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 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()} />
                 </LineContainerComponent>
                 <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 { RenderingManager } from 'babylonjs/Rendering/renderingManager';
 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 {
     globalState: GlobalState;
@@ -24,10 +28,131 @@ interface ISpriteManagerPropertyGridComponentProps {
 }
 
 export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+    private _snippetUrl = "https://snippet.babylonjs.com";
+
+
     constructor(props: ISpriteManagerPropertyGridComponentProps) {
         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() {
         const spriteManager = this.props.spriteManager;
 
@@ -36,9 +161,26 @@ export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteM
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={spriteManager} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
                     <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}/>
+                    {
+                        spriteManager.sprites.length < spriteManager.capacity &&
+                        <ButtonLineComponent label="Add new sprite" onClick={() => this.addNewSprite()} />
+                    }
+                    <ButtonLineComponent label="Dispose" onClick={() => this.disposeManager()} />
                 </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">
                     <CheckBoxLineComponent label="Pickable" target={spriteManager} propertyName="isPickable" 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();
     }
 
+    disposeSprite() {
+        const sprite = this.props.sprite;
+        sprite.dispose();
+
+        this.props.globalState.onCodeChangedObservable.notifyObservers({
+            object: sprite,
+            code: `TARGET.dispose();`
+        });
+
+        this.props.onSelectionChangedObservable?.notifyObservers(null);
+    }
+
     render() {
         const sprite = this.props.sprite;
         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="Link to manager" value={manager.name} onLink={() => this.onManagerLink()} />
                     <CheckBoxLineComponent label="Visible" target={sprite} propertyName="isVisible" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <ButtonLineComponent label="Dispose" onClick={() => this.disposeSprite()} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="PROPERTIES">
                     <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({
             label: "Add new sprite manager",
             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);
             }
         });            

+ 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 { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { INode } from "../glTFLoaderInterfaces";
+import { AbstractMesh } from 'babylonjs/Meshes/abstractMesh';
 
 const NAME = "EXT_mesh_gpu_instancing";
 
@@ -47,61 +48,66 @@ export class EXT_mesh_gpu_instancing implements IGLTFLoaderExtension {
     /** @hidden */
     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 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) {
-                            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;
                 });
             });

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

@@ -18,6 +18,7 @@ import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import "./shadowOnly.fragment";
 import "./shadowOnly.vertex";
 import { EffectFallbacks } from 'babylonjs/Materials/effectFallbacks';
+import { CascadedShadowGenerator } from 'babylonjs/Lights/Shadows/cascadedShadowGenerator';
 
 class ShadowOnlyMaterialDefines extends MaterialDefines {
     public CLIPPLANE = false;
@@ -41,6 +42,7 @@ class ShadowOnlyMaterialDefines extends MaterialDefines {
 
 export class ShadowOnlyMaterial extends PushMaterial {
     private _activeLight: IShadowLight;
+    private _needAlphaBlending = true;
 
     constructor(name: string, scene: Scene) {
         super(name, scene);
@@ -49,7 +51,7 @@ export class ShadowOnlyMaterial extends PushMaterial {
     public shadowColor = Color3.Black();
 
     public needAlphaBlending(): boolean {
-        return true;
+        return this._needAlphaBlending;
     }
 
     public needAlphaTesting(): boolean {
@@ -68,6 +70,15 @@ export class ShadowOnlyMaterial extends PushMaterial {
         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
     public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {
         if (this.isFrozen) {
@@ -114,6 +125,16 @@ export class ShadowOnlyMaterial extends PushMaterial {
 
         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
         MaterialHelper.PrepareDefinesForAttributes(mesh, defines, false, true);
 
@@ -226,10 +247,21 @@ export class ShadowOnlyMaterial extends PushMaterial {
         // Lights
         if (scene.lightsEnabled) {
             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
-        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());
         }
 

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

@@ -139,7 +139,7 @@ export class GraphFrame {
 
                             if (!portAdded) {
                                 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);
 
                                 link.isVisible = true;

+ 1 - 1
package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.2.0-alpha.15",
+    "version": "4.2.0-alpha.16",
     "repository": {
         "type": "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;
 
                 if (currentScene.meshes.length) {
-                    var worldExtends = currentScene.getWorldExtends();
                     currentScene.activeCamera.lowerRadiusLimit = null;
+
+                    var worldExtends = currentScene.getWorldExtends(function (mesh) {
+                        return mesh.isVisible && mesh.isEnabled();
+                    });
                     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
      * @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 (this._firstDevice[deviceType] === undefined) {
                 return null;

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

@@ -5,4 +5,5 @@ export * from "./shaderCodeCursor";
 export * from "./shaderCodeNode";
 export * from "./shaderCodeTestNode";
 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;
     bindVertexArray(vertexArray: any): void;
 
-    createIndexBuffer(data: ArrayBufferView): any;
+    createIndexBuffer(data: ArrayBufferView, dynamic: boolean): any;
     deleteIndexBuffer(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;
     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;
     getUniforms(shaderProgram: any, uniformsNames: string[]): WebGLUniformLocation[];
@@ -334,22 +336,22 @@ export class NativeEngine extends Engine {
         this._native.clear(mode);
     }
 
-    public createIndexBuffer(indices: IndicesArray): NativeDataBuffer {
+    public createIndexBuffer(indices: IndicesArray, updateable?: boolean): NativeDataBuffer {
         const data = this._normalizeIndexData(indices);
         const buffer = new NativeDataBuffer();
         buffer.references = 1;
         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) {
             throw new Error("Could not create a native index buffer.");
         }
         return buffer;
     }
 
-    public createVertexBuffer(data: DataArray): NativeDataBuffer {
+    public createVertexBuffer(data: DataArray, updateable?: boolean): NativeDataBuffer {
         const buffer = new NativeDataBuffer();
         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) {
             throw new Error("Could not create a native vertex buffer.");
         }
@@ -372,7 +374,15 @@ export class NativeEngine extends Engine {
                 if (vertexBuffer) {
                     const buffer = vertexBuffer.getBuffer() as Nullable<NativeDataBuffer>;
                     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 {
-        throw new Error("createDynamicVertexBuffer not yet implemented.");
+        return this.createVertexBuffer(data, true);
     }
 
     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)
      */
     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.

+ 2 - 2
src/Engines/thinEngine.ts

@@ -132,14 +132,14 @@ export class ThinEngine {
      */
     // Not mixed with Version for tooling purpose.
     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
      */
     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
     if (!this.activeCamera) {
-        var worldExtends = this.getWorldExtends();
+        var worldExtends = this.getWorldExtends((mesh) => mesh.isVisible && mesh.isEnabled());
         var worldSize = worldExtends.max.subtract(worldExtends.min);
         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
-        var eventPrefix = Tools.GetPointerPrefix();
+        var eventPrefix = Tools.GetPointerPrefix(engine);
 
         if (attachMove) {
             elementToAttachTo.addEventListener(eventPrefix + "move", <any>this._onPointerMove, false);
@@ -823,9 +823,9 @@ export class InputManager {
      * Detaches all event handlers
      */
     public detachControl() {
-        const eventPrefix = Tools.GetPointerPrefix();
         const canvas = this._scene.getEngine().getInputElement();
         const engine = this._scene.getEngine();
+        const eventPrefix = Tools.GetPointerPrefix(engine);
 
         if (!canvas) {
             return;

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

@@ -640,6 +640,30 @@ export abstract class PBRBaseMaterial extends PushMaterial {
      */
     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.
      */
@@ -1184,7 +1208,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
         var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vMetallicReflectanceFactors", "vEmissiveColor", "visibility", "vReflectionColor",
             "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",
             "mBones",
             "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",
             "vReflectionMicrosurfaceInfos",
             "vTangentSpaceParams", "boneTextureWidth",
-            "vDebugMode",
-            "vFilteringInfo", "linearRoughness"
+            "vDebugMode"
         ];
 
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
@@ -1302,8 +1325,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
                     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;
                     } else {
                         defines.REALTIME_FILTERING = false;
@@ -1367,7 +1394,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         else if (reflectionTexture.isCube) {
                             defines.USESPHERICALFROMREFLECTIONMAP = true;
                             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;
                             }
                             else {
@@ -1588,6 +1615,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         ubo.addUniform("vReflectivityInfos", 3);
         ubo.addUniform("vMicroSurfaceSamplerInfos", 2);
         ubo.addUniform("vReflectionInfos", 2);
+        ubo.addUniform("vReflectionFilteringInfo", 2);
         ubo.addUniform("vReflectionPosition", 3);
         ubo.addUniform("vReflectionSize", 3);
         ubo.addUniform("vBumpInfos", 3);
@@ -1723,12 +1751,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize);
                         }
 
-                        if (reflectionTexture.realTimeFiltering) {
+                        if (this.realTimeFiltering) {
                             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) {
@@ -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.anisotropy.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 { MaterialHelper } from "../../Materials/materialHelper";
 import { EffectFallbacks } from '../effectFallbacks';
+import { Scalar } from "../../Maths/math.scalar";
 
 declare type Engine = import("../../Engines/engine").Engine;
 declare type Scene = import("../../scene").Scene;
@@ -290,10 +291,11 @@ export class PBRSubSurfaceConfiguration {
      * @param uniformBuffer defines the Uniform buffer to fill in.
      * @param scene defines the scene 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);
 
         if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
@@ -313,11 +315,18 @@ export class PBRSubSurfaceConfiguration {
                         depth = (<any>refractionTexture).depth;
                     }
                 }
+
+                var width = refractionTexture.getSize().width;
+
                 uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, 1 / this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
                 uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos",
-                    refractionTexture.getSize().width,
+                    width,
                     refractionTexture.lodGenerationScale,
                     refractionTexture.lodGenerationOffset);
+
+                if (realTimeFiltering) {
+                    uniformBuffer.updateFloat2("vRefractionFilteringInfo", width, Scalar.Log2(width));
+                }
             }
 
             uniformBuffer.updateColor3("vDiffusionDistance", this.diffusionDistance);
@@ -503,7 +512,7 @@ export class PBRSubSurfaceConfiguration {
     public static AddUniforms(uniforms: string[]): void {
         uniforms.push(
             "vDiffusionDistance", "vTintColor", "vSubSurfaceIntensity",
-            "vRefractionMicrosurfaceInfos",
+            "vRefractionMicrosurfaceInfos", "vRefractionFilteringInfo",
             "vRefractionInfos", "vThicknessInfos", "vThicknessParam",
             "refractionMatrix", "thicknessMatrix");
     }
@@ -523,6 +532,7 @@ export class PBRSubSurfaceConfiguration {
      */
     public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
         uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
+        uniformBuffer.addUniform("vRefractionFilteringInfo", 2);
         uniformBuffer.addUniform("vRefractionInfos", 4);
         uniformBuffer.addUniform("refractionMatrix", 16);
         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.fragment";
+import { Logger } from '../../../Misc/logger';
 
 /**
  * Options for texture filtering
@@ -133,7 +134,7 @@ export class HDRFiltering {
                     alpha = 0;
                 }
 
-                effect.setFloat("linearRoughness", alpha);
+                effect.setFloat("alphaG", alpha);
 
                 this._effectRenderer.draw();
             }
@@ -164,7 +165,7 @@ export class HDRFiltering {
             vertexShader: "hdrFiltering",
             fragmentShader: "hdrFiltering",
             samplerNames: ["inputTexture"],
-            uniformNames: ["vSampleDirections", "vWeights", "up", "right", "front", "vFilteringInfo", "hdrScale", "linearRoughness"],
+            uniformNames: ["vSampleDirections", "vWeights", "up", "right", "front", "vFilteringInfo", "hdrScale", "alphaG"],
             useShaderStore: true,
             defines,
             onCompiled: onCompiled
@@ -192,6 +193,11 @@ export class HDRFiltering {
       * @return Promise called when prefiltering is done
       */
     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) => {
             this._effectRenderer = new EffectRenderer(this._engine);
             this._effectWrapper = this._createEffect(texture);

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

@@ -345,36 +345,6 @@ export class BaseTexture implements IAnimatable {
         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.
      */

+ 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
      * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
      * @return {boolean} is it ready

+ 7 - 1
src/Misc/tools.ts

@@ -281,9 +281,10 @@ export class Tools {
 
     /**
      * 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"
      */
-    public static GetPointerPrefix(): string {
+    public static GetPointerPrefix(engine: Engine): string {
         var eventPrefix = "pointer";
 
         // Check if pointer events are supported
@@ -291,6 +292,11 @@ export class Tools {
             eventPrefix = "mouse";
         }
 
+        // Special Fallback...
+        if (engine._badDesktopOS) {
+            eventPrefix = "mouse";
+        }
+
         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.realTimeFiltering = true;
-
         this._renderTargetTexture.onBeforeRenderObservable.add((faceIndex: number) => {
             switch (faceIndex) {
                 case 0:

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

@@ -14,6 +14,7 @@
 	#endif
 
 	#ifdef BONETEXTURE
+        #define inline
 		mat4 readMatrixFromRawSampler(sampler2D smp, float index)
 		{
 			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_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 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
 			tangent = normalize(cross(tangent, n));
 			vec3 bitangent = cross(n, tangent);
 			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);
 
+		    #ifdef WEBGL2
 		    for(uint i = 0u; i < NUM_SAMPLES; ++i)
+		    #else
+		    for(int i = 0; i < NUM_SAMPLES; ++i)
+		    #endif
 		    {
 		        vec2 Xi = hammersley(i, NUM_SAMPLES);
 		        vec3 Ls = hemisphereCosSample(Xi);
@@ -166,8 +168,10 @@
 		    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;
 				#ifdef GAMMA_INPUT
 				    c = toLinearSpace(c);
@@ -181,15 +185,19 @@
 			vec3 bitangent = cross(n, tangent);
 			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 weight = 0.;
+			#ifdef WEBGL2
 			for(uint i = 0u; i < NUM_SAMPLES; ++i)
+			#else
+			for(int i = 0; i < NUM_SAMPLES; ++i)
+			#endif
 			{
 			    vec2 Xi = hammersley(i, NUM_SAMPLES);
-			    vec3 H = hemisphereImportanceSampleDggx(Xi, linearRoughness);
+			    vec3 H = hemisphereImportanceSampleDggx(Xi, alphaG);
 
 			    float NoV = 1.;
 			    float NoH = H.z;
@@ -199,7 +207,7 @@
 			    L = normalize(L);
 
 			    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 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));
     }
 #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
 
 float log4(float x) {

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

@@ -4,6 +4,7 @@
 	* sampler3dSetting.x = textureOffset (0.5 / textureSize).
 	* sampler3dSetting.y = textureSize.
 	*/
+    #define inline
 	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

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

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

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

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

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

@@ -52,6 +52,7 @@
         #endif
     }
 
+    #define inline
     void sampleReflectionTexture(
         const in float alphaG,
         const in vec3 vReflectionMicrosurfaceInfos,
@@ -111,8 +112,15 @@
             #else
                 float requestedReflectionLOD = reflectionLOD;
             #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
             float lodReflectionNormalized = saturate(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x));
             float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
@@ -146,6 +154,7 @@
         environmentRadiance.rgb *= vReflectionColor.rgb;
     }
 
+    #define inline
     void reflectionBlock(
         const in vec3 vPositionW,
         const in vec3 normalW,
@@ -254,8 +263,12 @@
                     irradianceVector.z *= -1.0;
                 #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
                     environmentIrradiance = computeEnvironmentIrradiance(irradianceVector);
                 #endif

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

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

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

@@ -25,6 +25,7 @@ struct subSurfaceOutParams
 };
 
 #ifdef SUBSURFACE
+    #define inline
     void subSurfaceBlock(
         const in vec3 vSubSurfaceIntensity,
         const in vec2 vThicknessParam,
@@ -202,7 +203,15 @@ struct subSurfaceOutParams
                 float requestedRefractionLOD = refractionLOD;
             #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
             float lodRefractionNormalized = saturate(refractionLOD / log2(vRefractionMicrosurfaceInfos.x));
             float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
@@ -300,12 +309,19 @@ struct subSurfaceOutParams
             #ifdef REFLECTIONMAP_OPPOSITEZ
                 irradianceVector.z *= -1.0;
             #endif
+            #ifdef INVERTCUBICMAP
+                irradianceVector.y *= -1.0;
+            #endif
         #else
             vec3 irradianceVector = irradianceVector_;
         #endif
 
         #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)
             #ifdef REFLECTIONMAP_3D
                 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;
 }
 
+#define inline
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
     vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
     strq /= strq.w;

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

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

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

@@ -155,11 +155,7 @@
         uniform samplerCube reflectionSampler;
         
         #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
             uniform samplerCube reflectionSamplerLow;
             uniform samplerCube reflectionSamplerHigh;

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

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

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

@@ -14,6 +14,7 @@
         return mix(value, 1.0, mask);
     }
 
+    #define inline
     float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, vec2 depthValues)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -30,13 +31,10 @@
             float shadow = textureCube(shadowSampler, directionToLight).x;
         #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)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -72,6 +70,7 @@
         return  min(1.0, visibility + darkness);
     }
 
+    #define inline
     float computeShadowWithESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -92,6 +91,7 @@
         return esm;
     }
 
+    #define inline
     float computeShadowWithCloseESMCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float depthScale, vec2 depthValues)
     {
         vec3 directionToLight = vPositionW - lightPosition;
@@ -114,6 +114,7 @@
     }
 
     #ifdef WEBGL2
+        #define inline
         float computeShadowCSM(float layer, vec4 vPositionFromLight, float depthMetric, highp sampler2DArray shadowSampler, float darkness, float frustumEdgeFalloff)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -128,14 +129,11 @@
                 float shadow = texture2D(shadowSampler, uvLayer).x;
             #endif
 
-            if (shadowPixelDepth > shadow)
-            {
-                return computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);
-            }
-            return 1.;
+            return shadowPixelDepth > shadow ? computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff) : 1.;
         }
     #endif
 
+    #define inline
     float computeShadow(vec4 vPositionFromLight, float depthMetric, sampler2D shadowSampler, float darkness, float frustumEdgeFalloff)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -145,22 +143,21 @@
         {
             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)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -170,34 +167,37 @@
         {
             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)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -207,20 +207,23 @@
         {
             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)
     {
         vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -230,24 +233,27 @@
         {
             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
         #define GREATEST_LESS_THAN_ONE 0.99999994
 
         // 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)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -265,6 +271,7 @@
         // 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
         // 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)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -301,6 +308,7 @@
         // 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
         // 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)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -340,99 +348,108 @@
         }
 
         // 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)
         {
             if (depthMetric > 1.0 || depthMetric < 0.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)
         // 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/
+        #define inline
         float computeShadowWithPCF3(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
             if (depthMetric > 1.0 || depthMetric < 0.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)
         // 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/
+        #define inline
         float computeShadowWithPCF5(vec4 vPositionFromLight, float depthMetric, sampler2DShadow shadowSampler, vec2 shadowMapSizeAndInverse, float darkness, float frustumEdgeFalloff)
         {
             if (depthMetric > 1.0 || depthMetric < 0.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](
@@ -575,6 +592,7 @@
         // 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
         // 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)
         {
             vec3 clipSpace = vPositionFromLight.xyz / vPositionFromLight.w;
@@ -598,36 +616,39 @@
             if (numBlocker < 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
@@ -635,86 +656,98 @@
         // 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
         // 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)
         {
             if (depthMetric > 1.0 || depthMetric < 0.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)
         {
             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)
         {
             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)
         {
             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)
         {
             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)
         {
             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)
         {
             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<hdrFilteringFunctions>
 
+uniform float alphaG;
 uniform samplerCube inputTexture;
+uniform vec2 vFilteringInfo;
 uniform float hdrScale;
 
 varying vec3 direction;
 
 void main() {
-    vec3 color = radiance(inputTexture, direction);
+    vec3 color = radiance(alphaG, inputTexture, direction, vFilteringInfo);
 
     gl_FragColor = vec4(color * hdrScale, 1.0);
 }

+ 69 - 3
src/Sprites/sprite.ts

@@ -1,7 +1,7 @@
 import { Vector3 } from "../Maths/math.vector";
 import { Nullable } from "../types";
 import { ActionManager } from "../Actions/actionManager";
-import { ISpriteManager } from "./spriteManager";
+import { ISpriteManager, SpriteManager } from "./spriteManager";
 import { Color4 } from '../Maths/math.color';
 import { Observable } from '../Misc/observable';
 import { IAnimatable } from '../Animations/animatable.interface';
@@ -66,7 +66,7 @@ export class Sprite implements IAnimatable {
     private _direction = 1;
     private _manager: ISpriteManager;
     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
      */
@@ -172,7 +172,7 @@ export class Sprite implements IAnimatable {
      * @param delay defines the start delay (in ms)
      * @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._toIndex = to;
         this._loopAnimation = loop;
@@ -237,4 +237,70 @@ export class Sprite implements IAnimatable {
         this.onDisposeObservable.notifyObservers(this);
         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 { DataBuffer } from '../Meshes/dataBuffer';
 import { Engine } from '../Engines/engine';
+import { WebRequest } from '../Misc/webRequest';
 declare type Ray = import("../Culling/ray").Ray;
 
 /**
@@ -98,6 +99,12 @@ export interface ISpriteManager extends IDisposable {
  * @see http://doc.babylonjs.com/babylon101/sprites
  */
 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 */
     public sprites = new Array<Sprite>();
     /** 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() {
         return this._capacity;
     }
 
-    public set capacity(value: number) {
-        this._capacity = value;
-    }
-
     /**
      * Gets or sets the spritesheet texture
      */
@@ -192,6 +195,8 @@ export class SpriteManager implements ISpriteManager {
 
     public set texture(value: Texture) {
         this._spriteTexture = value;
+        this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
+        this._spriteTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
         this._textureContent = null;
     }
 
@@ -233,9 +238,12 @@ export class SpriteManager implements ISpriteManager {
         }
         this._capacity = capacity;
         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) {
             this.cellWidth = cellSize.width;
@@ -757,4 +765,92 @@ export class SpriteManager implements ISpriteManager {
         this.onDisposeObservable.notifyObservers(this);
         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",
             "playgroundId": "#PXC9CF#2",
             "referenceImage": "visibility.png"
+        },
+        {
+            "title": "GLTF Extension EXT_mesh_gpu_instancing",
+            "playgroundId": "#QFIGLW#9",
+            "referenceImage": "gltfExtensionExtMeshGpuInstancingTest.png"
         }
     ]
 }