123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- import * as React from "react";
- import { GlobalState, RuntimeMode } from "../globalState";
- import { Engine } from "babylonjs/Engines/engine";
- import { Nullable } from "babylonjs/types";
- import { Scene } from "babylonjs/scene";
- import { Utilities } from "../tools/utilities";
- import { DownloadManager } from "../tools/downloadManager";
- import { WebGPUEngine } from "babylonjs";
- require("../scss/rendering.scss");
- interface IRenderingComponentProps {
- globalState: GlobalState;
- }
- declare const Ammo: any;
- export class RenderingComponent extends React.Component<IRenderingComponentProps> {
- private _engine: Nullable<Engine>;
- private _scene: Nullable<Scene>;
- private _canvasRef: React.RefObject<HTMLCanvasElement>;
- private _downloadManager: DownloadManager;
- private _unityToolkitWasLoaded = false;
- public constructor(props: IRenderingComponentProps) {
- super(props);
- this._canvasRef = React.createRef();
- // Create the global handleException
- (window as any).handleException = (e: Error) => {
- console.error(e);
- this.props.globalState.onErrorObservable.notifyObservers(e);
- };
- this.props.globalState.onRunRequiredObservable.add(() => {
- this._compileAndRunAsync();
- });
- this._downloadManager = new DownloadManager(this.props.globalState);
- this.props.globalState.onDownloadRequiredObservable.add(() => {
- if (!this._engine) {
- return;
- }
- this._downloadManager.download(this._engine);
- });
- this.props.globalState.onInspectorRequiredObservable.add((state) => {
- if (!this._scene) {
- return;
- }
- if (state) {
- this._scene.debugLayer.show({
- embedMode: true,
- });
- } else {
- this._scene.debugLayer.hide();
- }
- this.props.globalState.inspectorIsOpened = state;
- });
- this.props.globalState.onFullcreenRequiredObservable.add(() => {
- this._engine?.switchFullscreen(false);
- });
- if (this.props.globalState.runtimeMode !== RuntimeMode.Editor) {
- this.props.globalState.onCodeLoaded.add((code) => {
- this.props.globalState.currentCode = code;
- this.props.globalState.onRunRequiredObservable.notifyObservers();
- });
- }
- window.addEventListener("resize", () => {
- if (!this._engine) {
- return;
- }
- this._engine.resize();
- });
- }
- private async _loadScriptAsync(url: string): Promise<void> {
- return new Promise((resolve, reject) => {
- let script = document.createElement('script');
- script.src = url;
- script.onload = () => {
- resolve();
- };
- document.head.appendChild(script);
- });
- }
- private async _compileAndRunAsync() {
- this.props.globalState.onDisplayWaitRingObservable.notifyObservers(false);
- this.props.globalState.onErrorObservable.notifyObservers(null);
- const displayInspector = this._scene?.debugLayer.isVisible();
- let useWebGPU = location.href.indexOf("webgpu") !== -1 && !!navigator.gpu;
- let forceWebGL1 = false;
- const configuredEngine = Utilities.ReadStringFromStore("engineVersion", "WebGL2");
- switch (configuredEngine) {
- case "WebGPU":
- useWebGPU = true;
- break;
- case "WebGL":
- forceWebGL1 = true;
- break;
- }
-
- if (this._engine) {
- try {
- this._engine.dispose();
- } catch (ex) {
- // just ignore
- }
- this._engine = null;
- }
- try {
- let globalObject = window as any;
- let canvas = this._canvasRef.current!;
- globalObject.canvas = canvas;
- if (useWebGPU) {
- globalObject.createDefaultEngine = async function() {
- var engine = new WebGPUEngine(canvas);
- await engine.initAsync();
- return engine;
- }
- } else {
- globalObject.createDefaultEngine = function () {
- return new Engine(canvas, true, {
- disableWebGL2Support: forceWebGL1,
- preserveDrawingBuffer: true,
- stencil: true,
- });
- };
- }
- let zipVariables = "var engine = null;\r\nvar scene = null;\r\nvar sceneToRender = null;\r\n";
- let defaultEngineZip = `var createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: ${forceWebGL1}}); }`;
- if (useWebGPU) {
- defaultEngineZip = `var createDefaultEngine = async function() {
- var engine = new BABYLON.WebGPUEngine(canvas);
- await engine.initAsync();
- return engine;
- }`;
- }
- let code = await this.props.globalState.getCompiledCode();
- if (!code) {
- return;
- }
- // Check for Ammo.js
- if (code.indexOf("AmmoJSPlugin") > -1 && typeof Ammo === "function") {
- await Ammo();
- }
- // Check for Unity Toolkit
- if ((location.href.indexOf("UnityToolkit") !== -1 || Utilities.ReadBoolFromStore("unity-toolkit", false)) && !this._unityToolkitWasLoaded) {
- await this._loadScriptAsync("/libs/babylon.manager.js");
- this._unityToolkitWasLoaded = true;
- }
- let createEngineFunction = "createDefaultEngine";
- let createSceneFunction = "";
- let checkCamera = true;
- let checkSceneCount = true;
- if (code.indexOf("createEngine") !== -1) {
- createEngineFunction = "createEngine";
- }
- // Check for different typos
- if (code.indexOf("delayCreateScene") !== -1) {
- // delayCreateScene
- createSceneFunction = "delayCreateScene";
- checkCamera = false;
- } else if (code.indexOf("createScene") !== -1) {
- // createScene
- createSceneFunction = "createScene";
- } else if (code.indexOf("CreateScene") !== -1) {
- // CreateScene
- createSceneFunction = "CreateScene";
- } else if (code.indexOf("createscene") !== -1) {
- // createscene
- createSceneFunction = "createscene";
- }
- if (!createSceneFunction) {
- this.props.globalState.onErrorObservable.notifyObservers({
- message: "You must provide a function named createScene.",
- });
- return;
- } else {
- code += `
- var engine;
- var scene;
- initFunction = async function() {
- var asyncEngineCreation = async function() {
- try {
- return ${createEngineFunction}();
- } catch(e) {
- console.log("the available createEngine function failed. Creating the default engine instead");
- return createDefaultEngine();
- }
- }
- engine = await asyncEngineCreation();`;
- code += "\r\nif (!engine) throw 'engine should not be null.';";
- if (this.props.globalState.language === "JS") {
- code += "\r\n" + "scene = " + createSceneFunction + "();";
- } else {
- var startCar = code.search("var " + createSceneFunction);
- code = code.substr(0, startCar) + code.substr(startCar + 4);
- code += "\n" + "scene = " + createSceneFunction + "();";
- }
- code += `}`;
- // Execute the code
- Utilities.FastEval(code);
- await globalObject.initFunction();
- this._engine = globalObject.engine;
- if (!this._engine) {
- this.props.globalState.onErrorObservable.notifyObservers({
- message: "createEngine function must return an engine.",
- });
- return;
- }
- if (!globalObject.scene) {
- this.props.globalState.onErrorObservable.notifyObservers({
- message: createSceneFunction + " function must return a scene.",
- });
- return;
- }
- let sceneToRenderCode = "sceneToRender = scene";
- // if scene returns a promise avoid checks
- if (globalObject.scene.then) {
- checkCamera = false;
- checkSceneCount = false;
- sceneToRenderCode = "scene.then(returnedScene => { sceneToRender = returnedScene; });\r\n";
- }
- let createEngineZip = createEngineFunction === "createEngine" ? zipVariables : zipVariables + defaultEngineZip;
- this.props.globalState.zipCode = createEngineZip + ";\r\n" + code + ";\r\ninitFunction().then(() => {" + sceneToRenderCode;
- }
- if (globalObject.scene.then) {
- globalObject.scene.then((s: Scene) => {
- this._scene = s;
- globalObject.scene = this._scene;
- });
- } else {
- this._scene = globalObject.scene as Scene;
- }
- this._engine.runRenderLoop(() => {
- if (!this._scene || !this._engine) {
- return;
- }
- if (this.props.globalState.runtimeMode === RuntimeMode.Editor && window.innerWidth > this.props.globalState.MobileSizeTrigger) {
- if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
- this._engine.resize();
- }
- }
- if (this._scene.activeCamera || this._scene.activeCameras && this._scene.activeCameras.length > 0) {
- this._scene.render();
- }
- // Update FPS if camera is not a webxr camera
- if (!(this._scene.activeCamera && this._scene.activeCamera.getClassName && this._scene.activeCamera.getClassName() === "WebXRCamera")) {
- if (this.props.globalState.runtimeMode !== RuntimeMode.Full) {
- this.props.globalState.fpsElement.innerHTML = this._engine.getFps().toFixed() + " fps";
- }
- }
- });
- if (checkSceneCount && this._engine.scenes.length === 0) {
- this.props.globalState.onErrorObservable.notifyObservers({
- message: "You must at least create a scene.",
- });
- return;
- }
- if (this._engine.scenes[0] && displayInspector) {
- this.props.globalState.onInspectorRequiredObservable.notifyObservers(true);
- }
- if (checkCamera && this._engine.scenes[0].activeCamera == null) {
- this.props.globalState.onErrorObservable.notifyObservers({
- message: "You must at least create a camera.",
- });
- return;
- } else if (globalObject.scene.then) {
- globalObject.scene.then(() => {
- if (this._engine!.scenes[0] && displayInspector) {
- this.props.globalState.onInspectorRequiredObservable.notifyObservers(true);
- }
- });
- } else {
- this._engine.scenes[0].executeWhenReady(() => {
- this.props.globalState.onRunExecutedObservable.notifyObservers();
- });
- }
- } catch (err) {
- this.props.globalState.onErrorObservable.notifyObservers(err);
- }
- }
- public render() {
- return <canvas id="renderCanvas" ref={this._canvasRef}></canvas>;
- }
- }
|