1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-06-07 16:14:21 +02:00
This commit is contained in:
syuilo
2026-04-24 15:42:58 +09:00
parent df5d5d23cc
commit b00880c21f
3 changed files with 56 additions and 47 deletions

View File

@@ -96,9 +96,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
<MkSelect <MkSelect
:modelValue="graphicsQuality" :items="[ :modelValue="graphicsQuality" :items="[
{ label: 'High', value: 'high' }, { label: 'High', value: GRAPHICS_QUALITY_HIGH },
{ label: 'Medium', value: 'medium' }, { label: 'Medium', value: GRAPHICS_QUALITY_MEDIUM },
{ label: 'Low', value: 'low' }, { label: 'Low', value: GRAPHICS_QUALITY_LOW },
]" @update:modelValue="v => graphicsQuality = v" ]" @update:modelValue="v => graphicsQuality = v"
> >
<template #label>Graphics quality</template> <template #label>Graphics quality</template>
@@ -123,6 +123,7 @@ import MkRange from '@/components/MkRange.vue';
import { RoomController } from '@/world/room/controller.js'; import { RoomController } from '@/world/room/controller.js';
import { cm, getHex, getRgb } from '@/world/utility.js'; import { cm, getHex, getRgb } from '@/world/utility.js';
import { deepClone } from '@/utility/clone.js'; import { deepClone } from '@/utility/clone.js';
import { GRAPHICS_QUALITY_HIGH, GRAPHICS_QUALITY_LOW, GRAPHICS_QUALITY_MEDIUM } from '@/world/room/engine.js';
const canvas = useTemplateRef('canvas'); const canvas = useTemplateRef('canvas');
@@ -140,7 +141,7 @@ function resize() {
const isZenMode = ref(false); const isZenMode = ref(false);
const isRoomSettingsOpen = ref(false); const isRoomSettingsOpen = ref(false);
const isChanged = ref(false); const isChanged = ref(false);
const graphicsQuality = ref<'low' | 'medium' | 'high'>('medium'); const graphicsQuality = ref<number>(0);
const data = localStorage.getItem('roomData') != null ? JSON.parse(localStorage.getItem('roomData')!) : { const data = localStorage.getItem('roomData') != null ? JSON.parse(localStorage.getItem('roomData')!) : {
heya: { heya: {
@@ -179,13 +180,13 @@ const data = localStorage.getItem('roomData') != null ? JSON.parse(localStorage.
let latestData = deepClone(data); let latestData = deepClone(data);
const controller = new RoomController(data); const controller = new RoomController(data, {
onMounted(async () => {
controller.init(canvas.value!, {
graphicsQuality: graphicsQuality.value, graphicsQuality: graphicsQuality.value,
}); });
onMounted(async () => {
controller.init(canvas.value!);
canvas.value!.focus(); canvas.value!.focus();
window.addEventListener('resize', resize); window.addEventListener('resize', resize);
@@ -311,7 +312,7 @@ async function revert() {
} }
async function reload() { async function reload() {
await controller.reset(null, null, { await controller.reset(null, {
graphicsQuality: graphicsQuality.value, graphicsQuality: graphicsQuality.value,
}); });
} }

View File

@@ -15,7 +15,7 @@ import * as sound from '@/utility/sound.js';
type Options = { type Options = {
workerMode?: boolean; workerMode?: boolean;
graphicsQuality?: 'low' | 'medium' | 'high'; graphicsQuality: number;
}; };
// 抽象化レイヤー // 抽象化レイヤー
@@ -38,8 +38,9 @@ export class RoomController {
public roomState: ShallowRef<RoomState>; public roomState: ShallowRef<RoomState>;
public initializeProgress = ref(0); public initializeProgress = ref(0);
constructor(roomState: RoomState) { constructor(roomState: RoomState, options: Options) {
this.roomState = shallowRef(roomState); this.roomState = shallowRef(roomState);
this.options = options;
this.onCanvasKeydown = this.onCanvasKeydown.bind(this); this.onCanvasKeydown = this.onCanvasKeydown.bind(this);
this.onCanvasKeyup = this.onCanvasKeyup.bind(this); this.onCanvasKeyup = this.onCanvasKeyup.bind(this);
@@ -50,23 +51,22 @@ export class RoomController {
this.onCanvasClick = this.onCanvasClick.bind(this); this.onCanvasClick = this.onCanvasClick.bind(this);
} }
public async init(canvas: HTMLCanvasElement, options: Options = {}) { public async init(canvas: HTMLCanvasElement) {
this.canvas = canvas; this.canvas = canvas;
this.canvas.width = canvas.clientWidth; this.canvas.width = canvas.clientWidth;
this.canvas.height = canvas.clientHeight; this.canvas.height = canvas.clientHeight;
this.options = options;
if (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, graphicsQuality: options.graphicsQuality }, [offscreen]); this.worker.postMessage({ type: 'init', canvas: offscreen, roomState: this.roomState.value, graphicsQuality: this.options.graphicsQuality }, [offscreen]);
this.isReady.value = true; this.isReady.value = true;
} else { } else {
const babylonEngine = new BABYLON.WebGPUEngine(canvas, { doNotHandleContextLost: true }); const babylonEngine = new BABYLON.WebGPUEngine(canvas, { doNotHandleContextLost: true });
babylonEngine.compatibilityMode = false; babylonEngine.compatibilityMode = false;
await babylonEngine.initAsync(); await babylonEngine.initAsync();
this.engine = new RoomEngine(this.roomState.value, { canvas, engine: babylonEngine, graphicsQuality: options.graphicsQuality }); this.engine = new RoomEngine(this.roomState.value, { canvas, engine: babylonEngine, graphicsQuality: this.options.graphicsQuality });
this.engine.on('loadingProgress', ({ progress }) => { this.engine.on('loadingProgress', ({ progress }) => {
this.initializeProgress.value = progress; this.initializeProgress.value = progress;
}); });
@@ -171,16 +171,17 @@ export class RoomController {
return false; return false;
} }
public async reset(roomState?: RoomState, canvas?: HTMLCanvasElement, options: Options = {}) { public async reset(roomState?: RoomState | null, options?: Options | null, canvas?: HTMLCanvasElement | null) {
this.destroy(); this.destroy();
if (roomState != null) this.roomState.value = roomState; if (roomState != null) this.roomState.value = roomState;
if (options != null) this.options = options;
this.isReady.value = false; this.isReady.value = false;
this.isSitting.value = false; this.isSitting.value = false;
this.isEditMode.value = false; this.isEditMode.value = false;
this.grabbing.value = null; this.grabbing.value = null;
this.selected.value = null; this.selected.value = null;
this.initializeProgress.value = 0; this.initializeProgress.value = 0;
await this.init(canvas ?? this.canvas!, options ?? this.options); await this.init(canvas ?? this.canvas!);
} }
public pauseRender() { public pauseRender() {

View File

@@ -93,6 +93,10 @@ function enableObjectCollision(meshes: BABYLON.Mesh[]) {
} }
} }
export const GRAPHICS_QUALITY_HIGH = 1;
export const GRAPHICS_QUALITY_MEDIUM = 0;
export const GRAPHICS_QUALITY_LOW = -1;
export type RoomEngineEvents = { export type RoomEngineEvents = {
'changeSelectedState': (ctx: { 'changeSelectedState': (ctx: {
selected: { selected: {
@@ -121,7 +125,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
private engine: BABYLON.WebGPUEngine; private engine: BABYLON.WebGPUEngine;
public scene: BABYLON.Scene; public scene: BABYLON.Scene;
private shadowGeneratorForRoomLight: BABYLON.ShadowGenerator; private shadowGeneratorForRoomLight: BABYLON.ShadowGenerator;
private shadowGeneratorForSunLight: BABYLON.ShadowGenerator; private shadowGeneratorForSunLight: BABYLON.ShadowGenerator | null = null;
public camera: BABYLON.UniversalCamera; public camera: BABYLON.UniversalCamera;
public objectEntities: Map<string, { public objectEntities: Map<string, {
rootMesh: BABYLON.Mesh; rootMesh: BABYLON.Mesh;
@@ -218,7 +222,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
constructor(roomState: RoomState, options: { constructor(roomState: RoomState, options: {
canvas: HTMLCanvasElement; canvas: HTMLCanvasElement;
engine: BABYLON.WebGPUEngine; engine: BABYLON.WebGPUEngine;
graphicsQuality?: 'low' | 'medium' | 'high'; graphicsQuality: number;
}) { }) {
super(); super();
@@ -231,13 +235,13 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
}; };
this.canvas = options.canvas; this.canvas = options.canvas;
this.fps = options.graphicsQuality === 'low' || options.graphicsQuality === 'medium' ? 30 : null; this.fps = options.graphicsQuality >= GRAPHICS_QUALITY_HIGH ? null : 30;
this.useGlow = options.graphicsQuality !== 'low'; this.useGlow = options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM;
registerBuiltInLoaders(); registerBuiltInLoaders();
this.engine = options.engine; this.engine = options.engine;
if (options.graphicsQuality === 'low') this.engine.setHardwareScalingLevel(2); if (options.graphicsQuality <= GRAPHICS_QUALITY_LOW) this.engine.setHardwareScalingLevel(2);
this.scene = new BABYLON.Scene(this.engine); this.scene = new BABYLON.Scene(this.engine);
// なんかレンダリングがおかしくなるときがあるのでコメントアウト // なんかレンダリングがおかしくなるときがあるのでコメントアウト
// オブジェクトを選択し、後ろを向いて別のオブジェクトを選択した後、最初のオブジェクトに振り返ると消えているなど // オブジェクトを選択し、後ろを向いて別のオブジェクトを選択した後、最初のオブジェクトに振り返ると消えているなど
@@ -314,11 +318,6 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
//this.scene.activeCamera = this.camera; //this.scene.activeCamera = this.camera;
const ambientLight = new BABYLON.HemisphericLight('ambientLight', new BABYLON.Vector3(0, 1, -0.5), this.scene);
ambientLight.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
ambientLight.intensity = 0.3;
//ambientLight.intensity = 0;
this.roomLight = new BABYLON.SpotLight('roomLight', new BABYLON.Vector3(0, cm(249), 0), new BABYLON.Vector3(0, -1, 0), 16, 8, this.scene); this.roomLight = new BABYLON.SpotLight('roomLight', new BABYLON.Vector3(0, cm(249), 0), new BABYLON.Vector3(0, -1, 0), 16, 8, this.scene);
this.roomLight.diffuse = new BABYLON.Color3(1.0, 0.9, 0.8); this.roomLight.diffuse = new BABYLON.Color3(1.0, 0.9, 0.8);
this.roomLight.shadowMinZ = cm(10); this.roomLight.shadowMinZ = cm(10);
@@ -327,12 +326,17 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
this.shadowGeneratorForRoomLight = new BABYLON.ShadowGenerator(2048, this.roomLight); this.shadowGeneratorForRoomLight = new BABYLON.ShadowGenerator(2048, this.roomLight);
this.shadowGeneratorForRoomLight.forceBackFacesOnly = true; this.shadowGeneratorForRoomLight.forceBackFacesOnly = true;
this.shadowGeneratorForRoomLight.bias = 0.0001; this.shadowGeneratorForRoomLight.bias = 0.00001;
this.shadowGeneratorForRoomLight.usePercentageCloserFiltering = true; this.shadowGeneratorForRoomLight.usePercentageCloserFiltering = true;
this.shadowGeneratorForRoomLight.filteringQuality = BABYLON.ShadowGenerator.QUALITY_HIGH; this.shadowGeneratorForRoomLight.filteringQuality = BABYLON.ShadowGenerator.QUALITY_HIGH;
if (options.graphicsQuality <= GRAPHICS_QUALITY_LOW) {
this.shadowGeneratorForRoomLight.getShadowMap().refreshRate = 120;
} else if (options.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM) {
this.shadowGeneratorForRoomLight.getShadowMap().refreshRate = 60; this.shadowGeneratorForRoomLight.getShadowMap().refreshRate = 60;
}
//this.shadowGenerator1.useContactHardeningShadow = true; //this.shadowGenerator1.useContactHardeningShadow = true;
if (options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM) {
const sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0.2, -1, -1), this.scene); const sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0.2, -1, -1), this.scene);
sunLight.position = new BABYLON.Vector3(cm(-20), cm(1000), cm(1000)); sunLight.position = new BABYLON.Vector3(cm(-20), cm(1000), cm(1000));
sunLight.diffuse = this.time === 0 ? new BABYLON.Color3(1.0, 0.9, 0.8) : this.time === 1 ? new BABYLON.Color3(1.0, 0.8, 0.6) : new BABYLON.Color3(0.6, 0.8, 1.0); sunLight.diffuse = this.time === 0 ? new BABYLON.Color3(1.0, 0.9, 0.8) : this.time === 1 ? new BABYLON.Color3(1.0, 0.8, 0.6) : new BABYLON.Color3(0.6, 0.8, 1.0);
@@ -342,13 +346,16 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
this.shadowGeneratorForSunLight = new BABYLON.ShadowGenerator(2048, sunLight); this.shadowGeneratorForSunLight = new BABYLON.ShadowGenerator(2048, sunLight);
this.shadowGeneratorForSunLight.forceBackFacesOnly = true; this.shadowGeneratorForSunLight.forceBackFacesOnly = true;
this.shadowGeneratorForSunLight.bias = 0.0001; this.shadowGeneratorForSunLight.bias = 0.00001;
this.shadowGeneratorForSunLight.usePercentageCloserFiltering = true; this.shadowGeneratorForSunLight.usePercentageCloserFiltering = true;
this.shadowGeneratorForSunLight.usePoissonSampling = true; this.shadowGeneratorForSunLight.usePoissonSampling = true;
if (options.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM) {
this.shadowGeneratorForSunLight.getShadowMap().refreshRate = 60; this.shadowGeneratorForSunLight.getShadowMap().refreshRate = 60;
}
}
this.lightContainer = new BABYLON.ClusteredLightContainer('clustered', [], this.scene); this.lightContainer = new BABYLON.ClusteredLightContainer('clustered', [], this.scene);
this.lightContainer.maxRange = options.graphicsQuality === 'high' ? cm(1000) : options.graphicsQuality === 'medium' ? cm(100) : cm(50); this.lightContainer.maxRange = options.graphicsQuality >= GRAPHICS_QUALITY_HIGH ? cm(1000) : options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM ? cm(100) : cm(50);
this.lightContainer.verticalTiles = 32; this.lightContainer.verticalTiles = 32;
this.lightContainer.horizontalTiles = 32; this.lightContainer.horizontalTiles = 32;
this.lightContainer.depthSlices = 32; this.lightContainer.depthSlices = 32;
@@ -829,7 +836,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
m.checkCollisions = false; m.checkCollisions = false;
m.receiveShadows = true; m.receiveShadows = true;
this.shadowGeneratorForRoomLight.addShadowCaster(m); this.shadowGeneratorForRoomLight.addShadowCaster(m);
this.shadowGeneratorForSunLight.addShadowCaster(m); this.shadowGeneratorForSunLight?.addShadowCaster(m);
//if (m.material) (m.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(1, 1, 1); //if (m.material) (m.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(1, 1, 1);
if (m.material) { if (m.material) {
(m.material as BABYLON.PBRMaterial).reflectionTexture = this.envMapIndoor; (m.material as BABYLON.PBRMaterial).reflectionTexture = this.envMapIndoor;
@@ -1081,7 +1088,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
if (def.receiveShadows !== false) mesh.receiveShadows = true; if (def.receiveShadows !== false) mesh.receiveShadows = true;
if (def.castShadows !== false) { if (def.castShadows !== false) {
this.shadowGeneratorForRoomLight.addShadowCaster(mesh); this.shadowGeneratorForRoomLight.addShadowCaster(mesh);
this.shadowGeneratorForSunLight.addShadowCaster(mesh); this.shadowGeneratorForSunLight?.addShadowCaster(mesh);
} }
//if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2); //if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2);
@@ -1369,7 +1376,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
private turnOnRoomLight(forInit = false) { private turnOnRoomLight(forInit = false) {
if (!forInit && SNAPSHOT_RENDERING) this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意 if (!forInit && SNAPSHOT_RENDERING) this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
this.roomLight.intensity = 10 * WORLD_SCALE * WORLD_SCALE; this.roomLight.intensity = 15 * WORLD_SCALE * WORLD_SCALE;
this.envMapIndoor.level = 0.6; this.envMapIndoor.level = 0.6;
if (!forInit && SNAPSHOT_RENDERING) { if (!forInit && SNAPSHOT_RENDERING) {
setTimeout(() => { setTimeout(() => {
@@ -1381,7 +1388,7 @@ export class RoomEngine extends EventEmitter<RoomEngineEvents> {
private turnOffRoomLight() { private turnOffRoomLight() {
if (SNAPSHOT_RENDERING) this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意 if (SNAPSHOT_RENDERING) this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
this.roomLight.intensity = 0; this.roomLight.intensity = 0;
this.envMapIndoor.level = 0; this.envMapIndoor.level = 0.025;
setTimeout(() => { setTimeout(() => {
if (SNAPSHOT_RENDERING) this.sr.enableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意 if (SNAPSHOT_RENDERING) this.sr.enableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
}, 10); }, 10);