diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index 7ffd26b2a9..51c5a57645 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -269,6 +269,7 @@ const roomControllerOptions = computed(() => ({ fps: fps.value, resolution: resolution.value, useVirtualJoystick, + workerMode: false, })); const controller = new RoomController(data, roomControllerOptions.value); diff --git a/packages/frontend/src/world/room/controller.ts b/packages/frontend/src/world/room/controller.ts index f0568444d7..40cdc9cdce 100644 --- a/packages/frontend/src/world/room/controller.ts +++ b/packages/frontend/src/world/room/controller.ts @@ -62,8 +62,23 @@ export class RoomController { if (this.options.workerMode) { const offscreen = canvas.transferControlToOffscreen(); this.worker = new RoomWorker(); - this.worker.postMessage({ type: 'init', canvas: offscreen, roomState: this.roomState.value, ...this.options }, [offscreen]); - this.isReady.value = true; + this.worker.postMessage({ type: 'init', canvas: offscreen, roomState: this.roomState.value, options: this.options }, [offscreen]); + this.worker.onmessage = (event) => { + switch (event.data?.type) { + case 'progress': { + this.initializeProgress.value = event.data.progress; + break; + } + case 'inited': { + this.initializeProgress.value = 1; + this.isReady.value = true; + break; + } + default: { + console.warn('Unrecognized message from worker:', event.data?.type); + } + } + }; } else { const babylonEngine = new BABYLON.WebGPUEngine(canvas, { doNotHandleContextLost: true }); babylonEngine.compatibilityMode = false; @@ -104,6 +119,14 @@ export class RoomController { this.engine.on('playSfxUrl', ({ url, options }) => { sound.playUrl(url, options); }); + + if (_DEV_) { + (window as any).showBabylonInspector = () => { + import('@babylonjs/inspector').then(({ ShowInspector }) => { + ShowInspector(this.engine.scene); + }); + }; + } } this.canvas.addEventListener('keydown', this.onCanvasKeydown); diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index 30b99bf94b..8aecf1c508 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -10,9 +10,7 @@ // TODO: テクスチャ置き換え時、元のテクスチャをちゃんとdispose import * as BABYLON from '@babylonjs/core'; -import { AxesViewer } from '@babylonjs/core/Debug/axesViewer'; import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic'; -import { BoundingBoxRenderer } from '@babylonjs/core/Rendering/boundingBoxRenderer'; import { GridMaterial } from '@babylonjs/materials'; import { EventEmitter } from 'eventemitter3'; import { TIME_MAP, scaleMorph, camelToKebab, cm, WORLD_SCALE, getMeshesBoundingBox, Timer, getYRotationDirection, FreeCameraTouchVirtualJoystickInput } from '../utility.js'; @@ -437,18 +435,13 @@ export class RoomEngine extends EventEmitter { if (_DEV_) { // snapshot renderingかつglow layerが有効だとなんかクラッシュする if (!(SNAPSHOT_RENDERING && this.useGlow)) { - const axes = new AxesViewer(this.scene, 30); - axes.xAxis.position = new BABYLON.Vector3(0, 30, 0); - axes.yAxis.position = new BABYLON.Vector3(0, 30, 0); - axes.zAxis.position = new BABYLON.Vector3(0, 30, 0); - } - - if (!IN_WEB_WORKER) { - (window as any).showBabylonInspector = () => { - import('@babylonjs/inspector').then(({ ShowInspector }) => { - ShowInspector(this.scene); - }); - }; + import('@babylonjs/core/Debug/axesViewer').then(m => { + const { AxesViewer } = m; + const axes = new AxesViewer(this.scene, 30); + axes.xAxis.position = new BABYLON.Vector3(0, 30, 0); + axes.yAxis.position = new BABYLON.Vector3(0, 30, 0); + axes.zAxis.position = new BABYLON.Vector3(0, 30, 0); + }); } } } @@ -588,7 +581,8 @@ export class RoomEngine extends EventEmitter { const renderLoop = (timeStamp: number) => { if (this.disposed) return; - this.currentRafId = window.requestAnimationFrame(renderLoop); + // workerで実行される可能性がある + this.currentRafId = requestAnimationFrame(renderLoop); const delta = timeStamp - then; if (delta <= interval) return; @@ -599,14 +593,16 @@ export class RoomEngine extends EventEmitter { this.engine.endFrame(); }; - this.currentRafId = window.requestAnimationFrame(renderLoop); + // workerで実行される可能性がある + this.currentRafId = requestAnimationFrame(renderLoop); } } public pauseRender() { this.engine.stopRenderLoop(); if (this.currentRafId != null) { - window.cancelAnimationFrame(this.currentRafId); + // workerで実行される可能性がある + cancelAnimationFrame(this.currentRafId); this.currentRafId = null; } } @@ -1638,7 +1634,9 @@ export class RoomEngine extends EventEmitter { this.scene.customRenderTargets.push(reflectionProbe.cubeTexture); reflectionProbe.cubeTexture.render(); - await new Promise(res => window.setTimeout(res, 2000)); + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals + await new Promise(res => setTimeout(res, 2000)); const tex = reflectionProbe.cubeTexture; reflectionProbe.renderList = []; @@ -1731,6 +1729,8 @@ export class RoomEngine extends EventEmitter { // ↑追記: engine.resizeした後に一瞬待つことで回避できることが判明 if (SNAPSHOT_RENDERING) this.sr.disableSnapshotRendering(); this.engine.resize(); + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals setTimeout(() => { if (SNAPSHOT_RENDERING) this.sr.enableSnapshotRendering(); }, 1); @@ -1738,7 +1738,8 @@ export class RoomEngine extends EventEmitter { public destroy() { if (this.currentRafId != null) { - window.cancelAnimationFrame(this.currentRafId); + // workerで実行される可能性がある + cancelAnimationFrame(this.currentRafId); this.currentRafId = null; } this.timer.dispose(); diff --git a/packages/frontend/src/world/room/utility.ts b/packages/frontend/src/world/room/utility.ts index 04944be992..ce4d3be3fc 100644 --- a/packages/frontend/src/world/room/utility.ts +++ b/packages/frontend/src/world/room/utility.ts @@ -5,7 +5,6 @@ import * as BABYLON from '@babylonjs/core'; import { applyMorphTargetsToMesh, cm, getPlaneUvIndexes, Timer } from '../utility.js'; -import type { RoomEngine } from './engine.js'; export const SYSTEM_MESH_NAMES = ['__TOP__', '__SIDE__', '__PICK__', '__COLLISION__']; export const SYSTEM_HEYA_MESH_NAMES = ['__ROOM_WALL__', '__ROOM_SIDE__', '__ROOM_FLOOR__', '__ROOM_CEILING__', '__ROOM_TOP__', '__ROOM_BOTTOM__']; diff --git a/packages/frontend/src/world/room/worker.ts b/packages/frontend/src/world/room/worker.ts index df650e4b0d..73f8e9d308 100644 --- a/packages/frontend/src/world/room/worker.ts +++ b/packages/frontend/src/world/room/worker.ts @@ -21,8 +21,15 @@ onmessage = async (event) => { babylonEngine.compatibilityMode = false; babylonEngine.enableOfflineSupport = false; await babylonEngine.initAsync(); - engine = new RoomEngine(roomState, { canvas, engine: babylonEngine }); + if (event.data.options.resolution === 2) babylonEngine.setHardwareScalingLevel(0.5); + if (event.data.options.resolution === 0.5) babylonEngine.setHardwareScalingLevel(2); + + engine = new RoomEngine(roomState, { canvas, engine: babylonEngine, ...event.data.options }); + engine.on('loadingProgress', ({ progress }) => { + self.postMessage({ type: 'progress', progress }); + }); await engine.init(); + self.postMessage({ type: 'inited' }); break; } case 'resize': { diff --git a/packages/frontend/src/world/utility.ts b/packages/frontend/src/world/utility.ts index b3ce1657aa..cd8369ca50 100644 --- a/packages/frontend/src/world/utility.ts +++ b/packages/frontend/src/world/utility.ts @@ -544,6 +544,8 @@ export class RecyvlingTextGrid { } export function sleep(ms: number) { + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals return new Promise(resolve => setTimeout(resolve, ms)); } @@ -552,7 +554,9 @@ export class Timer { private intervalIds: number[] = []; public setTimeout(callback: () => void, ms: number) { - const id = window.setTimeout(() => { + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals + const id = setTimeout(() => { this.timeoutIds = this.timeoutIds.filter(i => i !== id); callback(); }, ms); @@ -560,18 +564,24 @@ export class Timer { } public setInterval(callback: () => void, ms: number) { - const id = window.setInterval(callback, ms); + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals + const id = setInterval(callback, ms); this.intervalIds.push(id); } public dispose() { for (const id of this.timeoutIds) { - window.clearTimeout(id); + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals + clearTimeout(id); } this.timeoutIds = []; for (const id of this.intervalIds) { - window.clearInterval(id); + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals + clearInterval(id); } this.intervalIds = []; }