1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-25 12:04:04 +02:00
This commit is contained in:
syuilo
2026-04-30 18:06:50 +09:00
parent b34e957c25
commit ce2e74f3ca
4 changed files with 71 additions and 61 deletions

View File

@@ -65,10 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</div> </div>
<div v-if="controller.isReady.value && controller.isEditMode.value && controller.selected.value != null && !controller.grabbing.value" :key="controller.selected.value.objectId" class="_panel" :class="$style.overlayObjectInfoPanel"> <div v-if="controller.isReady.value && controller.isEditMode.value && controller.selected.value != null && selectedObjectDef != null && !controller.grabbing.value" :key="controller.selected.value.objectId" class="_panel" :class="$style.overlayObjectInfoPanel">
{{ controller.selected.value.objectDef.name }} {{ selectedObjectDef.name }}
<XObjectCustomizeForm :schema="controller.selected.value.objectDef.options.schema" :options="controller.selected.value.objectState.options" @update="(k, v) => controller.updateObjectOption(controller.selected.value.objectId, k, v)"></XObjectCustomizeForm> <XObjectCustomizeForm :schema="selectedObjectDef.options.schema" :options="controller.selected.value.objectState.options" @update="(k, v) => controller.updateObjectOption(controller.selected.value.objectId, k, v)"></XObjectCustomizeForm>
</div> </div>
<div v-if="isRoomSettingsOpen && controller.isEditMode.value" class="_panel" :class="$style.overlayObjectInfoPanel"> <div v-if="isRoomSettingsOpen && controller.isEditMode.value" class="_panel" :class="$style.overlayObjectInfoPanel">
@@ -120,6 +120,7 @@ import MkProgressBar from '@/components/MkProgressBar.vue';
import { Joystick } from '@/world/joystick.js'; import { Joystick } from '@/world/joystick.js';
import { isTouchUsing } from '@/utility/touch.js'; import { isTouchUsing } from '@/utility/touch.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { getObjectDef } from '@/world/room/object-defs.js';
const canvas = useTemplateRef('canvas'); const canvas = useTemplateRef('canvas');
@@ -304,6 +305,8 @@ if (data.heya.options.pillars == null) {
}; };
} }
console.log('installedObjects:', data.installedObjects.length);
let latestData = deepClone(data); let latestData = deepClone(data);
const roomControllerOptions = computed<RoomControllerOptions>(() => ({ const roomControllerOptions = computed<RoomControllerOptions>(() => ({
@@ -317,6 +320,8 @@ const roomControllerOptions = computed<RoomControllerOptions>(() => ({
const controller = new RoomController(data, roomControllerOptions.value); const controller = new RoomController(data, roomControllerOptions.value);
const selectedObjectDef = computed(() => controller.selected.value == null ? null : getObjectDef(controller.selected.value.objectState.type));
onMounted(async () => { onMounted(async () => {
if (!await BABYLON.WebGPUEngine.IsSupportedAsync) { if (!await BABYLON.WebGPUEngine.IsSupportedAsync) {
os.alert({ os.alert({

View File

@@ -5,11 +5,12 @@
import { reactive, ref, shallowRef, triggerRef, watch } from 'vue'; import { reactive, ref, shallowRef, triggerRef, watch } from 'vue';
import * as BABYLON from '@babylonjs/core'; import * as BABYLON from '@babylonjs/core';
import { EventEmitter } from 'eventemitter3';
import { cm } from '../utility.js'; import { cm } from '../utility.js';
import RoomWorker from './worker?worker'; import RoomWorker from './worker?worker';
import { GRAPHICS_QUALITY_MEDIUM, RoomEngine } from './engine.js'; import { GRAPHICS_QUALITY_MEDIUM, RoomEngine } from './engine.js';
import type { ShallowRef } from 'vue'; 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 type { ObjectDef, RoomStateObject } from './object.js';
import * as sound from '@/utility/sound.js'; import * as sound from '@/utility/sound.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@@ -38,7 +39,6 @@ export class RoomController {
public selected = ref<{ public selected = ref<{
objectId: string; objectId: string;
objectState: RoomStateObject; objectState: RoomStateObject;
objectDef: ObjectDef;
} | null>(null); } | null>(null);
public roomState: ShallowRef<RoomState>; public roomState: ShallowRef<RoomState>;
public initializeProgress = ref(0); public initializeProgress = ref(0);
@@ -60,23 +60,22 @@ export class RoomController {
this.canvas.width = canvas.clientWidth; this.canvas.width = canvas.clientWidth;
this.canvas.height = canvas.clientHeight; this.canvas.height = canvas.clientHeight;
const engineEvents = new EventEmitter<RoomEngineEvents>();
if (this.options.workerMode) { if (this.options.workerMode) {
const offscreen = canvas.transferControlToOffscreen(); const offscreen = canvas.transferControlToOffscreen();
this.worker = new RoomWorker(); this.worker = new RoomWorker();
this.worker.postMessage({ type: 'init', canvas: offscreen, roomState: this.roomState.value, options: this.options }, [offscreen]); this.worker.postMessage({ type: 'init', canvas: offscreen, roomState: this.roomState.value, options: this.options }, [offscreen]);
this.worker.onmessage = (event) => { this.worker.onmessage = (event) => {
switch (event.data?.type) { switch (event.data?.type) {
case 'progress': {
this.initializeProgress.value = event.data.progress;
break;
}
case 'inited': { case 'inited': {
this.initializeProgress.value = 1; this.initializeProgress.value = 1;
this.isReady.value = true; this.isReady.value = true;
break; break;
} }
case 'changeEditMode': { case 'ev': {
this.isEditMode.value = event.data.isEditMode; const { type, ctx } = event.data.ev;
engineEvents.emit(type, ctx);
break; break;
} }
default: { default: {
@@ -104,8 +103,8 @@ export class RoomController {
...this.options, ...this.options,
}); });
this.engine.on('loadingProgress', ({ progress }) => { this.engine.on('ev', ({ type, ctx }) => {
this.initializeProgress.value = progress; engineEvents.emit(type, ctx);
}); });
await this.engine.init(); await this.engine.init();
@@ -113,31 +112,6 @@ export class RoomController {
this.initializeProgress.value = 1; this.initializeProgress.value = 1;
this.isReady.value = true; 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_) { if (_DEV_) {
(window as any).showBabylonInspector = () => { (window as any).showBabylonInspector = () => {
import('@babylonjs/inspector').then(({ ShowInspector }) => { 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('keydown', this.onCanvasKeydown);
this.canvas.addEventListener('keyup', this.onCanvasKeyup); this.canvas.addEventListener('keyup', this.onCanvasKeyup);
this.canvas.addEventListener('wheel', this.onCanvasWheel); this.canvas.addEventListener('wheel', this.onCanvasWheel);

View File

@@ -104,7 +104,6 @@ export type RoomEngineEvents = {
selected: { selected: {
objectId: string; objectId: string;
objectState: RoomStateObject<any>; objectState: RoomStateObject<any>;
objectDef: ObjectDef<any>;
} | null; } | null;
}) => void; }) => void;
'changeGrabbingState': (ctx: { grabbing: { forInstall: boolean } | null }) => void; 'changeGrabbingState': (ctx: { grabbing: { forInstall: boolean } | null }) => void;
@@ -121,7 +120,7 @@ export type RoomEngineEvents = {
'loadingProgress': (ctx: { progress: number }) => void; 'loadingProgress': (ctx: { progress: number }) => void;
}; };
export class RoomEngine extends EventEmitter<RoomEngineEvents> { export class RoomEngine extends EventEmitter {
private useGlow: boolean; private useGlow: boolean;
private engine: BABYLON.WebGPUEngine; private engine: BABYLON.WebGPUEngine;
public scene: BABYLON.Scene; public scene: BABYLON.Scene;
@@ -156,7 +155,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
} }
set grabbingCtx(v) { set grabbingCtx(v) {
this._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で検知できないので、そのような操作を実際に行うようになった & それを検知する必要性が出てきたら専用の設定関数などを新設してそれを使わせる // TODO: たぶんオブジェクト内の値のmutateはsetで検知できないので、そのような操作を実際に行うようになった & それを検知する必要性が出てきたら専用の設定関数などを新設してそれを使わせる
@@ -171,7 +170,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
} }
set selected(v) { set selected(v) {
this._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: 夜 private time: 0 | 1 | 2 = 0; // 0: 昼, 1: 夕, 2: 夜
@@ -184,7 +183,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
set gridSnapping(v) { set gridSnapping(v) {
this._gridSnapping = v; this._gridSnapping = v;
if (this.gridMaterial != null) this.gridMaterial.gridRatio = v.scale; // setter内でconstructor内設定の値に依存するのはタイミングによってはundefinedになりそうなので、実際に当該マテリアルを表示する必要が生じる直前に利用側で設定させた方がいいかもしれない 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; private putParticleSystem: BABYLON.ParticleSystem;
@@ -206,7 +205,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
} }
set isEditMode(v) { set isEditMode(v) {
this._isEditMode = v; this._isEditMode = v;
this.emit('changeEditMode', { isEditMode: v }); this.ev('changeEditMode', { isEditMode: v });
} }
public isSitting = false; public isSitting = false;
@@ -440,6 +439,11 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
} }
} }
private ev<K extends keyof RoomEngineEvents>(type: K, ctx: Parameters<RoomEngineEvents[K]>[0]) {
console.log(type, ctx);
this.emit('ev', { type, ctx });
}
public async init() { public async init() {
await this.loadHeya(); await this.loadHeya();
if (RENDER_OUTDOOR_ENV) await this.loadEnvModel(); if (RENDER_OUTDOOR_ENV) await this.loadEnvModel();
@@ -455,7 +459,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
options: o.options, options: o.options,
}).then(() => { }).then(() => {
loadedCount++; loadedCount++;
this.emit('loadingProgress', { progress: loadedCount / objects.length }); this.ev('loadingProgress', { progress: loadedCount / objects.length });
}))); })));
// 不具合のもと // 不具合のもと
@@ -829,7 +833,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
} }
} }
public async changeHeyaType(type: RoomState['heya']['type']) { public async changeHeyaType(type: RoomState['heya']['type'], forInit = false) {
this.roomState.heya.type = type; this.roomState.heya.type = type;
if (this.heyaManager != null) { if (this.heyaManager != null) {
@@ -867,11 +871,13 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
// TODO // TODO
} }
this.emit('changeRoomState', { roomState: this.roomState }); if (!forInit) {
this.ev('changeRoomState', { roomState: this.roomState });
}
} }
private async loadHeya() { private async loadHeya() {
await this.changeHeyaType(this.roomState.heya.type); await this.changeHeyaType(this.roomState.heya.type, true);
} }
private async loadObject(args: { private async loadObject(args: {
@@ -1343,7 +1349,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
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)!.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.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<RoomEngineEvents> {
public updateRoomLightColor(color: [number, number, number]) { public updateRoomLightColor(color: [number, number, number]) {
this.roomLight.diffuse = new BABYLON.Color3(...color); this.roomLight.diffuse = new BABYLON.Color3(...color);
this.roomState.roomLightColor = color; this.roomState.roomLightColor = color;
this.emit('changeRoomState', { roomState: this.roomState }); this.ev('changeRoomState', { roomState: this.roomState });
} }
private turnOnRoomLight(forInit = false) { private turnOnRoomLight(forInit = false) {
@@ -1590,7 +1596,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
options, options,
}); });
this.emit('changeRoomState', { roomState: this.roomState }); this.ev('changeRoomState', { roomState: this.roomState });
}, },
}; };
@@ -1700,7 +1706,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
for (const o of this.roomState.installedObjects.filter(o => o.sticky === objectId)) { for (const o of this.roomState.installedObjects.filter(o => o.sticky === objectId)) {
o.sticky = null; o.sticky = null;
} }
this.emit('changeRoomState', { roomState: this.roomState }); this.ev('changeRoomState', { roomState: this.roomState });
this.selected = null; this.selected = null;
this.playSfxUrl('/client-assets/room/sfx/remove.mp3', { this.playSfxUrl('/client-assets/room/sfx/remove.mp3', {
@@ -1725,7 +1731,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
if (options == null) return; if (options == null) return;
options[key] = value; options[key] = value;
this.emit('changeRoomState', { roomState: this.roomState }); this.ev('changeRoomState', { roomState: this.roomState });
const entity = this.objectEntities.get(objectId); const entity = this.objectEntities.get(objectId);
if (entity == null) return; if (entity == null) return;
@@ -1735,11 +1741,11 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
public updateHeyaOptions(options: RoomState['heya']['options']) { public updateHeyaOptions(options: RoomState['heya']['options']) {
this.roomState.heya.options = options; this.roomState.heya.options = options;
this.heyaManager.applyOptions(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 }) { private playSfxUrl(url: string, options: { volume: number; playbackRate: number }) {
this.emit('playSfxUrl', { url, options }); this.ev('playSfxUrl', { url, options });
} }
public resize() { public resize() {

View File

@@ -29,12 +29,8 @@ onmessage = async (event) => {
...event.data.options, ...event.data.options,
}); });
engine.on('loadingProgress', ({ progress }) => { engine.on('ev', ({ type, ctx }) => {
self.postMessage({ type: 'progress', progress }); self.postMessage({ type: 'ev', ev: { type, ctx } });
});
engine.on('changeEditMode', ({ isEditMode }) => {
self.postMessage({ type: 'changeEditMode', isEditMode });
}); });
await engine.init(); await engine.init();