瀏覽代碼

Merge remote-tracking branch 'upstream/master'

MackeyK24 6 年之前
父節點
當前提交
33bf359134
共有 39 個文件被更改,包括 877 次插入764 次删除
  1. 17 20
      Playground/babylon.d.txt
  2. 11 16
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 359 281
      dist/preview release/babylon.max.js
  5. 1 1
      dist/preview release/babylon.max.js.map
  6. 22 32
      dist/preview release/babylon.module.d.ts
  7. 1 1
      dist/preview release/glTF2Interface/package.json
  8. 6 4
      dist/preview release/gui/babylon.gui.d.ts
  9. 21 9
      dist/preview release/gui/babylon.gui.js
  10. 1 1
      dist/preview release/gui/babylon.gui.js.map
  11. 1 1
      dist/preview release/gui/babylon.gui.min.js
  12. 12 8
      dist/preview release/gui/babylon.gui.module.d.ts
  13. 2 2
      dist/preview release/gui/package.json
  14. 6 6
      dist/preview release/inspector/package.json
  15. 3 3
      dist/preview release/loaders/package.json
  16. 2 2
      dist/preview release/materialsLibrary/package.json
  17. 1 1
      dist/preview release/package.json
  18. 1 1
      dist/preview release/packagesSizeBaseLine.json
  19. 2 2
      dist/preview release/postProcessesLibrary/package.json
  20. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  21. 3 3
      dist/preview release/serializers/package.json
  22. 22 32
      dist/preview release/viewer/babylon.module.d.ts
  23. 5 19
      dist/preview release/viewer/babylon.viewer.d.ts
  24. 23 23
      dist/preview release/viewer/babylon.viewer.js
  25. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  26. 5 22
      dist/preview release/viewer/babylon.viewer.module.d.ts
  27. 3 1
      dist/preview release/what's new.md
  28. 9 3
      gui/src/2D/controls/colorpicker.ts
  29. 3 3
      gui/src/2D/controls/control.ts
  30. 2 2
      gui/src/2D/controls/inputText.ts
  31. 10 3
      gui/src/2D/controls/sliders/baseSlider.ts
  32. 2 2
      package.json
  33. 2 2
      src/Animations/runtimeAnimation.ts
  34. 17 2
      src/Cameras/camera.ts
  35. 2 2
      src/Engines/Extensions/engine.multiview.ts
  36. 3 3
      src/Engines/engine.ts
  37. 12 0
      src/Gizmos/planeRotationGizmo.ts
  38. 2 2
      src/Materials/effect.ts
  39. 279 245
      src/Meshes/Compression/dracoCompression.ts

+ 17 - 20
Playground/babylon.d.txt

@@ -29958,9 +29958,10 @@ declare module BABYLON {
          * Checks if a cullable object (mesh...) is in the camera frustum
          * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
          * @param target The object to check
+         * @param checkRigCameras If the rig cameras should be checked (eg. with webVR camera both eyes should be checked) (Default: false)
          * @returns true if the object is in frustum otherwise false
          */
-        isInFrustum(target: ICullable): boolean;
+        isInFrustum(target: ICullable, checkRigCameras?: boolean): boolean;
         /**
          * Checks if a cullable object (mesh...) is in the camera frustum
          * Unlike isInFrustum this cheks the full bounding box
@@ -34410,6 +34411,7 @@ declare module BABYLON {
         wheelDeltaPercentage: number;
         private _wheel;
         private _observer;
+        private computeDeltaFromMouseWheelLegacyEvent;
         /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
@@ -48978,7 +48980,7 @@ declare module BABYLON {
         /**
          * Configuration for the decoder.
          */
-        decoder?: {
+        decoder: {
             /**
              * The url to the WebAssembly module.
              */
@@ -49019,20 +49021,18 @@ declare module BABYLON {
      *
      * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
      * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
-     * Use `DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+     * Use `DracoCompression.DecoderAvailable` to determine if the decoder configuration is available for the current context.
      *
-     * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+     * To decode Draco compressed data, get the default DracoCompression object and call decodeMeshAsync:
      * ```javascript
-     *     var dracoCompression = new DracoCompression();
-     *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
-     *         [VertexBuffer.PositionKind]: 0
-     *     });
+     *     var vertexData = await DracoCompression.Default.decodeMeshAsync(data);
      * ```
      *
      * @see https://www.babylonjs-playground.com/#N3EK4B#0
      */
     export class DracoCompression implements IDisposable {
-        private _workerPoolPromise;
+        private _workerPoolPromise?;
+        private _decoderModulePromise?;
         /**
          * The configuration. Defaults to the following urls:
          * - wasmUrl: "https://preview.babylonjs.com/draco_wasm_wrapper_gltf.js"
@@ -49041,7 +49041,7 @@ declare module BABYLON {
          */
         static Configuration: IDracoCompressionConfiguration;
         /**
-         * Returns true if the decoder is available.
+         * Returns true if the decoder configuration is available.
          */
         static readonly DecoderAvailable: boolean;
         /**
@@ -49056,7 +49056,7 @@ declare module BABYLON {
         static readonly Default: DracoCompression;
         /**
          * Constructor
-         * @param numWorkers The number of workers for async operations
+         * @param numWorkers The number of workers for async operations. Specify `0` to disable web workers and run synchronously in the current context.
          */
         constructor(numWorkers?: number);
         /**
@@ -49077,11 +49077,6 @@ declare module BABYLON {
         decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes?: {
             [kind: string]: number;
         }): Promise<VertexData>;
-        /**
-         * The worker function that gets converted to a blob url to pass into a worker.
-         */
-        private static _Worker;
-        private _loadDecoderWasmBinaryAsync;
     }
 }
 declare module BABYLON {
@@ -57315,7 +57310,7 @@ declare module BABYLON.GUI {
          */
         contains(x: number, y: number): boolean;
         /** @hidden */
processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
-        /** @hidden */
onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        /** @hidden */
onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         /** @hidden */
onPointerEnter(target: Control): boolean;
         /** @hidden */
onPointerOut(target: Control, force?: boolean): void;
         /** @hidden */
onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
@@ -58125,7 +58120,7 @@ declare module BABYLON.GUI {
         /** @hidden */
         private _onCutText;
         /** @hidden */
-        private _onPasteText;
draw(context: CanvasRenderingContext2D): void;
onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
+        private _onPasteText;
draw(context: CanvasRenderingContext2D): void;
onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         protected _beforeRenderText(text: string): string;
         dispose(): void;
     }
@@ -58261,6 +58256,7 @@ declare module BABYLON.GUI {
         private _h;
         private _s;
         private _v;
+        private _lastPointerDownID;
         /**
          * BABYLON.Observable raised when the value changes
          */
@@ -58298,7 +58294,7 @@ declare module BABYLON.GUI {
         private _pointerIsDown;
         private _updateValueFromPointer;
         private _isPointOnSquare;
-        private _isPointOnWheel;
onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
+        private _isPointOnWheel;
onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         /**
          * This function expands the color picker by creating a color picker dialog with manual
          * color value input and the ability to save colors into an array to be used later in
@@ -58562,6 +58558,7 @@ declare module BABYLON.GUI {
         private _isThumbClamped;
         protected _displayThumb: boolean;
         private _step;
+        private _lastPointerDownID;
         protected _effectiveBarOffset: number;
         protected _renderLeft: number;
         protected _renderTop: number;
@@ -58605,7 +58602,7 @@ declare module BABYLON.GUI {
         protected _prepareRenderingData(type: string): void;
         private _pointerIsDown;
         /** @hidden */
-        protected _updateValueFromPointer(x: number, y: number): void;
onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
+        protected _updateValueFromPointer(x: number, y: number): void;
onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
     }
 }
 declare module BABYLON.GUI {

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

@@ -30528,9 +30528,10 @@ declare module BABYLON {
          * Checks if a cullable object (mesh...) is in the camera frustum
          * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
          * @param target The object to check
+         * @param checkRigCameras If the rig cameras should be checked (eg. with webVR camera both eyes should be checked) (Default: false)
          * @returns true if the object is in frustum otherwise false
          */
-        isInFrustum(target: ICullable): boolean;
+        isInFrustum(target: ICullable, checkRigCameras?: boolean): boolean;
         /**
          * Checks if a cullable object (mesh...) is in the camera frustum
          * Unlike isInFrustum this cheks the full bounding box
@@ -35058,6 +35059,7 @@ declare module BABYLON {
         wheelDeltaPercentage: number;
         private _wheel;
         private _observer;
+        private computeDeltaFromMouseWheelLegacyEvent;
         /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
@@ -49722,7 +49724,7 @@ declare module BABYLON {
         /**
          * Configuration for the decoder.
          */
-        decoder?: {
+        decoder: {
             /**
              * The url to the WebAssembly module.
              */
@@ -49763,20 +49765,18 @@ declare module BABYLON {
      *
      * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
      * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
-     * Use `DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+     * Use `DracoCompression.DecoderAvailable` to determine if the decoder configuration is available for the current context.
      *
-     * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+     * To decode Draco compressed data, get the default DracoCompression object and call decodeMeshAsync:
      * ```javascript
-     *     var dracoCompression = new DracoCompression();
-     *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
-     *         [VertexBuffer.PositionKind]: 0
-     *     });
+     *     var vertexData = await DracoCompression.Default.decodeMeshAsync(data);
      * ```
      *
      * @see https://www.babylonjs-playground.com/#N3EK4B#0
      */
     export class DracoCompression implements IDisposable {
-        private _workerPoolPromise;
+        private _workerPoolPromise?;
+        private _decoderModulePromise?;
         /**
          * The configuration. Defaults to the following urls:
          * - wasmUrl: "https://preview.babylonjs.com/draco_wasm_wrapper_gltf.js"
@@ -49785,7 +49785,7 @@ declare module BABYLON {
          */
         static Configuration: IDracoCompressionConfiguration;
         /**
-         * Returns true if the decoder is available.
+         * Returns true if the decoder configuration is available.
          */
         static readonly DecoderAvailable: boolean;
         /**
@@ -49800,7 +49800,7 @@ declare module BABYLON {
         static readonly Default: DracoCompression;
         /**
          * Constructor
-         * @param numWorkers The number of workers for async operations
+         * @param numWorkers The number of workers for async operations. Specify `0` to disable web workers and run synchronously in the current context.
          */
         constructor(numWorkers?: number);
         /**
@@ -49821,11 +49821,6 @@ declare module BABYLON {
         decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes?: {
             [kind: string]: number;
         }): Promise<VertexData>;
-        /**
-         * The worker function that gets converted to a blob url to pass into a worker.
-         */
-        private static _Worker;
-        private _loadDecoderWasmBinaryAsync;
     }
 }
 declare module BABYLON {

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


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


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


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

@@ -31324,9 +31324,10 @@ declare module "babylonjs/Cameras/camera" {
          * Checks if a cullable object (mesh...) is in the camera frustum
          * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
          * @param target The object to check
+         * @param checkRigCameras If the rig cameras should be checked (eg. with webVR camera both eyes should be checked) (Default: false)
          * @returns true if the object is in frustum otherwise false
          */
-        isInFrustum(target: ICullable): boolean;
+        isInFrustum(target: ICullable, checkRigCameras?: boolean): boolean;
         /**
          * Checks if a cullable object (mesh...) is in the camera frustum
          * Unlike isInFrustum this cheks the full bounding box
@@ -36022,6 +36023,7 @@ declare module "babylonjs/Cameras/Inputs/arcRotateCameraMouseWheelInput" {
         wheelDeltaPercentage: number;
         private _wheel;
         private _observer;
+        private computeDeltaFromMouseWheelLegacyEvent;
         /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
@@ -51952,7 +51954,7 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
         /**
          * Configuration for the decoder.
          */
-        decoder?: {
+        decoder: {
             /**
              * The url to the WebAssembly module.
              */
@@ -51993,20 +51995,18 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
      *
      * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
      * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
-     * Use `DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+     * Use `DracoCompression.DecoderAvailable` to determine if the decoder configuration is available for the current context.
      *
-     * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+     * To decode Draco compressed data, get the default DracoCompression object and call decodeMeshAsync:
      * ```javascript
-     *     var dracoCompression = new DracoCompression();
-     *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
-     *         [VertexBuffer.PositionKind]: 0
-     *     });
+     *     var vertexData = await DracoCompression.Default.decodeMeshAsync(data);
      * ```
      *
      * @see https://www.babylonjs-playground.com/#N3EK4B#0
      */
     export class DracoCompression implements IDisposable {
-        private _workerPoolPromise;
+        private _workerPoolPromise?;
+        private _decoderModulePromise?;
         /**
          * The configuration. Defaults to the following urls:
          * - wasmUrl: "https://preview.babylonjs.com/draco_wasm_wrapper_gltf.js"
@@ -52015,7 +52015,7 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
          */
         static Configuration: IDracoCompressionConfiguration;
         /**
-         * Returns true if the decoder is available.
+         * Returns true if the decoder configuration is available.
          */
         static readonly DecoderAvailable: boolean;
         /**
@@ -52030,7 +52030,7 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
         static readonly Default: DracoCompression;
         /**
          * Constructor
-         * @param numWorkers The number of workers for async operations
+         * @param numWorkers The number of workers for async operations. Specify `0` to disable web workers and run synchronously in the current context.
          */
         constructor(numWorkers?: number);
         /**
@@ -52051,11 +52051,6 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
         decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes?: {
             [kind: string]: number;
         }): Promise<VertexData>;
-        /**
-         * The worker function that gets converted to a blob url to pass into a worker.
-         */
-        private static _Worker;
-        private _loadDecoderWasmBinaryAsync;
     }
 }
 declare module "babylonjs/Meshes/Compression/index" {
@@ -90522,9 +90517,10 @@ declare module BABYLON {
          * Checks if a cullable object (mesh...) is in the camera frustum
          * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
          * @param target The object to check
+         * @param checkRigCameras If the rig cameras should be checked (eg. with webVR camera both eyes should be checked) (Default: false)
          * @returns true if the object is in frustum otherwise false
          */
-        isInFrustum(target: ICullable): boolean;
+        isInFrustum(target: ICullable, checkRigCameras?: boolean): boolean;
         /**
          * Checks if a cullable object (mesh...) is in the camera frustum
          * Unlike isInFrustum this cheks the full bounding box
@@ -95052,6 +95048,7 @@ declare module BABYLON {
         wheelDeltaPercentage: number;
         private _wheel;
         private _observer;
+        private computeDeltaFromMouseWheelLegacyEvent;
         /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
@@ -109716,7 +109713,7 @@ declare module BABYLON {
         /**
          * Configuration for the decoder.
          */
-        decoder?: {
+        decoder: {
             /**
              * The url to the WebAssembly module.
              */
@@ -109757,20 +109754,18 @@ declare module BABYLON {
      *
      * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
      * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
-     * Use `DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+     * Use `DracoCompression.DecoderAvailable` to determine if the decoder configuration is available for the current context.
      *
-     * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+     * To decode Draco compressed data, get the default DracoCompression object and call decodeMeshAsync:
      * ```javascript
-     *     var dracoCompression = new DracoCompression();
-     *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
-     *         [VertexBuffer.PositionKind]: 0
-     *     });
+     *     var vertexData = await DracoCompression.Default.decodeMeshAsync(data);
      * ```
      *
      * @see https://www.babylonjs-playground.com/#N3EK4B#0
      */
     export class DracoCompression implements IDisposable {
-        private _workerPoolPromise;
+        private _workerPoolPromise?;
+        private _decoderModulePromise?;
         /**
          * The configuration. Defaults to the following urls:
          * - wasmUrl: "https://preview.babylonjs.com/draco_wasm_wrapper_gltf.js"
@@ -109779,7 +109774,7 @@ declare module BABYLON {
          */
         static Configuration: IDracoCompressionConfiguration;
         /**
-         * Returns true if the decoder is available.
+         * Returns true if the decoder configuration is available.
          */
         static readonly DecoderAvailable: boolean;
         /**
@@ -109794,7 +109789,7 @@ declare module BABYLON {
         static readonly Default: DracoCompression;
         /**
          * Constructor
-         * @param numWorkers The number of workers for async operations
+         * @param numWorkers The number of workers for async operations. Specify `0` to disable web workers and run synchronously in the current context.
          */
         constructor(numWorkers?: number);
         /**
@@ -109815,11 +109810,6 @@ declare module BABYLON {
         decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes?: {
             [kind: string]: number;
         }): Promise<VertexData>;
-        /**
-         * The worker function that gets converted to a blob url to pass into a worker.
-         */
-        private static _Worker;
-        private _loadDecoderWasmBinaryAsync;
     }
 }
 declare module BABYLON {

+ 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.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 6 - 4
dist/preview release/gui/babylon.gui.d.ts

@@ -1083,7 +1083,7 @@ declare module BABYLON.GUI {
         /** @hidden */
         _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         /** @hidden */
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         /** @hidden */
         _onPointerEnter(target: Control): boolean;
         /** @hidden */
@@ -1923,7 +1923,7 @@ declare module BABYLON.GUI {
         private _onPasteText;
         _draw(context: CanvasRenderingContext2D): void;
         _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         protected _beforeRenderText(text: string): string;
         dispose(): void;
@@ -2062,6 +2062,7 @@ declare module BABYLON.GUI {
         private _h;
         private _s;
         private _v;
+        private _lastPointerDownID;
         /**
          * BABYLON.Observable raised when the value changes
          */
@@ -2102,7 +2103,7 @@ declare module BABYLON.GUI {
         private _isPointOnSquare;
         private _isPointOnWheel;
         _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         /**
          * This function expands the color picker by creating a color picker dialog with manual
@@ -2375,6 +2376,7 @@ declare module BABYLON.GUI {
         private _isThumbClamped;
         protected _displayThumb: boolean;
         private _step;
+        private _lastPointerDownID;
         protected _effectiveBarOffset: number;
         protected _renderLeft: number;
         protected _renderTop: number;
@@ -2420,7 +2422,7 @@ declare module BABYLON.GUI {
         /** @hidden */
         protected _updateValueFromPointer(x: number, y: number): void;
         _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
     }
 }

+ 21 - 9
dist/preview release/gui/babylon.gui.js

@@ -1837,6 +1837,7 @@ var ColorPicker = /** @class */ (function (_super) {
         _this._h = 360;
         _this._s = 1;
         _this._v = 1;
+        _this._lastPointerDownID = -1;
         /**
          * Observable raised when the value changes
          */
@@ -2205,9 +2206,14 @@ var ColorPicker = /** @class */ (function (_super) {
         }
         this._updateValueFromPointer(x, y);
         this._host._capturingControl[pointerId] = this;
+        this._lastPointerDownID = pointerId;
         return true;
     };
-    ColorPicker.prototype._onPointerMove = function (target, coordinates) {
+    ColorPicker.prototype._onPointerMove = function (target, coordinates, pointerId) {
+        // Only listen to pointer move events coming from the last pointer to click on the element (To support dual vr controller interaction)
+        if (pointerId != this._lastPointerDownID) {
+            return;
+        }
         // Invert transform
         this._invertTransformMatrix.transformCoordinates(coordinates.x, coordinates.y, this._transformedPosition);
         var x = this._transformedPosition.x;
@@ -2215,7 +2221,7 @@ var ColorPicker = /** @class */ (function (_super) {
         if (this._pointerIsDown) {
             this._updateValueFromPointer(x, y);
         }
-        _super.prototype._onPointerMove.call(this, target, coordinates);
+        _super.prototype._onPointerMove.call(this, target, coordinates, pointerId);
     };
     ColorPicker.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex, notifyClick) {
         this._pointerIsDown = false;
@@ -5164,10 +5170,10 @@ var Control = /** @class */ (function () {
         return true;
     };
     /** @hidden */
-    Control.prototype._onPointerMove = function (target, coordinates) {
+    Control.prototype._onPointerMove = function (target, coordinates, pointerId) {
         var canNotify = this.onPointerMoveObservable.notifyObservers(coordinates, -1, target, this);
         if (canNotify && this.parent != null) {
-            this.parent._onPointerMove(target, coordinates);
+            this.parent._onPointerMove(target, coordinates, pointerId);
         }
     };
     /** @hidden */
@@ -5254,7 +5260,7 @@ var Control = /** @class */ (function () {
         }
         this._dummyVector2.copyFromFloats(x, y);
         if (type === babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["PointerEventTypes"].POINTERMOVE) {
-            this._onPointerMove(this, this._dummyVector2);
+            this._onPointerMove(this, this._dummyVector2, pointerId);
             var previousControlOver = this._host._lastControlOver[pointerId];
             if (previousControlOver && previousControlOver !== this) {
                 previousControlOver._onPointerOut(this);
@@ -7951,13 +7957,13 @@ var InputText = /** @class */ (function (_super) {
         this._host.focusedControl = this;
         return true;
     };
-    InputText.prototype._onPointerMove = function (target, coordinates) {
+    InputText.prototype._onPointerMove = function (target, coordinates, pointerId) {
         if (this._host.focusedControl === this && this._isPointerDown) {
             this._clickedCoordinate = coordinates.x;
             this._markAsDirty();
             this._updateValueFromCursorIndex(this._cursorOffset);
         }
-        _super.prototype._onPointerMove.call(this, target, coordinates);
+        _super.prototype._onPointerMove.call(this, target, coordinates, pointerId);
     };
     InputText.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex, notifyClick) {
         this._isPointerDown = false;
@@ -9996,6 +10002,7 @@ var BaseSlider = /** @class */ (function (_super) {
         _this._isThumbClamped = false;
         _this._displayThumb = true;
         _this._step = 0;
+        _this._lastPointerDownID = -1;
         // Shared rendering info
         _this._effectiveBarOffset = 0;
         /** Observable raised when the sldier value changes */
@@ -10258,13 +10265,18 @@ var BaseSlider = /** @class */ (function (_super) {
         this._pointerIsDown = true;
         this._updateValueFromPointer(coordinates.x, coordinates.y);
         this._host._capturingControl[pointerId] = this;
+        this._lastPointerDownID = pointerId;
         return true;
     };
-    BaseSlider.prototype._onPointerMove = function (target, coordinates) {
+    BaseSlider.prototype._onPointerMove = function (target, coordinates, pointerId) {
+        // Only listen to pointer move events coming from the last pointer to click on the element (To support dual vr controller interaction)
+        if (pointerId != this._lastPointerDownID) {
+            return;
+        }
         if (this._pointerIsDown) {
             this._updateValueFromPointer(coordinates.x, coordinates.y);
         }
-        _super.prototype._onPointerMove.call(this, target, coordinates);
+        _super.prototype._onPointerMove.call(this, target, coordinates, pointerId);
     };
     BaseSlider.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex, notifyClick) {
         this._pointerIsDown = false;

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


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


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

@@ -1113,7 +1113,7 @@ declare module "babylonjs-gui/2D/controls/control" {
         /** @hidden */
         _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         /** @hidden */
-        _onPointerMove(target: Control, coordinates: Vector2): void;
+        _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void;
         /** @hidden */
         _onPointerEnter(target: Control): boolean;
         /** @hidden */
@@ -1988,7 +1988,7 @@ declare module "babylonjs-gui/2D/controls/inputText" {
         private _onPasteText;
         _draw(context: CanvasRenderingContext2D): void;
         _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: Vector2): void;
+        _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         protected _beforeRenderText(text: string): string;
         dispose(): void;
@@ -2137,6 +2137,7 @@ declare module "babylonjs-gui/2D/controls/colorpicker" {
         private _h;
         private _s;
         private _v;
+        private _lastPointerDownID;
         /**
          * Observable raised when the value changes
          */
@@ -2177,7 +2178,7 @@ declare module "babylonjs-gui/2D/controls/colorpicker" {
         private _isPointOnSquare;
         private _isPointOnWheel;
         _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: Vector2): void;
+        _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         /**
          * This function expands the color picker by creating a color picker dialog with manual
@@ -2474,6 +2475,7 @@ declare module "babylonjs-gui/2D/controls/sliders/baseSlider" {
         private _isThumbClamped;
         protected _displayThumb: boolean;
         private _step;
+        private _lastPointerDownID;
         protected _effectiveBarOffset: number;
         protected _renderLeft: number;
         protected _renderTop: number;
@@ -2519,7 +2521,7 @@ declare module "babylonjs-gui/2D/controls/sliders/baseSlider" {
         /** @hidden */
         protected _updateValueFromPointer(x: number, y: number): void;
         _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: Vector2): void;
+        _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
     }
 }
@@ -4929,7 +4931,7 @@ declare module BABYLON.GUI {
         /** @hidden */
         _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         /** @hidden */
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         /** @hidden */
         _onPointerEnter(target: Control): boolean;
         /** @hidden */
@@ -5769,7 +5771,7 @@ declare module BABYLON.GUI {
         private _onPasteText;
         _draw(context: CanvasRenderingContext2D): void;
         _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         protected _beforeRenderText(text: string): string;
         dispose(): void;
@@ -5908,6 +5910,7 @@ declare module BABYLON.GUI {
         private _h;
         private _s;
         private _v;
+        private _lastPointerDownID;
         /**
          * BABYLON.Observable raised when the value changes
          */
@@ -5948,7 +5951,7 @@ declare module BABYLON.GUI {
         private _isPointOnSquare;
         private _isPointOnWheel;
         _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
         /**
          * This function expands the color picker by creating a color picker dialog with manual
@@ -6221,6 +6224,7 @@ declare module BABYLON.GUI {
         private _isThumbClamped;
         protected _displayThumb: boolean;
         private _step;
+        private _lastPointerDownID;
         protected _effectiveBarOffset: number;
         protected _renderLeft: number;
         protected _renderTop: number;
@@ -6266,7 +6270,7 @@ declare module BABYLON.GUI {
         /** @hidden */
         protected _updateValueFromPointer(x: number, y: number): void;
         _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
-        _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
+        _onPointerMove(target: Control, coordinates: BABYLON.Vector2, pointerId: number): void;
         _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
     }
 }

+ 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.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-beta.6"
+        "babylonjs": "4.0.0-beta.7"
     },
     "engines": {
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -31,11 +31,11 @@
     "dependencies": {
         "@types/react": "~16.7.3",
         "@types/react-dom": "~16.0.9",
-        "babylonjs": "4.0.0-beta.6",
-        "babylonjs-gui": "4.0.0-beta.6",
-        "babylonjs-loaders": "4.0.0-beta.6",
-        "babylonjs-serializers": "4.0.0-beta.6",
-        "babylonjs-gltf2interface": "4.0.0-beta.6"
+        "babylonjs": "4.0.0-beta.7",
+        "babylonjs-gui": "4.0.0-beta.7",
+        "babylonjs-loaders": "4.0.0-beta.7",
+        "babylonjs-serializers": "4.0.0-beta.7",
+        "babylonjs-gltf2interface": "4.0.0-beta.7"
     },
     "engines": {
         "node": "*"

+ 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.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-beta.6",
-        "babylonjs": "4.0.0-beta.6"
+        "babylonjs-gltf2interface": "4.0.0-beta.7",
+        "babylonjs": "4.0.0-beta.7"
     },
     "engines": {
         "node": "*"

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

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

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

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

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

@@ -1 +1 @@
-{"engineOnly":289102,"sceneOnly":495880,"minGridMaterial":621110,"minStandardMaterial":745656}
+{"engineOnly":289103,"sceneOnly":496179,"minGridMaterial":621409,"minStandardMaterial":745955}

+ 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.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-beta.6"
+        "babylonjs": "4.0.0-beta.7"
     },
     "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.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-beta.6"
+        "babylonjs": "4.0.0-beta.7"
     },
     "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.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-beta.6",
-        "babylonjs-gltf2interface": "4.0.0-beta.6"
+        "babylonjs": "4.0.0-beta.7",
+        "babylonjs-gltf2interface": "4.0.0-beta.7"
     },
     "engines": {
         "node": "*"

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

@@ -31324,9 +31324,10 @@ declare module "babylonjs/Cameras/camera" {
          * Checks if a cullable object (mesh...) is in the camera frustum
          * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
          * @param target The object to check
+         * @param checkRigCameras If the rig cameras should be checked (eg. with webVR camera both eyes should be checked) (Default: false)
          * @returns true if the object is in frustum otherwise false
          */
-        isInFrustum(target: ICullable): boolean;
+        isInFrustum(target: ICullable, checkRigCameras?: boolean): boolean;
         /**
          * Checks if a cullable object (mesh...) is in the camera frustum
          * Unlike isInFrustum this cheks the full bounding box
@@ -36022,6 +36023,7 @@ declare module "babylonjs/Cameras/Inputs/arcRotateCameraMouseWheelInput" {
         wheelDeltaPercentage: number;
         private _wheel;
         private _observer;
+        private computeDeltaFromMouseWheelLegacyEvent;
         /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
@@ -51952,7 +51954,7 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
         /**
          * Configuration for the decoder.
          */
-        decoder?: {
+        decoder: {
             /**
              * The url to the WebAssembly module.
              */
@@ -51993,20 +51995,18 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
      *
      * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
      * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
-     * Use `DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+     * Use `DracoCompression.DecoderAvailable` to determine if the decoder configuration is available for the current context.
      *
-     * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+     * To decode Draco compressed data, get the default DracoCompression object and call decodeMeshAsync:
      * ```javascript
-     *     var dracoCompression = new DracoCompression();
-     *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
-     *         [VertexBuffer.PositionKind]: 0
-     *     });
+     *     var vertexData = await DracoCompression.Default.decodeMeshAsync(data);
      * ```
      *
      * @see https://www.babylonjs-playground.com/#N3EK4B#0
      */
     export class DracoCompression implements IDisposable {
-        private _workerPoolPromise;
+        private _workerPoolPromise?;
+        private _decoderModulePromise?;
         /**
          * The configuration. Defaults to the following urls:
          * - wasmUrl: "https://preview.babylonjs.com/draco_wasm_wrapper_gltf.js"
@@ -52015,7 +52015,7 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
          */
         static Configuration: IDracoCompressionConfiguration;
         /**
-         * Returns true if the decoder is available.
+         * Returns true if the decoder configuration is available.
          */
         static readonly DecoderAvailable: boolean;
         /**
@@ -52030,7 +52030,7 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
         static readonly Default: DracoCompression;
         /**
          * Constructor
-         * @param numWorkers The number of workers for async operations
+         * @param numWorkers The number of workers for async operations. Specify `0` to disable web workers and run synchronously in the current context.
          */
         constructor(numWorkers?: number);
         /**
@@ -52051,11 +52051,6 @@ declare module "babylonjs/Meshes/Compression/dracoCompression" {
         decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes?: {
             [kind: string]: number;
         }): Promise<VertexData>;
-        /**
-         * The worker function that gets converted to a blob url to pass into a worker.
-         */
-        private static _Worker;
-        private _loadDecoderWasmBinaryAsync;
     }
 }
 declare module "babylonjs/Meshes/Compression/index" {
@@ -90522,9 +90517,10 @@ declare module BABYLON {
          * Checks if a cullable object (mesh...) is in the camera frustum
          * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
          * @param target The object to check
+         * @param checkRigCameras If the rig cameras should be checked (eg. with webVR camera both eyes should be checked) (Default: false)
          * @returns true if the object is in frustum otherwise false
          */
-        isInFrustum(target: ICullable): boolean;
+        isInFrustum(target: ICullable, checkRigCameras?: boolean): boolean;
         /**
          * Checks if a cullable object (mesh...) is in the camera frustum
          * Unlike isInFrustum this cheks the full bounding box
@@ -95052,6 +95048,7 @@ declare module BABYLON {
         wheelDeltaPercentage: number;
         private _wheel;
         private _observer;
+        private computeDeltaFromMouseWheelLegacyEvent;
         /**
          * Attach the input controls to a specific dom element to get the input from.
          * @param element Defines the element the controls should be listened from
@@ -109716,7 +109713,7 @@ declare module BABYLON {
         /**
          * Configuration for the decoder.
          */
-        decoder?: {
+        decoder: {
             /**
              * The url to the WebAssembly module.
              */
@@ -109757,20 +109754,18 @@ declare module BABYLON {
      *
      * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
      * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
-     * Use `DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+     * Use `DracoCompression.DecoderAvailable` to determine if the decoder configuration is available for the current context.
      *
-     * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+     * To decode Draco compressed data, get the default DracoCompression object and call decodeMeshAsync:
      * ```javascript
-     *     var dracoCompression = new DracoCompression();
-     *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
-     *         [VertexBuffer.PositionKind]: 0
-     *     });
+     *     var vertexData = await DracoCompression.Default.decodeMeshAsync(data);
      * ```
      *
      * @see https://www.babylonjs-playground.com/#N3EK4B#0
      */
     export class DracoCompression implements IDisposable {
-        private _workerPoolPromise;
+        private _workerPoolPromise?;
+        private _decoderModulePromise?;
         /**
          * The configuration. Defaults to the following urls:
          * - wasmUrl: "https://preview.babylonjs.com/draco_wasm_wrapper_gltf.js"
@@ -109779,7 +109774,7 @@ declare module BABYLON {
          */
         static Configuration: IDracoCompressionConfiguration;
         /**
-         * Returns true if the decoder is available.
+         * Returns true if the decoder configuration is available.
          */
         static readonly DecoderAvailable: boolean;
         /**
@@ -109794,7 +109789,7 @@ declare module BABYLON {
         static readonly Default: DracoCompression;
         /**
          * Constructor
-         * @param numWorkers The number of workers for async operations
+         * @param numWorkers The number of workers for async operations. Specify `0` to disable web workers and run synchronously in the current context.
          */
         constructor(numWorkers?: number);
         /**
@@ -109815,11 +109810,6 @@ declare module BABYLON {
         decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes?: {
             [kind: string]: number;
         }): Promise<VertexData>;
-        /**
-         * The worker function that gets converted to a blob url to pass into a worker.
-         */
-        private static _Worker;
-        private _loadDecoderWasmBinaryAsync;
     }
 }
 declare module BABYLON {

+ 5 - 19
dist/preview release/viewer/babylon.viewer.d.ts

@@ -197,11 +197,11 @@ declare module BabylonViewer {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
+            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<Template> | Promise<string>;
+            hideOverlayScreen(): Promise<string> | Promise<Template>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -218,11 +218,11 @@ declare module BabylonViewer {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<Template> | Promise<string>;
+            showLoadingScreen(): Promise<string> | Promise<Template>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<Template> | Promise<string>;
+            hideLoadingScreen(): Promise<string> | Promise<Template>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -951,7 +951,7 @@ declare module BabylonViewer {
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
@@ -1594,20 +1594,6 @@ declare module BabylonViewer {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 declare module BabylonViewer {
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-declare module BabylonViewer {
     export interface IEnvironmentMapConfiguration {
             /**
                 * Environment map texture path in relative to the asset folder.

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


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


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

@@ -230,11 +230,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
+            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<Template> | Promise<string>;
+            hideOverlayScreen(): Promise<string> | Promise<Template>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -251,11 +251,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<Template> | Promise<string>;
+            showLoadingScreen(): Promise<string> | Promise<Template>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<Template> | Promise<string>;
+            hideLoadingScreen(): Promise<string> | Promise<Template>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -1031,14 +1031,13 @@ declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
 }
 
 declare module 'babylonjs-viewer/optimizer/custom' {
-    import { extendedUpgrade } from "babylonjs-viewer/optimizer/custom/extended";
     import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
     /**
       *
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 
@@ -1738,22 +1737,6 @@ declare module 'babylonjs-viewer/loader/plugins' {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 
-declare module 'babylonjs-viewer/optimizer/custom/extended' {
-    import { SceneManager } from 'babylonjs-viewer/managers/sceneManager';
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-
 declare module 'babylonjs-viewer/configuration/interfaces' {
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';

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

@@ -217,7 +217,7 @@
 - CannonJS ignores connectedPivot joint parameter ([TrevorDev](https://github.com/TrevorDev))
 - Fix case sensitive paths ([mrdunk](https://github.com))
 - Fix more case sensitive paths ([mrdunk](https://github.com))
-- Attaching a BoundingBoxGizmo on a child node shouldn't remove its parent ([TrevorDev](https://github.com/TrevorDev))
+- Attaching a BoundingBoxGizmo on a child node shouldn't remove its parent, rotation gizmo should work on object with parent ([TrevorDev](https://github.com/TrevorDev))
 - AmmoJS fix include issue caused after modules update and use world contact point to be consistent with Oimo and Cannon ([TrevorDev](https://github.com/TrevorDev))
 - Warn of motor with maxForce in Oimo plugin and set default force to be consistent with others, cannonJS support no impostor, cannonJS cylinder axis, ammoJS wake up impostor when apply force/impulse ([TrevorDev](https://github.com/TrevorDev))
 - Utility layer should render on last active camera ([TrevorDev](https://github.com/TrevorDev))
@@ -236,6 +236,7 @@
 - Fix code branch, that does not try to (re)load an `EquiRectangularCubeTexture`/`HDRCubeTexture` when the caching returns an empty or corrupt `InternalTexture` ([Dennis Dervisis](https://github.com/ddervisis))
 - Add error eventlistener (bubbling up the onError callback chain) in case an `EquiRectangularCubeTexture` cannot be loaded, because of a wrong path or IO problems ([Dennis Dervisis](https://github.com/ddervisis))
 - 3D GUI buttons no longer will scale up when pressing with a multitouch device ([TrevorDev](https://github.com/TrevorDev))
+- 2D GUI elements will use the last clicked controller instead of only the right controller when dual VR controllers are interacting with an element ([TrevorDev](https://github.com/TrevorDev))
 
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
@@ -263,6 +264,7 @@
 - Fix a bug causing `WebRequest.open` to crash if `WebRequest.CustomRequestHeaders` are set [#6055](https://github.com/BabylonJS/Babylon.js/issues/6055)([susares](https://github.com/susares))
 - Fix a bug causing `Mesh.clone` to crash if no physicsEngineComponent is used  ([barroij](https://github.com/barroij))
 - Fix zoom inertia making it difficult to zoom out with ArcRotateCamera ([TrevorDev](https://github.com/TrevorDev))
+- Option for isInFrustum to check rigCameras, viewMatrix updates for rigCameras will notify their parent ([TrevorDev](https://github.com/TrevorDev))
 
 ### Viewer
 

+ 9 - 3
gui/src/2D/controls/colorpicker.ts

@@ -29,6 +29,8 @@ export class ColorPicker extends Control {
     private _s = 1;
     private _v = 1;
 
+    private _lastPointerDownID = -1;
+
     /**
      * Observable raised when the value changes
      */
@@ -469,11 +471,15 @@ export class ColorPicker extends Control {
 
         this._updateValueFromPointer(x, y);
         this._host._capturingControl[pointerId] = this;
-
+        this._lastPointerDownID = pointerId;
         return true;
     }
 
-    public _onPointerMove(target: Control, coordinates: Vector2): void {
+    public _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void {
+        // Only listen to pointer move events coming from the last pointer to click on the element (To support dual vr controller interaction)
+        if (pointerId != this._lastPointerDownID) {
+            return;
+        }
         // Invert transform
         this._invertTransformMatrix.transformCoordinates(coordinates.x, coordinates.y, this._transformedPosition);
 
@@ -484,7 +490,7 @@ export class ColorPicker extends Control {
             this._updateValueFromPointer(x, y);
         }
 
-        super._onPointerMove(target, coordinates);
+        super._onPointerMove(target, coordinates, pointerId);
     }
 
     public _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void {

+ 3 - 3
gui/src/2D/controls/control.ts

@@ -1601,10 +1601,10 @@ export class Control {
     }
 
     /** @hidden */
-    public _onPointerMove(target: Control, coordinates: Vector2): void {
+    public _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void {
         var canNotify: boolean = this.onPointerMoveObservable.notifyObservers(coordinates, -1, target, this);
 
-        if (canNotify && this.parent != null) { this.parent._onPointerMove(target, coordinates); }
+        if (canNotify && this.parent != null) { this.parent._onPointerMove(target, coordinates, pointerId); }
     }
 
     /** @hidden */
@@ -1703,7 +1703,7 @@ export class Control {
         }
         this._dummyVector2.copyFromFloats(x, y);
         if (type === PointerEventTypes.POINTERMOVE) {
-            this._onPointerMove(this, this._dummyVector2);
+            this._onPointerMove(this, this._dummyVector2, pointerId);
 
             var previousControlOver = this._host._lastControlOver[pointerId];
             if (previousControlOver && previousControlOver !== this) {

+ 2 - 2
gui/src/2D/controls/inputText.ts

@@ -993,13 +993,13 @@ export class InputText extends Control implements IFocusableControl {
 
         return true;
     }
-    public _onPointerMove(target: Control, coordinates: Vector2): void {
+    public _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void {
         if (this._host.focusedControl === this && this._isPointerDown) {
             this._clickedCoordinate = coordinates.x;
             this._markAsDirty();
             this._updateValueFromCursorIndex(this._cursorOffset);
         }
-        super._onPointerMove(target, coordinates);
+        super._onPointerMove(target, coordinates, pointerId);
     }
 
     public _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void {

+ 10 - 3
gui/src/2D/controls/sliders/baseSlider.ts

@@ -18,6 +18,8 @@ export class BaseSlider extends Control {
     protected _displayThumb = true;
     private _step = 0;
 
+    private _lastPointerDownID = -1;
+
     // Shared rendering info
     protected _effectiveBarOffset = 0;
     protected _renderLeft: number;
@@ -301,16 +303,21 @@ export class BaseSlider extends Control {
 
         this._updateValueFromPointer(coordinates.x, coordinates.y);
         this._host._capturingControl[pointerId] = this;
-
+        this._lastPointerDownID = pointerId;
         return true;
     }
 
-    public _onPointerMove(target: Control, coordinates: Vector2): void {
+    public _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void {
+        // Only listen to pointer move events coming from the last pointer to click on the element (To support dual vr controller interaction)
+        if (pointerId != this._lastPointerDownID) {
+            return;
+        }
+
         if (this._pointerIsDown) {
             this._updateValueFromPointer(coordinates.x, coordinates.y);
         }
 
-        super._onPointerMove(target, coordinates);
+        super._onPointerMove(target, coordinates, pointerId);
     }
 
     public _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void {

+ 2 - 2
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.0.0-beta.6",
+    "version": "4.0.0-beta.7",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -107,4 +107,4 @@
         "xhr2": "^0.1.4",
         "xmlbuilder": "8.2.2"
     }
-}
+}

+ 2 - 2
src/Animations/runtimeAnimation.ts

@@ -482,10 +482,10 @@ export class RuntimeAnimation {
         this._previousDelay = delay;
         this._previousRatio = ratio;
 
-        if (!loop && (to > from && ratio >= range)) { // If we are out of range and not looping get back to caller
+        if (!loop && (to >= from && ratio >= range)) { // If we are out of range and not looping get back to caller
             returnValue = false;
             highLimitValue = animation._getKeyValue(this._maxValue);
-        } else if (!loop && (from > to && ratio <= range)) {
+        } else if (!loop && (from >= to && ratio <= range)) {
             returnValue = false;
             highLimitValue = animation._getKeyValue(this._minValue);
         } else if (this._animationState.loopMode !== Animation.ANIMATIONLOOPMODE_CYCLE) {

+ 17 - 2
src/Cameras/camera.ts

@@ -651,6 +651,11 @@ export class Camera extends Node {
             this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
         }
 
+        // Notify parent camera if rig camera is changed
+        if (this.parent && (this.parent as Camera).onViewMatrixChangedObservable) {
+            (this.parent as Camera).onViewMatrixChangedObservable.notifyObservers((this.parent as Camera));
+        }
+
         this.onViewMatrixChangedObservable.notifyObservers(this);
 
         this._computedViewMatrix.invertToRef(this._worldMatrix);
@@ -785,12 +790,22 @@ export class Camera extends Node {
      * Checks if a cullable object (mesh...) is in the camera frustum
      * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
      * @param target The object to check
+     * @param checkRigCameras If the rig cameras should be checked (eg. with webVR camera both eyes should be checked) (Default: false)
      * @returns true if the object is in frustum otherwise false
      */
-    public isInFrustum(target: ICullable): boolean {
+    public isInFrustum(target: ICullable, checkRigCameras = false): boolean {
         this._updateFrustumPlanes();
 
-        return target.isInFrustum(this._frustumPlanes);
+        if (checkRigCameras && this.rigCameras.length > 0) {
+            var result = false;
+            this.rigCameras.forEach((cam) => {
+                cam._updateFrustumPlanes();
+                result = result || target.isInFrustum(cam._frustumPlanes);
+            });
+            return result;
+        }else {
+            return target.isInFrustum(this._frustumPlanes);
+        }
     }
 
     /**

+ 2 - 2
src/Engines/Extensions/engine.multiview.ts

@@ -56,8 +56,8 @@ Engine.prototype.bindMultiviewFramebuffer = function(multiviewTexture: InternalT
     this.bindFramebuffer(multiviewTexture, undefined, undefined, undefined, true);
     gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, multiviewTexture._framebuffer);
     if (multiviewTexture._colorTextureArray && multiviewTexture._depthStencilTextureArray) {
-        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, multiviewTexture._colorTextureArray, 0, 0, 2);
-        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, multiviewTexture._depthStencilTextureArray, 0, 0, 2);
+        ext.framebufferTextureMultiviewOVR(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, multiviewTexture._colorTextureArray, 0, 0, 2);
+        ext.framebufferTextureMultiviewOVR(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, multiviewTexture._depthStencilTextureArray, 0, 0, 2);
     } else {
         throw "Invalid multiview frame buffer";
     }

+ 3 - 3
src/Engines/engine.ts

@@ -491,14 +491,14 @@ export class Engine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.0.0-beta.6";
+        return "babylonjs@4.0.0-beta.7";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.0.0-beta.6";
+        return "4.0.0-beta.7";
     }
 
     /**
@@ -1443,7 +1443,7 @@ export class Engine {
 
         this._caps.textureLOD = (this._webGLVersion > 1 || this._gl.getExtension('EXT_shader_texture_lod')) ? true : false;
 
-        this._caps.multiview = this._gl.getExtension('WEBGL_multiview');
+        this._caps.multiview = this._gl.getExtension('OVR_multiview2');
         // Draw buffers
         if (this._webGLVersion > 1) {
             this._caps.drawBuffersExtension = true;

+ 12 - 0
src/Gizmos/planeRotationGizmo.ts

@@ -98,6 +98,13 @@ export class PlaneRotationGizmo extends Gizmo {
                 if (!this.attachedMesh.rotationQuaternion) {
                     this.attachedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.attachedMesh.rotation.y, this.attachedMesh.rotation.x, this.attachedMesh.rotation.z);
                 }
+
+                // Remove parent priort to rotating
+                var attachedMeshParent = this.attachedMesh.parent;
+                if (attachedMeshParent) {
+                    this.attachedMesh.setParent(null);
+                }
+
                 // Calc angle over full 360 degree (https://stackoverflow.com/questions/43493711/the-angle-between-two-3d-vectors-with-a-result-range-0-360)
                 var newVector = event.dragPlanePoint.subtract(this.attachedMesh.absolutePosition).normalize();
                 var originalVector = lastDragPosition.subtract(this.attachedMesh.absolutePosition).normalize();
@@ -169,6 +176,11 @@ export class PlaneRotationGizmo extends Gizmo {
                     tmpSnapEvent.snapDistance = angle;
                     this.onSnapObservable.notifyObservers(tmpSnapEvent);
                 }
+
+                // Restore parent
+                if (attachedMeshParent) {
+                    this.attachedMesh.setParent(attachedMeshParent);
+                }
             }
         });
 

+ 2 - 2
src/Materials/effect.ts

@@ -638,7 +638,7 @@ export class Effect implements IDisposable {
         // #extension GL_EXT_shader_texture_lod : enable
         // #extension GL_EXT_frag_depth : enable
         // #extension GL_EXT_draw_buffers : require
-        var regex = /#extension.+(GL_OVR_multiview|GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g;
+        var regex = /#extension.+(GL_OVR_multiview2|GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g;
         var result = preparedSourceCode.replace(regex, "");
 
         // Migrate to GLSL v300
@@ -660,7 +660,7 @@ export class Effect implements IDisposable {
         // Add multiview setup to top of file when defined
         var hasMultiviewExtension = this.defines.indexOf("#define MULTIVIEW\n") !== -1;
         if (hasMultiviewExtension && !isFragment) {
-            result = "#extension GL_OVR_multiview : require\nlayout (num_views = 2) in;\n" + result;
+            result = "#extension GL_OVR_multiview2 : require\nlayout (num_views = 2) in;\n" + result;
         }
 
         callback(result);

+ 279 - 245
src/Meshes/Compression/dracoCompression.ts

@@ -7,7 +7,188 @@ import { VertexData } from "../../Meshes/mesh.vertexData";
 declare var DracoDecoderModule: any;
 declare var WebAssembly: any;
 
+// WorkerGlobalScope
 declare function importScripts(...urls: string[]): void;
+declare function postMessage(message: any, transfer?: any[]): void;
+
+function loadScriptAsync(url: string): Promise<void> {
+    if (typeof importScripts === "function") {
+        importScripts(url);
+        return Promise.resolve();
+    }
+    else {
+        return new Promise((resolve, reject) => {
+            Tools.LoadScript(url, () => {
+                resolve();
+            }, (message) => {
+                reject(new Error(message));
+            });
+        });
+    }
+}
+
+function loadFileAsync(url: string): Promise<ArrayBuffer> {
+    return new Promise((resolve, reject) => {
+        Tools.LoadFile(url, (data) => {
+            resolve(data as ArrayBuffer);
+        }, undefined, undefined, true, (request, exception) => {
+            reject(exception);
+        });
+    });
+}
+
+function createDecoderAsync(wasmUrl?: string, wasmBinary?: ArrayBuffer, fallbackUrl?: string): Promise<any> | undefined {
+    const decoderUrl = (wasmBinary && wasmUrl) || fallbackUrl;
+    if (decoderUrl) {
+        return loadScriptAsync(decoderUrl).then(() => {
+            return new Promise((resolve) => {
+                DracoDecoderModule({ wasmBinary: wasmBinary }).then((module: any) => {
+                    resolve({ module: module });
+                });
+            });
+        });
+    }
+
+    return undefined;
+}
+
+function decodeMesh(decoderModule: any, dataView: ArrayBufferView, attributes: { [kind: string]: number } | undefined, onIndicesData: (data: Uint32Array) => void, onAttributeData: (kind: string, data: Float32Array) => void): void {
+    const buffer = new decoderModule.DecoderBuffer();
+    buffer.Init(dataView, dataView.byteLength);
+
+    const decoder = new decoderModule.Decoder();
+    let geometry: any;
+    let status: any;
+
+    try {
+        const type = decoder.GetEncodedGeometryType(buffer);
+        switch (type) {
+            case decoderModule.TRIANGULAR_MESH:
+                geometry = new decoderModule.Mesh();
+                status = decoder.DecodeBufferToMesh(buffer, geometry);
+                break;
+            case decoderModule.POINT_CLOUD:
+                geometry = new decoderModule.PointCloud();
+                status = decoder.DecodeBufferToPointCloud(buffer, geometry);
+                break;
+            default:
+                throw new Error(`Invalid geometry type ${type}`);
+        }
+
+        if (!status.ok() || !geometry.ptr) {
+            throw new Error(status.error_msg());
+        }
+
+        const numPoints = geometry.num_points();
+
+        if (type === decoderModule.TRIANGULAR_MESH) {
+            const numFaces = geometry.num_faces();
+            const faceIndices = new decoderModule.DracoInt32Array();
+            try {
+                const indices = new Uint32Array(numFaces * 3);
+                for (let i = 0; i < numFaces; i++) {
+                    decoder.GetFaceFromMesh(geometry, i, faceIndices);
+                    const offset = i * 3;
+                    indices[offset + 0] = faceIndices.GetValue(0);
+                    indices[offset + 1] = faceIndices.GetValue(1);
+                    indices[offset + 2] = faceIndices.GetValue(2);
+                }
+                onIndicesData(indices);
+            }
+            finally {
+                decoderModule.destroy(faceIndices);
+            }
+        }
+
+        const processAttribute = (kind: string, attribute: any) => {
+            const dracoData = new decoderModule.DracoFloat32Array();
+            try {
+                decoder.GetAttributeFloatForAllPoints(geometry, attribute, dracoData);
+                const babylonData = new Float32Array(numPoints * attribute.num_components());
+                for (let i = 0; i < babylonData.length; i++) {
+                    babylonData[i] = dracoData.GetValue(i);
+                }
+                onAttributeData(kind, babylonData);
+            }
+            finally {
+                decoderModule.destroy(dracoData);
+            }
+        };
+
+        if (attributes) {
+            for (const kind in attributes) {
+                const id = attributes[kind];
+                const attribute = decoder.GetAttributeByUniqueId(geometry, id);
+                processAttribute(kind, attribute);
+            }
+        }
+        else {
+            const nativeAttributeTypes: { [kind: string]: string } = {
+                "position": "POSITION",
+                "normal": "NORMAL",
+                "color": "COLOR",
+                "uv": "TEX_COORD"
+            };
+
+            for (const kind in nativeAttributeTypes) {
+                const id = decoder.GetAttributeId(geometry, decoderModule[nativeAttributeTypes[kind]]);
+                if (id !== -1) {
+                    const attribute = decoder.GetAttribute(geometry, id);
+                    processAttribute(kind, attribute);
+                }
+            }
+        }
+    }
+    finally {
+        if (geometry) {
+            decoderModule.destroy(geometry);
+        }
+
+        decoderModule.destroy(decoder);
+        decoderModule.destroy(buffer);
+    }
+}
+
+/**
+ * The worker function that gets converted to a blob url to pass into a worker.
+ */
+function worker(): void {
+    let decoderPromise: Promise<any> | undefined;
+
+    onmessage = (event) => {
+        const data = event.data;
+        switch (data.id) {
+            case "init": {
+                const decoder = data.decoder;
+                decoderPromise = createDecoderAsync(decoder.wasmUrl, decoder.wasmBinary, decoder.fallbackUrl);
+                postMessage("done");
+                break;
+            }
+            case "decodeMesh": {
+                if (!decoderPromise) {
+                    throw new Error("Draco decoder module is not available");
+                }
+                decoderPromise.then((decoder) => {
+                    decodeMesh(decoder.module, data.dataView, data.attributes, (indices) => {
+                        postMessage({ id: "indices", value: indices }, [indices.buffer]);
+                    }, (kind, data) => {
+                        postMessage({ id: kind, value: data }, [data.buffer]);
+                    });
+                    postMessage("done");
+                });
+                break;
+            }
+        }
+    };
+}
+
+function getAbsoluteUrl<T>(url: T): T | string {
+    if (typeof document !== "object" || typeof url !== "string") {
+        return url;
+    }
+
+    return Tools.GetAbsoluteUrl(url);
+}
 
 /**
  * Configuration for Draco compression
@@ -16,7 +197,7 @@ export interface IDracoCompressionConfiguration {
     /**
      * Configuration for the decoder.
      */
-    decoder?: {
+    decoder: {
         /**
          * The url to the WebAssembly module.
          */
@@ -60,20 +241,18 @@ export interface IDracoCompressionConfiguration {
  *
  * Draco has two versions, one for WebAssembly and one for JavaScript. The decoder configuration can be set to only support Webssembly or only support the JavaScript version.
  * Decoding will automatically fallback to the JavaScript version if WebAssembly version is not configured or if WebAssembly is not supported by the browser.
- * Use `DracoCompression.DecoderAvailable` to determine if the decoder is available for the current session.
+ * Use `DracoCompression.DecoderAvailable` to determine if the decoder configuration is available for the current context.
  *
- * To decode Draco compressed data, create a DracoCompression object and call decodeMeshAsync:
+ * To decode Draco compressed data, get the default DracoCompression object and call decodeMeshAsync:
  * ```javascript
- *     var dracoCompression = new DracoCompression();
- *     var vertexData = await dracoCompression.decodeMeshAsync(data, {
- *         [VertexBuffer.PositionKind]: 0
- *     });
+ *     var vertexData = await DracoCompression.Default.decodeMeshAsync(data);
  * ```
  *
  * @see https://www.babylonjs-playground.com/#N3EK4B#0
  */
 export class DracoCompression implements IDisposable {
-    private _workerPoolPromise: Promise<WorkerPool>;
+    private _workerPoolPromise?: Promise<WorkerPool>;
+    private _decoderModulePromise?: Promise<any>;
 
     /**
      * The configuration. Defaults to the following urls:
@@ -90,25 +269,11 @@ export class DracoCompression implements IDisposable {
     };
 
     /**
-     * Returns true if the decoder is available.
+     * Returns true if the decoder configuration is available.
      */
     public static get DecoderAvailable(): boolean {
-        if (typeof DracoDecoderModule !== "undefined") {
-            return true;
-        }
-
         const decoder = DracoCompression.Configuration.decoder;
-        if (decoder) {
-            if (decoder.wasmUrl && decoder.wasmBinaryUrl && typeof WebAssembly === "object") {
-                return true;
-            }
-
-            if (decoder.fallbackUrl) {
-                return true;
-            }
-        }
-
-        return false;
+        return !!((decoder.wasmUrl && decoder.wasmBinaryUrl && typeof WebAssembly === "object") || decoder.fallbackUrl);
     }
 
     /**
@@ -117,7 +282,7 @@ export class DracoCompression implements IDisposable {
     public static DefaultNumWorkers = DracoCompression.GetDefaultNumWorkers();
 
     private static GetDefaultNumWorkers(): number {
-        if (typeof navigator === "undefined" || !navigator.hardwareConcurrency) {
+        if (typeof navigator !== "object" || !navigator.hardwareConcurrency) {
             return 1;
         }
 
@@ -140,24 +305,23 @@ export class DracoCompression implements IDisposable {
 
     /**
      * Constructor
-     * @param numWorkers The number of workers for async operations
+     * @param numWorkers The number of workers for async operations. Specify `0` to disable web workers and run synchronously in the current context.
      */
     constructor(numWorkers = DracoCompression.DefaultNumWorkers) {
-        if (!URL || !URL.createObjectURL) {
-            throw new Error("Object URLs are not available");
-        }
-
-        if (!Worker) {
-            throw new Error("Workers are not available");
-        }
+        const decoder = DracoCompression.Configuration.decoder;
 
-        this._workerPoolPromise = this._loadDecoderWasmBinaryAsync().then((decoderWasmBinary) => {
-            const workerBlobUrl = URL.createObjectURL(new Blob([`(${DracoCompression._Worker.toString()})()`], { type: "application/javascript" }));
-            const workerPromises = new Array<Promise<Worker>>(numWorkers);
-            for (let i = 0; i < workerPromises.length; i++) {
-                workerPromises[i] = new Promise((resolve, reject) => {
-                    const decoder = DracoCompression.Configuration.decoder;
-                    if (decoder) {
+        const decoderWasmBinaryPromise: Promise<ArrayBuffer | undefined> =
+            (decoder.wasmUrl && decoder.wasmBinaryUrl && typeof WebAssembly === "object")
+                ? loadFileAsync(getAbsoluteUrl(decoder.wasmBinaryUrl))
+                : Promise.resolve(undefined);
+
+        if (numWorkers && typeof Worker === "function") {
+            this._workerPoolPromise = decoderWasmBinaryPromise.then((decoderWasmBinary) => {
+                const workerContent = `${loadScriptAsync}${createDecoderAsync}${decodeMesh}(${worker})()`;
+                const workerBlobUrl = URL.createObjectURL(new Blob([workerContent], { type: "application/javascript" }));
+                const workerPromises = new Array<Promise<Worker>>(numWorkers);
+                for (let i = 0; i < workerPromises.length; i++) {
+                    workerPromises[i] = new Promise((resolve, reject) => {
                         const worker = new Worker(workerBlobUrl);
                         const onError = (error: ErrorEvent) => {
                             worker.removeEventListener("error", onError);
@@ -177,30 +341,40 @@ export class DracoCompression implements IDisposable {
                         worker.addEventListener("message", onMessage);
 
                         worker.postMessage({
-                            id: "initDecoder",
-                            decoderWasmUrl: decoder.wasmUrl ? Tools.GetAbsoluteUrl(decoder.wasmUrl) : null,
-                            decoderWasmBinary: decoderWasmBinary,
-                            fallbackUrl: decoder.fallbackUrl ? Tools.GetAbsoluteUrl(decoder.fallbackUrl) : null
+                            id: "init",
+                            decoder: {
+                                wasmUrl: getAbsoluteUrl(decoder.wasmUrl),
+                                wasmBinary: decoderWasmBinary,
+                                fallbackUrl: getAbsoluteUrl(decoder.fallbackUrl)
+                            }
                         });
-                    }
-                });
-            }
+                    });
+                }
 
-            return Promise.all(workerPromises).then((workers) => {
-                return new WorkerPool(workers);
+                return Promise.all(workerPromises).then((workers) => {
+                    return new WorkerPool(workers);
+                });
             });
-        });
+        }
+        else {
+            this._decoderModulePromise = decoderWasmBinaryPromise.then((decoderWasmBinary) => {
+                return createDecoderAsync(decoder.wasmUrl, decoderWasmBinary, decoder.fallbackUrl);
+            });
+        }
     }
 
     /**
      * Stop all async operations and release resources.
      */
     public dispose(): void {
-        this._workerPoolPromise.then((workerPool) => {
-            workerPool.dispose();
-        });
+        if (this._workerPoolPromise) {
+            this._workerPoolPromise.then((workerPool) => {
+                workerPool.dispose();
+            });
+        }
 
         delete this._workerPoolPromise;
+        delete this._decoderModulePromise;
     }
 
     /**
@@ -208,218 +382,78 @@ export class DracoCompression implements IDisposable {
      * @returns a promise that resolves when ready
      */
     public whenReadyAsync(): Promise<void> {
-        return this._workerPoolPromise.then(() => { });
+        if (this._workerPoolPromise) {
+            return this._workerPoolPromise.then(() => { });
+        }
+
+        if (this._decoderModulePromise) {
+            return this._decoderModulePromise.then(() => { });
+        }
+
+        return Promise.resolve();
     }
 
-   /**
-     * Decode Draco compressed mesh data to vertex data.
-     * @param data The ArrayBuffer or ArrayBufferView for the Draco compression data
-     * @param attributes A map of attributes from vertex buffer kinds to Draco unique ids
-     * @returns A promise that resolves with the decoded vertex data
-     */
+    /**
+      * Decode Draco compressed mesh data to vertex data.
+      * @param data The ArrayBuffer or ArrayBufferView for the Draco compression data
+      * @param attributes A map of attributes from vertex buffer kinds to Draco unique ids
+      * @returns A promise that resolves with the decoded vertex data
+      */
     public decodeMeshAsync(data: ArrayBuffer | ArrayBufferView, attributes?: { [kind: string]: number }): Promise<VertexData> {
         const dataView = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
 
-        return this._workerPoolPromise.then((workerPool) => {
-            return new Promise<VertexData>((resolve, reject) => {
-                workerPool.push((worker, onComplete) => {
-                    const vertexData = new VertexData();
-
-                    const onError = (error: ErrorEvent) => {
-                        worker.removeEventListener("error", onError);
-                        worker.removeEventListener("message", onMessage);
-                        reject(error);
-                        onComplete();
-                    };
+        if (this._workerPoolPromise) {
+            return this._workerPoolPromise.then((workerPool) => {
+                return new Promise<VertexData>((resolve, reject) => {
+                    workerPool.push((worker, onComplete) => {
+                        const vertexData = new VertexData();
 
-                    const onMessage = (message: MessageEvent) => {
-                        if (message.data === "done") {
+                        const onError = (error: ErrorEvent) => {
                             worker.removeEventListener("error", onError);
                             worker.removeEventListener("message", onMessage);
-                            resolve(vertexData);
+                            reject(error);
                             onComplete();
-                        }
-                        else if (message.data.id === "indices") {
-                            vertexData.indices = message.data.value;
-                        }
-                        else {
-                            vertexData.set(message.data.value, message.data.id);
-                        }
-                    };
-
-                    worker.addEventListener("error", onError);
-                    worker.addEventListener("message", onMessage);
-
-                    const dataViewCopy = new Uint8Array(dataView.byteLength);
-                    dataViewCopy.set(new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength));
-
-                    worker.postMessage({ id: "decodeMesh", dataView: dataViewCopy, attributes: attributes }, [dataViewCopy.buffer]);
-                });
-            });
-        });
-    }
-
-    /**
-     * The worker function that gets converted to a blob url to pass into a worker.
-     */
-    private static _Worker(): void {
-        const nativeAttributeTypes: { [kind: string]: string } = {
-            "position": "POSITION",
-            "normal": "NORMAL",
-            "color": "COLOR",
-            "uv": "TEX_COORD"
-        };
-
-        // self is actually a DedicatedWorkerGlobalScope
-        const _self = self as any as {
-            onmessage: (event: MessageEvent) => void;
-            postMessage: (message: any, transfer?: any[]) => void;
-            close: () => void;
-        };
-
-        let decoderModulePromise: Promise<any>;
-
-        function initDecoder(decoderWasmUrl: string | undefined, decoderWasmBinary: ArrayBuffer | undefined, fallbackUrl: string | undefined): void {
-            if (decoderWasmUrl && decoderWasmBinary && typeof WebAssembly === "object") {
-                importScripts(decoderWasmUrl);
-                decoderModulePromise = DracoDecoderModule({
-                    wasmBinary: decoderWasmBinary
-                });
-            }
-            else if (fallbackUrl) {
-                importScripts(fallbackUrl);
-                decoderModulePromise = DracoDecoderModule();
-            }
-            else {
-                throw Error("Failed to initialize Draco decoder");
-            }
-
-            _self.postMessage("done");
-        }
+                        };
 
-        function decodeMesh(dataView: ArrayBufferView, attributes: { [kind: string]: number }): void {
-            decoderModulePromise.then((decoderModule) => {
-                const buffer = new decoderModule.DecoderBuffer();
-                buffer.Init(dataView, dataView.byteLength);
-
-                const decoder = new decoderModule.Decoder();
-                let geometry: any;
-                let status: any;
-
-                try {
-                    const type = decoder.GetEncodedGeometryType(buffer);
-                    switch (type) {
-                        case decoderModule.TRIANGULAR_MESH:
-                            geometry = new decoderModule.Mesh();
-                            status = decoder.DecodeBufferToMesh(buffer, geometry);
-                            break;
-                        case decoderModule.POINT_CLOUD:
-                            geometry = new decoderModule.PointCloud();
-                            status = decoder.DecodeBufferToPointCloud(buffer, geometry);
-                            break;
-                        default:
-                            throw new Error(`Invalid geometry type ${type}`);
-                    }
-
-                    if (!status.ok() || !geometry.ptr) {
-                        throw new Error(status.error_msg());
-                    }
-
-                    const numPoints = geometry.num_points();
-
-                    if (type === decoderModule.TRIANGULAR_MESH) {
-                        const numFaces = geometry.num_faces();
-                        const faceIndices = new decoderModule.DracoInt32Array();
-                        try {
-                            const indices = new Uint32Array(numFaces * 3);
-                            for (let i = 0; i < numFaces; i++) {
-                                decoder.GetFaceFromMesh(geometry, i, faceIndices);
-                                const offset = i * 3;
-                                indices[offset + 0] = faceIndices.GetValue(0);
-                                indices[offset + 1] = faceIndices.GetValue(1);
-                                indices[offset + 2] = faceIndices.GetValue(2);
+                        const onMessage = (message: MessageEvent) => {
+                            if (message.data === "done") {
+                                worker.removeEventListener("error", onError);
+                                worker.removeEventListener("message", onMessage);
+                                resolve(vertexData);
+                                onComplete();
                             }
-                            _self.postMessage({ id: "indices", value: indices }, [indices.buffer]);
-                        }
-                        finally {
-                            decoderModule.destroy(faceIndices);
-                        }
-                    }
-
-                    const processAttribute = (kind: string, attribute: any) => {
-                        const dracoData = new decoderModule.DracoFloat32Array();
-                        try {
-                            decoder.GetAttributeFloatForAllPoints(geometry, attribute, dracoData);
-                            const babylonData = new Float32Array(numPoints * attribute.num_components());
-                            for (let i = 0; i < babylonData.length; i++) {
-                                babylonData[i] = dracoData.GetValue(i);
+                            else if (message.data.id === "indices") {
+                                vertexData.indices = message.data.value;
                             }
-                            _self.postMessage({ id: kind, value: babylonData }, [babylonData.buffer]);
-                        }
-                        finally {
-                            decoderModule.destroy(dracoData);
-                        }
-                    };
-
-                    if (attributes) {
-                        for (const kind in attributes) {
-                            const id = attributes[kind];
-                            const attribute = decoder.GetAttributeByUniqueId(geometry, id);
-                            processAttribute(kind, attribute);
-                        }
-                    }
-                    else {
-                        for (const kind in nativeAttributeTypes) {
-                            const id = decoder.GetAttributeId(geometry, decoderModule[nativeAttributeTypes[kind]]);
-                            if (id !== -1) {
-                                const attribute = decoder.GetAttribute(geometry, id);
-                                processAttribute(kind, attribute);
+                            else {
+                                vertexData.set(message.data.value, message.data.id);
                             }
-                        }
-                    }
-                }
-                finally {
-                    if (geometry) {
-                        decoderModule.destroy(geometry);
-                    }
+                        };
 
-                    decoderModule.destroy(decoder);
-                    decoderModule.destroy(buffer);
-                }
+                        worker.addEventListener("error", onError);
+                        worker.addEventListener("message", onMessage);
 
-                _self.postMessage("done");
+                        const dataViewCopy = new Uint8Array(dataView.byteLength);
+                        dataViewCopy.set(new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength));
+
+                        worker.postMessage({ id: "decodeMesh", dataView: dataViewCopy, attributes: attributes }, [dataViewCopy.buffer]);
+                    });
+                });
             });
         }
 
-        _self.onmessage = (event) => {
-            const data = event.data;
-            switch (data.id) {
-                case "initDecoder": {
-                    initDecoder(data.decoderWasmUrl, data.decoderWasmBinary, data.fallbackUrl);
-                    break;
-                }
-                case "decodeMesh": {
-                    decodeMesh(data.dataView, data.attributes);
-                    break;
-                }
-            }
-        };
-    }
-
-    private _loadDecoderWasmBinaryAsync(): Promise<Nullable<ArrayBuffer>> {
-        const decoder = DracoCompression.Configuration.decoder;
-        if (decoder && decoder.wasmUrl && decoder.wasmBinaryUrl && typeof WebAssembly === "object") {
-            const wasmBinaryUrl = Tools.GetAbsoluteUrl(decoder.wasmBinaryUrl);
-            return new Promise((resolve, reject) => {
-                Tools.LoadFile(wasmBinaryUrl, (data) => {
-                    resolve(data as ArrayBuffer);
-                }, undefined, undefined, true, (request, exception) => {
-                    reject(exception);
+        if (this._decoderModulePromise) {
+            return this._decoderModulePromise.then((decoder) => {
+                const vertexData = new VertexData();
+                decodeMesh(decoder.module, dataView, attributes, (indices) => {
+                    vertexData.indices = indices;
+                }, (kind, data) => {
+                    vertexData.set(data, kind);
                 });
+                return vertexData;
             });
         }
-        else {
-            return Promise.resolve(null);
-        }
+
+        throw new Error("Draco decoder module is not available");
     }
 }