diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index 007665e224..ad58aa7189 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -154,14 +154,26 @@ SPDX-License-Identifier: AGPL-3.0-only 保存 + + + reload @@ -187,6 +199,7 @@ import { deviceKind } from '@/utility/device-kind.js'; import MkProgressBar from '@/components/MkProgressBar.vue'; import { Joystick } from '@/world/joystick.js'; import { isTouchUsing } from '@/utility/touch.js'; +import { prefer } from '@/preferences.js'; const canvas = useTemplateRef('canvas'); @@ -204,7 +217,20 @@ function resize() { const isZenMode = ref(false); const isRoomSettingsOpen = ref(false); const isChanged = ref(false); -const graphicsQuality = ref(deviceKind === 'smartphone' ? GRAPHICS_QUALITY_LOW : GRAPHICS_QUALITY_MEDIUM); + +const graphicsQualityRaw = prefer.model('world.graphicsQuality'); +const graphicsQualityAutoValue = computed(() => deviceKind === 'smartphone' ? GRAPHICS_QUALITY_LOW : GRAPHICS_QUALITY_MEDIUM); +const graphicsQuality = computed(() => graphicsQualityRaw.value ?? graphicsQualityAutoValue.value); + +const fpsRaw = prefer.model('world.fps'); +const fpsAutoValue = computed(() => graphicsQuality.value >= GRAPHICS_QUALITY_HIGH ? null : 30); +const fps = computed(() => + fpsRaw.value == null ? fpsAutoValue.value : + fpsRaw.value === 'max' ? null : + fpsRaw.value === '120' ? 120 : + fpsRaw.value === '60' ? 60 : + 30); + const useVirtualJoystick = isTouchUsing && (deviceKind === 'smartphone' || deviceKind === 'tablet'); const joyStickRadiusPx = 100; @@ -254,6 +280,7 @@ let latestData = deepClone(data); const controller = new RoomController(data, { graphicsQuality: graphicsQuality.value, + fps: fps.value, useVirtualJoystick, }); @@ -413,6 +440,7 @@ async function revert() { async function reload() { await controller.reset(null, { graphicsQuality: graphicsQuality.value, + fps: fps.value, useVirtualJoystick, }); } diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index d6a4c63011..3d0c49898b 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -533,6 +533,13 @@ export const PREF_DEF = definePreferences({ }, }, + 'world.graphicsQuality': { + default: null as number | null, + }, + 'world.fps': { + default: null as 'max' | '120' | '60' | '30' | null, + }, + 'experimental.stackingRouterView': { default: false, }, diff --git a/packages/frontend/src/world/room/controller.ts b/packages/frontend/src/world/room/controller.ts index 312d706a7b..4dc289d055 100644 --- a/packages/frontend/src/world/room/controller.ts +++ b/packages/frontend/src/world/room/controller.ts @@ -16,6 +16,7 @@ import * as sound from '@/utility/sound.js'; type Options = { workerMode?: boolean; graphicsQuality: number; + fps: number | null; useVirtualJoystick?: boolean; }; @@ -24,7 +25,7 @@ export class RoomController { private worker: Worker | null = null; private engine: RoomEngine | null = null; private canvas: HTMLCanvasElement | null = null; - private options: Options = {}; + private options: Options; private isCanvasDragging = false; public isReady = ref(false); public isSitting = ref(false); @@ -60,7 +61,7 @@ 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, graphicsQuality: this.options.graphicsQuality, useVirtualJoystick: this.options.useVirtualJoystick }, [offscreen]); + this.worker.postMessage({ type: 'init', canvas: offscreen, roomState: this.roomState.value, ...this.options }, [offscreen]); this.isReady.value = true; } else { const babylonEngine = new BABYLON.WebGPUEngine(canvas, { doNotHandleContextLost: true }); @@ -68,7 +69,7 @@ export class RoomController { babylonEngine.enableOfflineSupport = false; await babylonEngine.initAsync(); - this.engine = new RoomEngine(this.roomState.value, { canvas, engine: babylonEngine, graphicsQuality: this.options.graphicsQuality, useVirtualJoystick: this.options.useVirtualJoystick }); + this.engine = new RoomEngine(this.roomState.value, { canvas, engine: babylonEngine, ...this.options }); this.engine.on('loadingProgress', ({ progress }) => { this.initializeProgress.value = progress; }); diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index e9f009dab9..8d5f9cf0c1 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -224,6 +224,7 @@ export class RoomEngine extends EventEmitter { canvas: HTMLCanvasElement; engine: BABYLON.WebGPUEngine; graphicsQuality: number; + fps: number | null; useVirtualJoystick?: boolean; }) { super(); @@ -237,7 +238,7 @@ export class RoomEngine extends EventEmitter { }; this.canvas = options.canvas; - this.fps = options.graphicsQuality >= GRAPHICS_QUALITY_HIGH ? null : 30; + this.fps = options.fps; this.useGlow = options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM; registerBuiltInLoaders();