rendererComponent.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. import * as React from "react";
  2. import { GlobalState, RuntimeMode } from "../globalState";
  3. import { Engine } from "babylonjs/Engines/engine";
  4. import { Nullable } from "babylonjs/types";
  5. import { Scene } from "babylonjs/scene";
  6. import { Utilities } from "../tools/utilities";
  7. import { DownloadManager } from "../tools/downloadManager";
  8. require("../scss/rendering.scss");
  9. interface IRenderingComponentProps {
  10. globalState: GlobalState;
  11. }
  12. export class RenderingComponent extends React.Component<IRenderingComponentProps> {
  13. private _engine: Nullable<Engine>;
  14. private _scene: Nullable<Scene>;
  15. private _canvasRef: React.RefObject<HTMLCanvasElement>;
  16. private _downloadManager: DownloadManager;
  17. private _unityToolkitWasLoaded = false;
  18. public constructor(props: IRenderingComponentProps) {
  19. super(props);
  20. this._canvasRef = React.createRef();
  21. // Create the global handleException
  22. (window as any).handleException = (e: Error) => {
  23. console.error(e);
  24. this.props.globalState.onErrorObservable.notifyObservers(e);
  25. };
  26. this.props.globalState.onRunRequiredObservable.add(() => {
  27. this._compileAndRunAsync();
  28. });
  29. this._downloadManager = new DownloadManager(this.props.globalState);
  30. this.props.globalState.onDownloadRequiredObservable.add(() => {
  31. if (!this._engine) {
  32. return;
  33. }
  34. this._downloadManager.download(this._engine);
  35. });
  36. this.props.globalState.onInspectorRequiredObservable.add((state) => {
  37. if (!this._scene) {
  38. return;
  39. }
  40. if (state) {
  41. this._scene.debugLayer.show({
  42. embedMode: true,
  43. });
  44. } else {
  45. this._scene.debugLayer.hide();
  46. }
  47. this.props.globalState.inspectorIsOpened = state;
  48. });
  49. this.props.globalState.onFullcreenRequiredObservable.add(() => {
  50. this._engine?.switchFullscreen(false);
  51. });
  52. if (this.props.globalState.runtimeMode !== RuntimeMode.Editor) {
  53. this.props.globalState.onCodeLoaded.add((code) => {
  54. this.props.globalState.currentCode = code;
  55. this.props.globalState.onRunRequiredObservable.notifyObservers();
  56. });
  57. }
  58. window.addEventListener("resize", () => {
  59. if (!this._engine) {
  60. return;
  61. }
  62. this._engine.resize();
  63. });
  64. }
  65. private async _loadScriptAsync(url: string) {
  66. return new Promise((resolve, reject) => {
  67. let script = document.createElement('script');
  68. script.src = url;
  69. script.onload = () => {
  70. resolve();
  71. }
  72. document.head.appendChild(script);
  73. });
  74. }
  75. private async _compileAndRunAsync() {
  76. this.props.globalState.onDisplayWaitRingObservable.notifyObservers(false);
  77. this.props.globalState.onErrorObservable.notifyObservers(null);
  78. const displayInspector = this._scene?.debugLayer.isVisible();
  79. if (this._engine) {
  80. try {
  81. this._engine.dispose();
  82. } catch (ex) {
  83. // just ignore
  84. }
  85. this._engine = null;
  86. }
  87. try {
  88. let globalObject = window as any;
  89. let canvas = this._canvasRef.current!;
  90. globalObject.canvas = canvas;
  91. globalObject.createDefaultEngine = function () {
  92. return new Engine(canvas, true, {
  93. preserveDrawingBuffer: true,
  94. stencil: true,
  95. });
  96. };
  97. let zipVariables = "var engine = null;\r\nvar scene = null;\r\nvar sceneToRender = null;\r\n";
  98. let defaultEngineZip = "var createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true }); }";
  99. let code = await this.props.globalState.getCompiledCode();
  100. if (!code) {
  101. return;
  102. }
  103. // Check for Unity Toolkit
  104. if ((location.href.indexOf("UnityToolkit") !== -1 || Utilities.ReadBoolFromStore("unity-toolkit", false)) && !this._unityToolkitWasLoaded) {
  105. await this._loadScriptAsync("https://raw.githubusercontent.com/MackeyK24/MackeyK24.github.io/master/toolkit/babylon.manager.js");
  106. this._unityToolkitWasLoaded = true;
  107. }
  108. let createEngineFunction = "createDefaultEngine";
  109. let createSceneFunction = "";
  110. let checkCamera = true;
  111. let checkSceneCount = true;
  112. if (code.indexOf("createEngine") !== -1) {
  113. createEngineFunction = "createEngine";
  114. }
  115. // Check for different typos
  116. if (code.indexOf("delayCreateScene") !== -1) {
  117. // delayCreateScene
  118. createSceneFunction = "delayCreateScene";
  119. checkCamera = false;
  120. } else if (code.indexOf("createScene") !== -1) {
  121. // createScene
  122. createSceneFunction = "createScene";
  123. } else if (code.indexOf("CreateScene") !== -1) {
  124. // CreateScene
  125. createSceneFunction = "CreateScene";
  126. } else if (code.indexOf("createscene") !== -1) {
  127. // createscene
  128. createSceneFunction = "createscene";
  129. }
  130. if (!createSceneFunction) {
  131. this._engine = globalObject.createDefaultEngine() as Engine;
  132. this._scene = new Scene(this._engine);
  133. globalObject.engine = this._engine;
  134. globalObject.scene = this._scene;
  135. let runScript: any = null;
  136. Utilities.FastEval("runScript = function(scene, canvas) {" + code + "}");
  137. runScript(this._scene, canvas);
  138. this.props.globalState.zipCode = zipVariables + defaultEngineZip + "var engine = createDefaultEngine();" + ";\r\nvar scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
  139. } else {
  140. code += `
  141. var engine;
  142. try {
  143. engine = ${createEngineFunction}();
  144. } catch(e) {
  145. console.log("the available createEngine function failed. Creating the default engine instead");
  146. engine = createDefaultEngine();
  147. }`;
  148. code += "\r\nif (!engine) throw 'engine should not be null.';";
  149. if (this.props.globalState.language === "JS") {
  150. code += "\r\n" + "scene = " + createSceneFunction + "();";
  151. } else {
  152. var startCar = code.search("var " + createSceneFunction);
  153. code = code.substr(0, startCar) + code.substr(startCar + 4);
  154. code += "\n" + "scene = " + createSceneFunction + "();";
  155. }
  156. // Execute the code
  157. Utilities.FastEval(code);
  158. this._engine = globalObject.engine;
  159. if (!this._engine) {
  160. this.props.globalState.onErrorObservable.notifyObservers({
  161. message: "createEngine function must return an engine.",
  162. });
  163. return;
  164. }
  165. if (!globalObject.scene) {
  166. this.props.globalState.onErrorObservable.notifyObservers({
  167. message: createSceneFunction + " function must return a scene.",
  168. });
  169. return;
  170. }
  171. let sceneToRenderCode = "sceneToRender = scene";
  172. // if scene returns a promise avoid checks
  173. if (globalObject.scene.then) {
  174. checkCamera = false;
  175. checkSceneCount = false;
  176. sceneToRenderCode = "scene.then(returnedScene => { sceneToRender = returnedScene; });\r\n";
  177. }
  178. let createEngineZip = createEngineFunction === "createEngine" ? zipVariables : zipVariables + defaultEngineZip;
  179. this.props.globalState.zipCode = createEngineZip + ";\r\n" + code + ";\r\n" + sceneToRenderCode;
  180. }
  181. if (globalObject.scene.then) {
  182. globalObject.scene.then((s: Scene) => {
  183. this._scene = s;
  184. globalObject.scene = this._scene;
  185. });
  186. } else {
  187. this._scene = globalObject.scene as Scene;
  188. }
  189. this._engine.runRenderLoop(() => {
  190. if (!this._scene || !this._engine) {
  191. return;
  192. }
  193. if (this.props.globalState.runtimeMode === RuntimeMode.Editor && window.innerWidth > this.props.globalState.MobileSizeTrigger) {
  194. if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
  195. this._engine.resize();
  196. }
  197. }
  198. if (this._scene.activeCamera || this._scene.activeCameras && this._scene.activeCameras.length > 0) {
  199. this._scene.render();
  200. }
  201. // Update FPS if camera is not a webxr camera
  202. if (!(this._scene.activeCamera && this._scene.activeCamera.getClassName && this._scene.activeCamera.getClassName() === "WebXRCamera")) {
  203. if (this.props.globalState.runtimeMode !== RuntimeMode.Full) {
  204. this.props.globalState.fpsElement.innerHTML = this._engine.getFps().toFixed() + " fps";
  205. }
  206. }
  207. });
  208. if (checkSceneCount && this._engine.scenes.length === 0) {
  209. this.props.globalState.onErrorObservable.notifyObservers({
  210. message: "You must at least create a scene.",
  211. });
  212. return;
  213. }
  214. if (this._engine.scenes[0] && displayInspector) {
  215. this.props.globalState.onInspectorRequiredObservable.notifyObservers(true);
  216. }
  217. if (checkCamera && this._engine.scenes[0].activeCamera == null) {
  218. this.props.globalState.onErrorObservable.notifyObservers({
  219. message: "You must at least create a camera.",
  220. });
  221. return;
  222. } else if (globalObject.scene.then) {
  223. globalObject.scene.then(() => {
  224. if (this._engine!.scenes[0] && displayInspector) {
  225. this.props.globalState.onInspectorRequiredObservable.notifyObservers(true);
  226. }
  227. });
  228. } else {
  229. this._engine.scenes[0].executeWhenReady(function () {});
  230. }
  231. } catch (err) {
  232. this.props.globalState.onErrorObservable.notifyObservers(err);
  233. }
  234. }
  235. public render() {
  236. return <canvas id="renderCanvas" ref={this._canvasRef}></canvas>;
  237. }
  238. }