Browse Source

Initial push

David Catuhe 5 năm trước cách đây
mục cha
commit
4710443be3
34 tập tin đã thay đổi với 1521 bổ sung1165 xóa
  1. 13 0
      .vscode/launch.json
  2. 3 1
      Tools/Config/config.json
  3. 25 3
      Tools/DevLoader/BabylonLoader.js
  4. 12 2
      Tools/Gulp/tasks/gulpTasks-libraries.js
  5. 1 1
      dist/preview release/babylon.d.ts
  6. 1 1
      dist/preview release/babylon.js
  7. 1155 1048
      dist/preview release/babylon.max.js
  8. 1 1
      dist/preview release/babylon.max.js.map
  9. 2 2
      dist/preview release/babylon.module.d.ts
  10. 56 0
      localDev/index-worker.html
  11. 62 0
      localDev/worker.js
  12. 2 2
      package.json
  13. 13 6
      src/Cameras/Inputs/BaseCameraPointersInput.ts
  14. 3 1
      src/Cameras/Inputs/arcRotateCameraVRDeviceOrientationInput.ts
  15. 4 2
      src/Cameras/Inputs/freeCameraDeviceOrientationInput.ts
  16. 3 0
      src/Cameras/VR/vrExperienceHelper.ts
  17. 4 1
      src/Cameras/VR/webVRCamera.ts
  18. 8 6
      src/Engines/Extensions/engine.cubeTexture.ts
  19. 1 1
      src/Engines/Extensions/engine.dynamicTexture.ts
  20. 2 1
      src/Engines/Extensions/engine.videoTexture.ts
  21. 6 5
      src/Engines/Extensions/engine.webVR.ts
  22. 43 43
      src/Engines/engine.ts
  23. 29 12
      src/Engines/thinEngine.ts
  24. 4 2
      src/Gamepads/gamepadManager.ts
  25. 3 1
      src/Inputs/scene.inputManager.ts
  26. 6 3
      src/Materials/Textures/dynamicTexture.ts
  27. 3 3
      src/Materials/Textures/internalTexture.ts
  28. 2 2
      src/Materials/Textures/texture.ts
  29. 6 7
      src/Meshes/Builders/groundBuilder.ts
  30. 4 5
      src/Meshes/mesh.ts
  31. 22 0
      src/Misc/canvasGenerator.ts
  32. 20 2
      src/Misc/fileTools.ts
  33. 1 0
      src/Misc/index.ts
  34. 1 1
      src/Misc/tools.ts

+ 13 - 0
.vscode/launch.json

@@ -132,6 +132,19 @@
             ]
         },
         {
+            "name": "Launch Local Dev - Worker mode (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/localDev/index-worker.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis"
+            ]
+        },
+        {
             "name": "Launch Local Dev (Experimental Firefox)",
             "type": "firefox",
             "request": "launch",

+ 3 - 1
Tools/Config/config.json

@@ -534,6 +534,7 @@
             }
         ],
         "build": {
+            "ignoreInWorkerMode": true,
             "ignoreInTestMode": true,
             "mainFolder": "./inspector/",
             "uncheckedLintImports": [
@@ -593,7 +594,8 @@
                 "entry": "./legacy/legacy.ts"
             }
         ],
-        "build": {
+        "build": {            
+            "ignoreInWorkerMode": true,
             "ignoreInTestMode": true,
             "mainFolder": "./nodeEditor/",
             "uncheckedLintImports": [

+ 25 - 3
Tools/DevLoader/BabylonLoader.js

@@ -33,6 +33,7 @@ var BABYLONDEVTOOLS;
         var dependencies;
         var useDist;
         var testMode;
+        var workerMode;
         var min;
         var babylonJSPath;
 
@@ -44,8 +45,14 @@ var BABYLONDEVTOOLS;
             esmQueue = [];
             dependencies = [];
             callback = null;
-            min = (document.location.href.toLowerCase().indexOf('dist=min') > 0);
-            useDist = (min || useDist || document.location.href.toLowerCase().indexOf('dist=true') > 0);
+            if (typeof document !== "undefined") {
+                min = document.location.href.toLowerCase().indexOf('dist=min') > 0;
+                useDist = (min || useDist || document.location.href.toLowerCase().indexOf('dist=true') > 0);
+            } else {
+                min = false;
+                useDist = false;
+                workerMode = true;
+            }
             babylonJSPath = '';
         }
 
@@ -108,7 +115,7 @@ var BABYLONDEVTOOLS;
         }
 
         Loader.prototype.dequeue = function() {
-            if (queue.length + esmQueue.length == 0) {
+            if (queue.length + esmQueue.length === 0) {
                 console.log('Scripts loaded');
                 BABYLON.Engine.ShadersRepository = "/src/Shaders/";
                 if (callback) {
@@ -117,6 +124,14 @@ var BABYLONDEVTOOLS;
                 return;
             }
 
+            if (typeof document === "undefined") {
+                let url = esmQueue.length ? esmQueue.shift() : queue.shift();
+                console.log(url);
+                importScripts(url);
+                this.dequeue();    
+                return;
+            } 
+
             var head = document.getElementsByTagName('head')[0];
             var script = document.createElement('script');
 
@@ -169,6 +184,9 @@ var BABYLONDEVTOOLS;
             distFolder += "/";
             
             if (!useDist) {
+                if (workerMode && module.build.ignoreInWorkerMode) {
+                    return;
+                }
                 var tempDirectory = '/.temp/' + localDevUMDFolderName + distFolder;
                 this.loadScript((babylonJSPath + tempDirectory + library.output)
                     .replace(".min.", ".")
@@ -191,6 +209,10 @@ var BABYLONDEVTOOLS;
         }
 
         Loader.prototype.loadCoreDev = function() {
+            if (typeof document === "undefined") {                
+                this.loadScript(babylonJSPath + "/dist/preview release/babylon.max.js");
+                return;
+            }
             // Es6 core import
             this.loadESMScript(babylonJSPath + "/.temp/" + localDevES6FolderName + "/core/Legacy/legacy.js");
         }

+ 12 - 2
Tools/Gulp/tasks/gulpTasks-libraries.js

@@ -166,7 +166,7 @@ var processDTSFiles = function(libraries, settings, cb) {
 /**
  * Dynamic module creation In Serie for WebPack leaks.
  */
-function buildExternalLibraries(settings) {
+function buildExternalLibraries(settings, fast) {
     // Creates the required tasks.
     var tasks = [];
 
@@ -182,7 +182,11 @@ function buildExternalLibraries(settings) {
         appendLoseDTS.push(function() { return appendLoseDTSFiles(settings, false) });
     }
 
-    tasks.push(cleanup, shaders, buildMin, buildMax, buildAMDDTS, processDTS, ...appendLoseDTS);
+    if (fast) {
+        tasks.push(cleanup, shaders, buildMax);
+    } else {
+        tasks.push(cleanup, shaders, buildMin, buildMax, buildAMDDTS, processDTS, ...appendLoseDTS);
+    }
 
     return gulp.series.apply(this, tasks);
 }
@@ -202,6 +206,12 @@ config.modules.map(function(module) {
 gulp.task("typescript", gulp.series("core"));
 
 /**
+ * Build the releasable files.
+ * Back Compat Only, now name core as it is a lib
+ */
+gulp.task("core-workers", buildExternalLibraries(config["core"], true));
+
+/**
  * Build all libs.
  */
 gulp.task("typescript-libraries", gulp.series(config.modules, config.viewerModules));

+ 1 - 1
dist/preview release/babylon.d.ts

@@ -29207,7 +29207,7 @@ declare module BABYLON {
          * Gets host window
          * @returns the host window object
          */
-        getHostWindow(): Window;
+        getHostWindow(): Nullable<Window>;
         /**
          * Gets the current render width
          * @param useScreen defines if screen size must be used (or the current render target if any)

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/babylon.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1155 - 1048
dist/preview release/babylon.max.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/babylon.max.js.map


+ 2 - 2
dist/preview release/babylon.module.d.ts

@@ -30079,7 +30079,7 @@ declare module "babylonjs/Engines/thinEngine" {
          * Gets host window
          * @returns the host window object
          */
-        getHostWindow(): Window;
+        getHostWindow(): Nullable<Window>;
         /**
          * Gets the current render width
          * @param useScreen defines if screen size must be used (or the current render target if any)
@@ -96458,7 +96458,7 @@ declare module BABYLON {
          * Gets host window
          * @returns the host window object
          */
-        getHostWindow(): Window;
+        getHostWindow(): Nullable<Window>;
         /**
          * Gets the current render width
          * @param useScreen defines if screen size must be used (or the current render target if any)

+ 56 - 0
localDev/index-worker.html

@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+    <title>Local Development - Worker mode</title>
+
+    <style>
+        html,
+        body {
+            width: 100%;
+            height: 100%;
+            padding: 0;
+            margin: 0;
+            overflow: hidden;
+        }
+
+        #renderCanvas {
+            width: 100%;
+            height: 100%;
+            display: block;
+            font-size: 0;
+        }
+
+        #fps {
+            position: absolute;
+            background-color: black;
+            border: 2px solid red;
+            text-align: center;
+            font-size: 16px;
+            color: white;
+            top: 15px;
+            right: 10px;
+            width: 60px;
+            height: 20px;
+        }
+    </style>
+</head>
+
+<body>
+    <div id="fps">0</div>
+    <canvas id="renderCanvas" touch-action="none"></canvas>
+
+    <script>
+        var canvas = document.getElementById("renderCanvas");
+
+        canvas.width = canvas.clientWidth;
+        canvas.height = canvas.clientHeight;
+
+        var offscreen = canvas.transferControlToOffscreen();
+
+        var worker = new Worker("worker.js"); 
+        worker.postMessage({canvas: offscreen}, [offscreen]);
+    </script>
+</body>
+
+</html>

+ 62 - 0
localDev/worker.js

@@ -0,0 +1,62 @@
+importScripts('../Tools/DevLoader/BabylonLoader.js');
+
+// Global to simulate PG.
+var engine = null;
+var canvas = null;
+
+onmessage = function(evt) {
+    canvas = evt.data.canvas;
+    
+    // Load the scripts + map file to allow vscode debug.
+    BABYLONDEVTOOLS.Loader
+        .require("src/index.js")
+        .load(function() {
+            if (typeof createEngine !== "undefined") {
+                engine = createEngine();
+            } else {
+                engine = new BABYLON.Engine(canvas, true, { premultipliedAlpha: false, stencil: true, disableWebGL2Support: false, preserveDrawingBuffer: true });
+            }
+
+            // call the scene creation from the js.
+            if (typeof delayCreateScene !== "undefined") {
+                var scene = delayCreateScene();
+
+                if (scene) {
+                    // Register a render loop to repeatedly render the scene
+
+                    engine.runRenderLoop(function() {
+                        if (scene.activeCamera) {
+                            scene.render();
+                        }
+                        // divFps.innerHTML = engine.getFps().toFixed() + " fps";
+                    });
+                }
+            }
+            else {
+                var scene = createScene();
+
+                if (scene) {
+
+                    var processCurrentScene = function(scene) {
+                        engine.runRenderLoop(function() {
+                            scene.render();
+                            //divFps.innerHTML = engine.getFps().toFixed() + " fps";
+                        });
+                    }
+
+                    if (scene.then) {
+                        // Handle if createScene returns a promise
+                        scene.then(function(currentScene) {
+                            processCurrentScene(currentScene);
+                        }).catch(function(e) {
+                            console.error(e);
+                            onError();
+                        });
+                    } else {
+                        // Register a render loop to repeatedly render the scene
+                        processCurrentScene(scene);
+                    }
+                }
+            }
+        });
+    }

+ 2 - 2
package.json

@@ -98,7 +98,7 @@
         "tslib": "^1.9.3",
         "tslint": "^5.11.0",
         "typedoc": "^0.15.0",
-        "typescript": "~3.5.1",
+        "typescript": "~3.6.3",
         "webpack": "^4.29.3",
         "webpack-bundle-analyzer": "^3.1.0",
         "webpack-cli": "^3.3.9",
@@ -107,4 +107,4 @@
         "xhr2": "^0.1.4",
         "xmlbuilder": "8.2.2"
     }
-}
+}

+ 13 - 6
src/Cameras/Inputs/BaseCameraPointersInput.ts

@@ -228,9 +228,13 @@ export abstract class BaseCameraPointersInput implements ICameraInput<Camera> {
         element.addEventListener("contextmenu",
             <EventListener>this.onContextMenu.bind(this), false);
 
-        Tools.RegisterTopRootEvents(this.camera.getScene().getEngine().getHostWindow(), [
-            { name: "blur", handler: this._onLostFocus }
-        ]);
+        let hostWindow = this.camera.getScene().getEngine().getHostWindow();
+
+        if (hostWindow) {
+            Tools.RegisterTopRootEvents(hostWindow, [
+                { name: "blur", handler: this._onLostFocus }
+            ]);
+        }
     }
 
     /**
@@ -239,9 +243,12 @@ export abstract class BaseCameraPointersInput implements ICameraInput<Camera> {
      */
     public detachControl(element: Nullable<HTMLElement>): void {
         if (this._onLostFocus) {
-            Tools.UnregisterTopRootEvents(this.camera.getScene().getEngine().getHostWindow(), [
-                { name: "blur", handler: this._onLostFocus }
-            ]);
+            let hostWindow = this.camera.getScene().getEngine().getHostWindow();
+            if (hostWindow) {
+                Tools.UnregisterTopRootEvents(hostWindow, [
+                    { name: "blur", handler: this._onLostFocus }
+                ]);
+            }
         }
 
         if (element && this._observer) {

+ 3 - 1
src/Cameras/Inputs/arcRotateCameraVRDeviceOrientationInput.ts

@@ -66,7 +66,9 @@ export class ArcRotateCameraVRDeviceOrientationInput implements ICameraInput<Arc
 
         let hostWindow = this.camera.getScene().getEngine().getHostWindow();
 
-        hostWindow.addEventListener("deviceorientation", this._deviceOrientationHandler);
+        if (hostWindow) {
+            hostWindow.addEventListener("deviceorientation", this._deviceOrientationHandler);
+        }
     }
 
     /** @hidden */

+ 4 - 2
src/Cameras/Inputs/freeCameraDeviceOrientationInput.ts

@@ -120,8 +120,10 @@ export class FreeCameraDeviceOrientationInput implements ICameraInput<FreeCamera
 
         let hostWindow = this.camera.getScene().getEngine().getHostWindow();
 
-        hostWindow.addEventListener("orientationchange", this._orientationChanged);
-        hostWindow.addEventListener("deviceorientation", this._deviceOrientation);
+        if (hostWindow) {
+            hostWindow.addEventListener("orientationchange", this._orientationChanged);
+            hostWindow.addEventListener("deviceorientation", this._deviceOrientation);
+        }
 
         //In certain cases, the attach control is called AFTER orientation was changed,
         //So this is needed.

+ 3 - 0
src/Cameras/VR/vrExperienceHelper.ts

@@ -784,6 +784,9 @@ export class VRExperienceHelper {
         // Window events
 
         let hostWindow = this._scene.getEngine().getHostWindow();
+        if (!hostWindow) {
+            return;
+        }
 
         hostWindow.addEventListener("resize", this._onResize);
         document.addEventListener("fullscreenchange", this._onFullscreenChange, false);

+ 4 - 1
src/Cameras/VR/webVRCamera.ts

@@ -510,7 +510,10 @@ export class WebVRFreeCamera extends FreeCamera implements PoseControlled {
         }
 
         let hostWindow = this._scene.getEngine().getHostWindow();
-        hostWindow.addEventListener('vrdisplaypresentchange', this._detachIfAttached);
+
+        if (hostWindow) {
+            hostWindow.addEventListener('vrdisplaypresentchange', this._detachIfAttached);
+        }
     }
 
     /**

+ 8 - 6
src/Engines/Extensions/engine.cubeTexture.ts

@@ -168,14 +168,16 @@ ThinEngine.prototype._cascadeLoadImgs = function(scene: Nullable<Scene>,
 ThinEngine.prototype._partialLoadImg = function(url: string, index: number, loadedImages: HTMLImageElement[], scene: Nullable<Scene>,
     onfinish: (images: HTMLImageElement[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void> = null, mimeType?: string) {
 
-    var img: HTMLImageElement;
+    var img: Nullable<HTMLImageElement>;
 
     var onload = () => {
-        loadedImages[index] = img;
-        (<any>loadedImages)._internalCount++;
+        if (img) {
+            loadedImages[index] = img;
+            (<any>loadedImages)._internalCount++;
 
-        if (scene) {
-            scene._removePendingData(img);
+            if (scene) {
+                scene._removePendingData(img);
+            }
         }
 
         if ((<any>loadedImages)._internalCount === 6) {
@@ -194,7 +196,7 @@ ThinEngine.prototype._partialLoadImg = function(url: string, index: number, load
     };
 
     img = FileTools.LoadImage(url, onload, onerror, scene ? scene.offlineProvider : null, mimeType);
-    if (scene) {
+    if (scene && img) {
         scene._addPendingData(img);
     }
 };

+ 1 - 1
src/Engines/Extensions/engine.dynamicTexture.ts

@@ -23,7 +23,7 @@ declare module "../../Engines/thinEngine" {
          * @param format defines the format of the data
          * @param forceBindTexture if the texture should be forced to be bound eg. after a graphics context loss (Default: false)
          */
-        updateDynamicTexture(texture: Nullable<InternalTexture>, canvas: HTMLCanvasElement, invertY: boolean, premulAlpha?: boolean, format?: number, forceBindTexture?: boolean): void;
+        updateDynamicTexture(texture: Nullable<InternalTexture>, canvas: HTMLCanvasElement | OffscreenCanvas, invertY: boolean, premulAlpha?: boolean, format?: number, forceBindTexture?: boolean): void;
     }
 }
 

+ 2 - 1
src/Engines/Extensions/engine.videoTexture.ts

@@ -1,6 +1,7 @@
 import { ThinEngine } from "../../Engines/thinEngine";
 import { InternalTexture } from '../../Materials/Textures/internalTexture';
 import { Nullable } from '../../types';
+import { CanvasGenerator } from '../../Misc/canvasGenerator';
 
 declare module "../../Engines/thinEngine" {
     export interface ThinEngine {
@@ -37,7 +38,7 @@ ThinEngine.prototype.updateVideoTexture = function(texture: Nullable<InternalTex
         // Copy video through the current working canvas if video texture is not supported
         if (!this._videoTextureSupported) {
             if (!texture._workingCanvas) {
-                texture._workingCanvas = document.createElement("canvas");
+                texture._workingCanvas = CanvasGenerator.CreateCanvas(texture.width, texture.height);
                 let context = texture._workingCanvas.getContext("2d");
 
                 if (!context) {

+ 6 - 5
src/Engines/Extensions/engine.webVR.ts

@@ -140,9 +140,11 @@ Engine.prototype.initWebVRAsync = function(): Promise<IDisplayChangedEventArgs>
             this._vrExclusivePointerMode = this._vrDisplay && this._vrDisplay.isPresenting;
         };
         let hostWindow = this.getHostWindow();
-        hostWindow.addEventListener('vrdisplayconnect', this._onVrDisplayConnect);
-        hostWindow.addEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
-        hostWindow.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
+        if (hostWindow) {
+            hostWindow.addEventListener('vrdisplayconnect', this._onVrDisplayConnect);
+            hostWindow.addEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
+            hostWindow.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
+        }
     }
     this._webVRInitPromise = this._webVRInitPromise || this._getVRDisplaysAsync();
     this._webVRInitPromise.then(notifyObservers);
@@ -245,8 +247,7 @@ Engine.prototype._connectVREvents = function(canvas?: HTMLCanvasElement, documen
     };
 
     if (DomManagement.IsWindowObjectExist()) {
-        let hostWindow = this.getHostWindow();
-
+        let hostWindow = this.getHostWindow()!;
         hostWindow.addEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted, false);
         hostWindow.addEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted, false);
     }

+ 43 - 43
src/Engines/engine.ts

@@ -511,61 +511,61 @@ export class Engine extends ThinEngine {
                 this.onCanvasPointerOutObservable.notifyObservers(ev);
             };
 
+            canvas.addEventListener("pointerout", this._onCanvasPointerOut);
+
             if (DomManagement.IsWindowObjectExist()) {
-                let hostWindow = this.getHostWindow();
+                let hostWindow = this.getHostWindow()!;
                 hostWindow.addEventListener("blur", this._onBlur);
                 hostWindow.addEventListener("focus", this._onFocus);
-            }
 
-            canvas.addEventListener("pointerout", this._onCanvasPointerOut);
+                let anyDoc = document as any;
 
-            let anyDoc = document as any;
+                // Fullscreen
+                this._onFullscreenChange = () => {
 
-            // Fullscreen
-            this._onFullscreenChange = () => {
+                    if (anyDoc.fullscreen !== undefined) {
+                        this.isFullscreen = anyDoc.fullscreen;
+                    } else if (anyDoc.mozFullScreen !== undefined) {
+                        this.isFullscreen = anyDoc.mozFullScreen;
+                    } else if (anyDoc.webkitIsFullScreen !== undefined) {
+                        this.isFullscreen = anyDoc.webkitIsFullScreen;
+                    } else if (anyDoc.msIsFullScreen !== undefined) {
+                        this.isFullscreen = anyDoc.msIsFullScreen;
+                    }
 
-                if (anyDoc.fullscreen !== undefined) {
-                    this.isFullscreen = anyDoc.fullscreen;
-                } else if (anyDoc.mozFullScreen !== undefined) {
-                    this.isFullscreen = anyDoc.mozFullScreen;
-                } else if (anyDoc.webkitIsFullScreen !== undefined) {
-                    this.isFullscreen = anyDoc.webkitIsFullScreen;
-                } else if (anyDoc.msIsFullScreen !== undefined) {
-                    this.isFullscreen = anyDoc.msIsFullScreen;
-                }
+                    // Pointer lock
+                    if (this.isFullscreen && this._pointerLockRequested && canvas) {
+                        Engine._RequestPointerlock(canvas);
+                    }
+                };
+
+                document.addEventListener("fullscreenchange", this._onFullscreenChange, false);
+                document.addEventListener("mozfullscreenchange", this._onFullscreenChange, false);
+                document.addEventListener("webkitfullscreenchange", this._onFullscreenChange, false);
+                document.addEventListener("msfullscreenchange", this._onFullscreenChange, false);
 
                 // Pointer lock
-                if (this.isFullscreen && this._pointerLockRequested && canvas) {
-                    Engine._RequestPointerlock(canvas);
+                this._onPointerLockChange = () => {
+                    this.isPointerLock = (anyDoc.mozPointerLockElement === canvas ||
+                        anyDoc.webkitPointerLockElement === canvas ||
+                        anyDoc.msPointerLockElement === canvas ||
+                        anyDoc.pointerLockElement === canvas
+                    );
+                };
+
+                document.addEventListener("pointerlockchange", this._onPointerLockChange, false);
+                document.addEventListener("mspointerlockchange", this._onPointerLockChange, false);
+                document.addEventListener("mozpointerlockchange", this._onPointerLockChange, false);
+                document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);
+
+                // Create Audio Engine if needed.
+                if (!Engine.audioEngine && options.audioEngine && Engine.AudioEngineFactory) {
+                    Engine.audioEngine = Engine.AudioEngineFactory(this.getRenderingCanvas());
                 }
-            };
-
-            document.addEventListener("fullscreenchange", this._onFullscreenChange, false);
-            document.addEventListener("mozfullscreenchange", this._onFullscreenChange, false);
-            document.addEventListener("webkitfullscreenchange", this._onFullscreenChange, false);
-            document.addEventListener("msfullscreenchange", this._onFullscreenChange, false);
-
-            // Pointer lock
-            this._onPointerLockChange = () => {
-                this.isPointerLock = (anyDoc.mozPointerLockElement === canvas ||
-                    anyDoc.webkitPointerLockElement === canvas ||
-                    anyDoc.msPointerLockElement === canvas ||
-                    anyDoc.pointerLockElement === canvas
-                );
-            };
-
-            document.addEventListener("pointerlockchange", this._onPointerLockChange, false);
-            document.addEventListener("mspointerlockchange", this._onPointerLockChange, false);
-            document.addEventListener("mozpointerlockchange", this._onPointerLockChange, false);
-            document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);
+            }
 
             this._connectVREvents();
 
-            // Create Audio Engine if needed.
-            if (!Engine.audioEngine && options.audioEngine && Engine.AudioEngineFactory) {
-                Engine.audioEngine = Engine.AudioEngineFactory(this.getRenderingCanvas());
-            }
-
             this.enableOfflineSupport = Engine.OfflineProviderFactory !== undefined;
 
             if (!options.doNotHandleTouchAction) {
@@ -1936,7 +1936,7 @@ export class Engine extends ThinEngine {
     }
 
     private _disableTouchAction(): void {
-        if (!this._renderingCanvas) {
+        if (!this._renderingCanvas || !this._renderingCanvas.setAttribute) {
             return;
         }
 

+ 29 - 12
src/Engines/thinEngine.ts

@@ -29,6 +29,7 @@ import { BaseTexture } from '../Materials/Textures/baseTexture';
 import { IOfflineProvider } from '../Offline/IOfflineProvider';
 import { IEffectFallbacks } from '../Materials/iEffectFallbacks';
 import { IWebRequest } from '../Misc/interfaces/iWebRequest';
+import { CanvasGenerator } from '../Misc/canvasGenerator';
 
 declare type Observer<T> = import("../Misc/observable").Observer<T>;
 declare type VideoTexture = import("../Materials/Textures/videoTexture").VideoTexture;
@@ -339,9 +340,9 @@ export class ThinEngine {
     private _textureUnits: Int32Array;
 
     /** @hidden */
-    public _workingCanvas: Nullable<HTMLCanvasElement>;
+    public _workingCanvas: Nullable<HTMLCanvasElement | OffscreenCanvas>;
     /** @hidden */
-    public _workingContext: Nullable<CanvasRenderingContext2D>;
+    public _workingContext: Nullable<CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D>;
 
     /** @hidden */
     public _bindedRenderFunction: any;
@@ -910,7 +911,7 @@ export class ThinEngine {
             return;
         }
 
-        this._workingCanvas = document.createElement("canvas");
+        this._workingCanvas = CanvasGenerator.CreateCanvas(1, 1);
         let context = this._workingCanvas.getContext("2d");
 
         if (context) {
@@ -1040,7 +1041,11 @@ export class ThinEngine {
      * Gets host window
      * @returns the host window object
      */
-    public getHostWindow(): Window {
+    public getHostWindow(): Nullable<Window> {
+        if (!DomManagement.IsWindowObjectExist()) {
+            return null;
+        }
+
         if (this._renderingCanvas && this._renderingCanvas.ownerDocument && this._renderingCanvas.ownerDocument.defaultView) {
             return this._renderingCanvas.ownerDocument.defaultView;
         }
@@ -1180,8 +1185,16 @@ export class ThinEngine {
      * Resize the view according to the canvas' size
      */
     public resize(): void {
-        var width = this._renderingCanvas ? this._renderingCanvas.clientWidth : window.innerWidth;
-        var height = this._renderingCanvas ? this._renderingCanvas.clientHeight : window.innerHeight;
+        let width: number;
+        let height: number;
+
+        if (DomManagement.IsWindowObjectExist()) {
+            width = this._renderingCanvas ? this._renderingCanvas.clientWidth : window.innerWidth;
+            height = this._renderingCanvas ? this._renderingCanvas.clientHeight : window.innerHeight;
+        } else {
+            width = this._renderingCanvas ? this._renderingCanvas.width : 100;
+            height = this._renderingCanvas ? this._renderingCanvas.height : 100;
+        }
 
         this.setSize(width / this._hardwareScalingLevel, height / this._hardwareScalingLevel);
     }
@@ -2743,7 +2756,7 @@ export class ThinEngine {
      */
     public createTexture(urlArg: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<ISceneLike>, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
         onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
-        buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
+        buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
         forcedExtension: Nullable<string> = null, excludeLoaders: Array<IInternalTextureLoader> = [], mimeType?: string): InternalTexture {
         var url = String(urlArg); // assign a new string, so that the original is still available in case of fallback
         var fromData = url.substr(0, 5) === "data:";
@@ -2851,7 +2864,7 @@ export class ThinEngine {
                 }
             }
         } else {
-            var onload = (img: HTMLImageElement) => {
+            var onload = (img: HTMLImageElement | ImageBitmap) => {
                 if (fromBlob && !this._doNotHandleContextLost) {
                     // We need to store the image if we need to rebuild the texture
                     // in case of a webgl context lost
@@ -2905,8 +2918,8 @@ export class ThinEngine {
             };
 
             if (!fromData || isBase64) {
-                if (buffer instanceof HTMLImageElement) {
-                    onload(buffer);
+                if (buffer && (<HTMLImageElement>buffer).decoding) {
+                    onload(<HTMLImageElement>buffer);
                 } else {
                     FileTools.LoadImage(url, onload, onInternalError, scene ? scene.offlineProvider : null, mimeType);
                 }
@@ -4225,8 +4238,8 @@ export class ThinEngine {
      */
     public static isSupported(): boolean {
         try {
-            var tempcanvas = document.createElement("canvas");
-            var gl = tempcanvas.getContext("webgl") || tempcanvas.getContext("experimental-webgl");
+            var tempcanvas = CanvasGenerator.CreateCanvas(1, 1);
+            var gl = tempcanvas.getContext("webgl") || (tempcanvas as any).getContext("experimental-webgl");
 
             return gl != null && !!window.WebGLRenderingContext;
         } catch (e) {
@@ -4309,6 +4322,10 @@ export class ThinEngine {
      */
     public static QueueNewFrame(func: () => void, requester?: any): number {
         if (!DomManagement.IsWindowObjectExist()) {
+            if (typeof requestAnimationFrame !== "undefined") {
+                return requestAnimationFrame(func);
+            }
+
             return setTimeout(func, 16);
         }
 

+ 4 - 2
src/Gamepads/gamepadManager.ts

@@ -104,8 +104,10 @@ export class GamepadManager {
             if (this._gamepadEventSupported) {
                 let hostWindow = this._scene ? this._scene.getEngine().getHostWindow() : window;
 
-                hostWindow.addEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
-                hostWindow.addEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent, false);
+                if (hostWindow) {
+                    hostWindow.addEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
+                    hostWindow.addEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent, false);
+                }
             }
             else {
                 this._startMonitoringGamepads();

+ 3 - 1
src/Inputs/scene.inputManager.ts

@@ -804,7 +804,9 @@ export class InputManager {
 
         if (attachUp) {
             let hostWindow = scene.getEngine().getHostWindow();
-            hostWindow.addEventListener(eventPrefix + "up", <any>this._onPointerUp, false);
+            if (hostWindow) {
+                hostWindow.addEventListener(eventPrefix + "up", <any>this._onPointerUp, false);
+            }
         }
     }
 

+ 6 - 3
src/Materials/Textures/dynamicTexture.ts

@@ -7,6 +7,7 @@ import { Texture } from "../../Materials/Textures/texture";
 import { _TimeToken } from "../../Instrumentation/timeToken";
 import { Constants } from "../../Engines/constants";
 import "../../Engines/Extensions/engine.dynamicTexture";
+import { CanvasGenerator } from '../../Misc/canvasGenerator';
 
 /**
  * A class extending Texture allowing drawing on a texture
@@ -14,7 +15,7 @@ import "../../Engines/Extensions/engine.dynamicTexture";
  */
 export class DynamicTexture extends Texture {
     private _generateMipMaps: boolean;
-    private _canvas: HTMLCanvasElement;
+    private _canvas: HTMLCanvasElement | OffscreenCanvas;
     private _context: CanvasRenderingContext2D;
     private _engine: Engine;
 
@@ -42,7 +43,7 @@ export class DynamicTexture extends Texture {
             this._canvas = options;
             this._texture = this._engine.createDynamicTexture(options.width, options.height, generateMipMaps, samplingMode);
         } else {
-            this._canvas = document.createElement("canvas");
+            this._canvas = CanvasGenerator.CreateCanvas(1, 1);
 
             if (options.width || options.width === 0) {
                 this._texture = this._engine.createDynamicTexture(options.width, options.height, generateMipMaps, samplingMode);
@@ -206,7 +207,9 @@ export class DynamicTexture extends Texture {
         }
 
         const serializationObject = super.serialize();
-        serializationObject.base64String = this._canvas.toDataURL();
+        if ((this._canvas as HTMLCanvasElement).toDataURL) {
+            serializationObject.base64String = (this._canvas as HTMLCanvasElement).toDataURL();
+        }
 
         serializationObject.invertY = this._invertY;
         serializationObject.samplingMode = this.samplingMode;

+ 3 - 3
src/Materials/Textures/internalTexture.ts

@@ -160,7 +160,7 @@ export class InternalTexture {
     /** @hidden */
     public _source = InternalTextureSource.Unknown;
     /** @hidden */
-    public _buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob> = null;
+    public _buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null;
     /** @hidden */
     public _bufferView: Nullable<ArrayBufferView> = null;
     /** @hidden */
@@ -174,9 +174,9 @@ export class InternalTexture {
     /** @hidden */
     public _files: Nullable<string[]> = null;
     /** @hidden */
-    public _workingCanvas: Nullable<HTMLCanvasElement> = null;
+    public _workingCanvas: Nullable<HTMLCanvasElement | OffscreenCanvas> = null;
     /** @hidden */
-    public _workingContext: Nullable<CanvasRenderingContext2D> = null;
+    public _workingContext: Nullable<CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D> = null;
     /** @hidden */
     public _framebuffer: Nullable<WebGLFramebuffer> = null;
     /** @hidden */

+ 2 - 2
src/Materials/Textures/texture.ts

@@ -218,7 +218,7 @@ export class Texture extends BaseTexture {
     protected _initialSamplingMode = Texture.BILINEAR_SAMPLINGMODE;
 
     /** @hidden */
-    public _buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob> = null;
+    public _buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null;
     private _deleteBuffer: boolean = false;
     protected _format: Nullable<number> = null;
     private _delayedOnLoad: Nullable<() => void> = null;
@@ -277,7 +277,7 @@ export class Texture extends BaseTexture {
      * @param format defines the format of the texture we are trying to load (Engine.TEXTUREFORMAT_RGBA...)
      * @param mimeType defines an optional mime type information
      */
-    constructor(url: Nullable<string>, sceneOrEngine: Nullable<Scene | ThinEngine>, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob> = null, deleteBuffer: boolean = false, format?: number, mimeType?: string) {
+    constructor(url: Nullable<string>, sceneOrEngine: Nullable<Scene | ThinEngine>, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null, deleteBuffer: boolean = false, format?: number, mimeType?: string) {
         super((sceneOrEngine && sceneOrEngine.getClassName() === "Scene") ? (sceneOrEngine as Scene) : null);
 
         this.name = url || "";

+ 6 - 7
src/Meshes/Builders/groundBuilder.ts

@@ -8,6 +8,7 @@ import { Tools } from "../../Misc/tools";
 import { Nullable } from '../../types';
 import { EngineStore } from '../../Engines/engineStore';
 import { Epsilon } from '../../Maths/math.constants';
+import { CanvasGenerator } from '../../Misc/canvasGenerator';
 
 VertexData.CreateGround = function(options: { width?: number, height?: number, subdivisions?: number, subdivisionsX?: number, subdivisionsY?: number }): VertexData {
     var indices = [];
@@ -382,9 +383,12 @@ export class GroundBuilder {
 
         ground._setReady(false);
 
-        var onload = (img: HTMLImageElement) => {
+        var onload = (img: HTMLImageElement | ImageBitmap) => {
+            var bufferWidth = img.width;
+            var bufferHeight = img.height;
+
             // Getting height map data
-            var canvas = document.createElement("canvas");
+            var canvas = CanvasGenerator.CreateCanvas(bufferWidth, bufferHeight);
             var context = canvas.getContext("2d");
 
             if (!context) {
@@ -395,11 +399,6 @@ export class GroundBuilder {
                 return;
             }
 
-            var bufferWidth = img.width;
-            var bufferHeight = img.height;
-            canvas.width = bufferWidth;
-            canvas.height = bufferHeight;
-
             context.drawImage(img, 0, 0);
 
             // Create VertexData from map data

+ 4 - 5
src/Meshes/mesh.ts

@@ -34,6 +34,7 @@ import { MeshLODLevel } from './meshLODLevel';
 import { Path3D } from '../Maths/math.path';
 import { Plane } from '../Maths/math.plane';
 import { TransformNode } from './transformNode';
+import { CanvasGenerator } from '../Misc/canvasGenerator';
 
 declare type LinesMesh = import("./linesMesh").LinesMesh;
 declare type InstancedMesh = import("./instancedMesh").InstancedMesh;
@@ -2218,14 +2219,12 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
     public applyDisplacementMap(url: string, minHeight: number, maxHeight: number, onSuccess?: (mesh: Mesh) => void, uvOffset?: Vector2, uvScale?: Vector2, forceUpdate = false): Mesh {
         var scene = this.getScene();
 
-        var onload = (img: HTMLImageElement) => {
+        var onload = (img: HTMLImageElement | ImageBitmap) => {
             // Getting height map data
-            var canvas = document.createElement("canvas");
-            var context = <CanvasRenderingContext2D>canvas.getContext("2d");
             var heightMapWidth = img.width;
             var heightMapHeight = img.height;
-            canvas.width = heightMapWidth;
-            canvas.height = heightMapHeight;
+            var canvas = CanvasGenerator.CreateCanvas(heightMapWidth, heightMapHeight);
+            var context = <CanvasRenderingContext2D>canvas.getContext("2d");
 
             context.drawImage(img, 0, 0);
 

+ 22 - 0
src/Misc/canvasGenerator.ts

@@ -0,0 +1,22 @@
+/**
+ * Helper class used to generate a canvas to manipulate images
+ */
+export class CanvasGenerator {
+    /**
+     * Create a new canvas (or offscreen canvas depending on the context)
+     * @param width defines the expected width
+     * @param height defines the expected height
+     * @return a new canvas or offscreen canvas
+     */
+    public static CreateCanvas(width: number, height: number): HTMLCanvasElement | OffscreenCanvas {
+        if (typeof document === "undefined") {
+            return new OffscreenCanvas(width, height);
+        }
+
+        let canvas = document.createElement("canvas");
+        canvas.width = width;
+        canvas.height = height;
+
+        return canvas;
+    }
+}

+ 20 - 2
src/Misc/fileTools.ts

@@ -88,13 +88,13 @@ export class FileTools {
      * @param mimeType optional mime type
      * @returns the HTMLImageElement of the loaded image
      */
-    public static LoadImage(input: string | ArrayBuffer | ArrayBufferView | Blob, onLoad: (img: HTMLImageElement) => void, onError: (message?: string, exception?: any) => void, offlineProvider: Nullable<IOfflineProvider>, mimeType?: string): HTMLImageElement {
+    public static LoadImage(input: string | ArrayBuffer | ArrayBufferView | Blob, onLoad: (img: HTMLImageElement | ImageBitmap) => void, onError: (message?: string, exception?: any) => void, offlineProvider: Nullable<IOfflineProvider>, mimeType?: string): Nullable<HTMLImageElement> {
         let url: string;
         let usingObjectURL = false;
 
         if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) {
             if (typeof Blob !== 'undefined') {
-               url = URL.createObjectURL(new Blob([input]));
+                url = URL.createObjectURL(new Blob([input]));
                 usingObjectURL = true;
             } else {
                 url = `data:${mimeType || "image/jpg"};base64,` + this._ArrayBufferToBase64(input);
@@ -109,6 +109,24 @@ export class FileTools {
             url = this.PreprocessUrl(input);
         }
 
+        if (typeof Image === "undefined") {
+            this.LoadFile(url, data => {
+                createImageBitmap(new Blob([data])).then(imgBmp => {
+                    onLoad(imgBmp);
+                }).catch(reason => {
+                    if (onError) {
+                        onError("Error while trying to load image: " + input, reason);
+                    }    
+                })
+            }, undefined, offlineProvider || undefined, true, (request, exception) => {
+                if (onError) {
+                    onError("Error while trying to load image: " + input, exception);
+                }
+            });
+
+            return null;
+        }
+
         var img = new Image();
         this.SetCorsBehavior(url, img);
 

+ 1 - 0
src/Misc/index.ts

@@ -41,3 +41,4 @@ export * from "./customAnimationFrameRequester";
 export * from "./retryStrategy";
 export * from "./loadFileError";
 export * from "./interfaces/screenshotSize";
+export * from "./canvasGenerator";

+ 1 - 1
src/Misc/tools.ts

@@ -363,7 +363,7 @@ export class Tools {
     * @param mimeType optional mime type
     * @returns the HTMLImageElement of the loaded image
     */
-    public static LoadImage(input: string | ArrayBuffer | Blob, onLoad: (img: HTMLImageElement) => void, onError: (message?: string, exception?: any) => void, offlineProvider: Nullable<IOfflineProvider>, mimeType?: string): HTMLImageElement {
+    public static LoadImage(input: string | ArrayBuffer | Blob, onLoad: (img: HTMLImageElement | ImageBitmap) => void, onError: (message?: string, exception?: any) => void, offlineProvider: Nullable<IOfflineProvider>, mimeType?: string): Nullable<HTMLImageElement> {
         return FileTools.LoadImage(input, onLoad, onError, offlineProvider, mimeType);
     }