From ce2e74f3ca61e382296389e77e38f84240bd27bf Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 30 Apr 2026 18:06:50 +0900 Subject: [PATCH] wip --- packages/frontend/src/pages/room.vue | 11 ++- .../frontend/src/world/room/controller.ts | 73 ++++++++++--------- packages/frontend/src/world/room/engine.ts | 40 +++++----- packages/frontend/src/world/room/worker.ts | 8 +- 4 files changed, 71 insertions(+), 61 deletions(-) diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index a7b3520afc..b7f243193b 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -65,10 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only -
- {{ controller.selected.value.objectDef.name }} +
+ {{ selectedObjectDef.name }} - +
@@ -120,6 +120,7 @@ import MkProgressBar from '@/components/MkProgressBar.vue'; import { Joystick } from '@/world/joystick.js'; import { isTouchUsing } from '@/utility/touch.js'; import { prefer } from '@/preferences.js'; +import { getObjectDef } from '@/world/room/object-defs.js'; const canvas = useTemplateRef('canvas'); @@ -304,6 +305,8 @@ if (data.heya.options.pillars == null) { }; } +console.log('installedObjects:', data.installedObjects.length); + let latestData = deepClone(data); const roomControllerOptions = computed(() => ({ @@ -317,6 +320,8 @@ const roomControllerOptions = computed(() => ({ const controller = new RoomController(data, roomControllerOptions.value); +const selectedObjectDef = computed(() => controller.selected.value == null ? null : getObjectDef(controller.selected.value.objectState.type)); + onMounted(async () => { if (!await BABYLON.WebGPUEngine.IsSupportedAsync) { os.alert({ diff --git a/packages/frontend/src/world/room/controller.ts b/packages/frontend/src/world/room/controller.ts index 6a1b6f4f5e..f14c56f662 100644 --- a/packages/frontend/src/world/room/controller.ts +++ b/packages/frontend/src/world/room/controller.ts @@ -5,11 +5,12 @@ import { reactive, ref, shallowRef, triggerRef, watch } from 'vue'; import * as BABYLON from '@babylonjs/core'; +import { EventEmitter } from 'eventemitter3'; import { cm } from '../utility.js'; import RoomWorker from './worker?worker'; import { GRAPHICS_QUALITY_MEDIUM, RoomEngine } from './engine.js'; import type { ShallowRef } from 'vue'; -import type { RoomState } from './engine.js'; +import type { RoomEngineEvents, RoomState } from './engine.js'; import type { ObjectDef, RoomStateObject } from './object.js'; import * as sound from '@/utility/sound.js'; import * as os from '@/os.js'; @@ -38,7 +39,6 @@ export class RoomController { public selected = ref<{ objectId: string; objectState: RoomStateObject; - objectDef: ObjectDef; } | null>(null); public roomState: ShallowRef; public initializeProgress = ref(0); @@ -60,23 +60,22 @@ export class RoomController { this.canvas.width = canvas.clientWidth; this.canvas.height = canvas.clientHeight; + const engineEvents = new EventEmitter(); + if (this.options.workerMode) { const offscreen = canvas.transferControlToOffscreen(); this.worker = new RoomWorker(); 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; } - case 'changeEditMode': { - this.isEditMode.value = event.data.isEditMode; + case 'ev': { + const { type, ctx } = event.data.ev; + engineEvents.emit(type, ctx); break; } default: { @@ -104,8 +103,8 @@ export class RoomController { ...this.options, }); - this.engine.on('loadingProgress', ({ progress }) => { - this.initializeProgress.value = progress; + this.engine.on('ev', ({ type, ctx }) => { + engineEvents.emit(type, ctx); }); await this.engine.init(); @@ -113,31 +112,6 @@ export class RoomController { this.initializeProgress.value = 1; this.isReady.value = true; - this.engine.on('changeGrabbingState', ({ grabbing }) => { - this.grabbing.value = grabbing; - }); - - this.engine.on('changeEditMode', ({ isEditMode }) => { - this.isEditMode.value = isEditMode; - }); - - this.engine.on('changeGridSnapping', ({ gridSnapping }) => { - this.gridSnapping.value = gridSnapping; - }); - - this.engine.on('changeSelectedState', ({ selected }) => { - this.selected.value = selected; - }); - - this.engine.on('changeRoomState', ({ roomState }) => { - this.roomState.value = roomState; - triggerRef(this.selected); - }); - - this.engine.on('playSfxUrl', ({ url, options }) => { - sound.playUrl(url, options); - }); - if (_DEV_) { (window as any).showBabylonInspector = () => { import('@babylonjs/inspector').then(({ ShowInspector }) => { @@ -147,6 +121,35 @@ export class RoomController { } } + engineEvents.on('loadingProgress', ({ progress }) => { + this.initializeProgress.value = progress; + }); + + engineEvents.on('changeGrabbingState', ({ grabbing }) => { + this.grabbing.value = grabbing; + }); + + engineEvents.on('changeEditMode', ({ isEditMode }) => { + this.isEditMode.value = isEditMode; + }); + + engineEvents.on('changeGridSnapping', ({ gridSnapping }) => { + this.gridSnapping.value = gridSnapping; + }); + + engineEvents.on('changeSelectedState', ({ selected }) => { + this.selected.value = selected; + }); + + engineEvents.on('changeRoomState', ({ roomState }) => { + this.roomState.value = roomState; + triggerRef(this.selected); + }); + + engineEvents.on('playSfxUrl', ({ url, options }) => { + sound.playUrl(url, options); + }); + this.canvas.addEventListener('keydown', this.onCanvasKeydown); this.canvas.addEventListener('keyup', this.onCanvasKeyup); this.canvas.addEventListener('wheel', this.onCanvasWheel); diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index 5e9b60b032..cb54528f1e 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -104,7 +104,6 @@ export type RoomEngineEvents = { selected: { objectId: string; objectState: RoomStateObject; - objectDef: ObjectDef; } | null; }) => void; 'changeGrabbingState': (ctx: { grabbing: { forInstall: boolean } | null }) => void; @@ -121,7 +120,7 @@ export type RoomEngineEvents = { 'loadingProgress': (ctx: { progress: number }) => void; }; -export class RoomEngine extends EventEmitter { +export class RoomEngine extends EventEmitter { private useGlow: boolean; private engine: BABYLON.WebGPUEngine; public scene: BABYLON.Scene; @@ -156,7 +155,7 @@ export class RoomEngine extends EventEmitter { } set grabbingCtx(v) { this._grabbingCtx = v; - this.emit('changeGrabbingState', { grabbing: v == null ? null : { forInstall: v.forInstall } }); + this.ev('changeGrabbingState', { grabbing: v == null ? null : { forInstall: v.forInstall } }); } // TODO: たぶんオブジェクト内の値のmutateはsetで検知できないので、そのような操作を実際に行うようになった & それを検知する必要性が出てきたら専用の設定関数などを新設してそれを使わせる @@ -171,7 +170,7 @@ export class RoomEngine extends EventEmitter { } set selected(v) { this._selected = v; - this.emit('changeSelectedState', { selected: v == null ? null : { objectId: v.objectId, objectState: v.objectState, objectDef: v.objectDef } }); + this.ev('changeSelectedState', { selected: v == null ? null : { objectId: v.objectId, objectState: v.objectState } }); } private time: 0 | 1 | 2 = 0; // 0: 昼, 1: 夕, 2: 夜 @@ -184,7 +183,7 @@ export class RoomEngine extends EventEmitter { set gridSnapping(v) { this._gridSnapping = v; if (this.gridMaterial != null) this.gridMaterial.gridRatio = v.scale; // setter内でconstructor内設定の値に依存するのはタイミングによってはundefinedになりそうなので、実際に当該マテリアルを表示する必要が生じる直前に利用側で設定させた方がいいかもしれない - this.emit('changeGridSnapping', { gridSnapping: v }); + this.ev('changeGridSnapping', { gridSnapping: v }); } private putParticleSystem: BABYLON.ParticleSystem; @@ -206,7 +205,7 @@ export class RoomEngine extends EventEmitter { } set isEditMode(v) { this._isEditMode = v; - this.emit('changeEditMode', { isEditMode: v }); + this.ev('changeEditMode', { isEditMode: v }); } public isSitting = false; @@ -440,6 +439,11 @@ export class RoomEngine extends EventEmitter { } } + private ev(type: K, ctx: Parameters[0]) { + console.log(type, ctx); + this.emit('ev', { type, ctx }); + } + public async init() { await this.loadHeya(); if (RENDER_OUTDOOR_ENV) await this.loadEnvModel(); @@ -455,7 +459,7 @@ export class RoomEngine extends EventEmitter { options: o.options, }).then(() => { loadedCount++; - this.emit('loadingProgress', { progress: loadedCount / objects.length }); + this.ev('loadingProgress', { progress: loadedCount / objects.length }); }))); // 不具合のもと @@ -829,7 +833,7 @@ export class RoomEngine extends EventEmitter { } } - public async changeHeyaType(type: RoomState['heya']['type']) { + public async changeHeyaType(type: RoomState['heya']['type'], forInit = false) { this.roomState.heya.type = type; if (this.heyaManager != null) { @@ -867,11 +871,13 @@ export class RoomEngine extends EventEmitter { // TODO } - this.emit('changeRoomState', { roomState: this.roomState }); + if (!forInit) { + this.ev('changeRoomState', { roomState: this.roomState }); + } } private async loadHeya() { - await this.changeHeyaType(this.roomState.heya.type); + await this.changeHeyaType(this.roomState.heya.type, true); } private async loadObject(args: { @@ -1343,7 +1349,7 @@ export class RoomEngine extends EventEmitter { this.roomState.installedObjects.find(o => o.id === selectedObject.metadata.objectId)!.position = [pos.x, pos.y, pos.z]; this.roomState.installedObjects.find(o => o.id === selectedObject.metadata.objectId)!.rotation = [rotation.x, rotation.y, rotation.z]; - this.emit('changeRoomState', { roomState: this.roomState }); + this.ev('changeRoomState', { roomState: this.roomState }); }); }, }; @@ -1410,7 +1416,7 @@ export class RoomEngine extends EventEmitter { public updateRoomLightColor(color: [number, number, number]) { this.roomLight.diffuse = new BABYLON.Color3(...color); this.roomState.roomLightColor = color; - this.emit('changeRoomState', { roomState: this.roomState }); + this.ev('changeRoomState', { roomState: this.roomState }); } private turnOnRoomLight(forInit = false) { @@ -1590,7 +1596,7 @@ export class RoomEngine extends EventEmitter { options, }); - this.emit('changeRoomState', { roomState: this.roomState }); + this.ev('changeRoomState', { roomState: this.roomState }); }, }; @@ -1700,7 +1706,7 @@ export class RoomEngine extends EventEmitter { for (const o of this.roomState.installedObjects.filter(o => o.sticky === objectId)) { o.sticky = null; } - this.emit('changeRoomState', { roomState: this.roomState }); + this.ev('changeRoomState', { roomState: this.roomState }); this.selected = null; this.playSfxUrl('/client-assets/room/sfx/remove.mp3', { @@ -1725,7 +1731,7 @@ export class RoomEngine extends EventEmitter { if (options == null) return; options[key] = value; - this.emit('changeRoomState', { roomState: this.roomState }); + this.ev('changeRoomState', { roomState: this.roomState }); const entity = this.objectEntities.get(objectId); if (entity == null) return; @@ -1735,11 +1741,11 @@ export class RoomEngine extends EventEmitter { public updateHeyaOptions(options: RoomState['heya']['options']) { this.roomState.heya.options = options; this.heyaManager.applyOptions(options); - this.emit('changeRoomState', { roomState: this.roomState }); + this.ev('changeRoomState', { roomState: this.roomState }); } private playSfxUrl(url: string, options: { volume: number; playbackRate: number }) { - this.emit('playSfxUrl', { url, options }); + this.ev('playSfxUrl', { url, options }); } public resize() { diff --git a/packages/frontend/src/world/room/worker.ts b/packages/frontend/src/world/room/worker.ts index 48b1a12efe..6cca5c9dd2 100644 --- a/packages/frontend/src/world/room/worker.ts +++ b/packages/frontend/src/world/room/worker.ts @@ -29,12 +29,8 @@ onmessage = async (event) => { ...event.data.options, }); - engine.on('loadingProgress', ({ progress }) => { - self.postMessage({ type: 'progress', progress }); - }); - - engine.on('changeEditMode', ({ isEditMode }) => { - self.postMessage({ type: 'changeEditMode', isEditMode }); + engine.on('ev', ({ type, ctx }) => { + self.postMessage({ type: 'ev', ev: { type, ctx } }); }); await engine.init();