room light

This commit is contained in:
syuilo 2026-06-09 18:50:46 +09:00
commit 39c6baf6f2
11 changed files with 70 additions and 108 deletions

View file

@ -1179,12 +1179,12 @@ export class RoomEngine extends EngineBase<{
this.fixedCamera.parent = null;
}
public updateRoomLightColor(color: [number, number, number]) {
this.sr.disableSnapshotRendering();
this.envManager.updateRoomLightColor(new BABYLON.Color3(...color));
this.sr.enableSnapshotRendering();
this.roomState.roomLightColor = color;
public updateLightSettings(light: RoomState['light']) {
this.roomState.light = light;
this.ev('changeRoomState', { roomState: this.roomState });
this.sr.disableSnapshotRendering();
this.envManager.applyRoomLight();
this.sr.enableSnapshotRendering();
}
public turnOnRoomLight(forInit = false) {

View file

@ -16,6 +16,7 @@ export abstract class EnvManager<T = any> {
public abstract envMapIndoor: BABYLON.CubeTexture | null;
public abstract maxCameraZ: number;
protected shadowGenerators: BABYLON.ShadowGenerator[] = [];
protected isRoomLightOn = true;
constructor(engine: RoomEngine) {
this.engine = engine;
@ -24,9 +25,17 @@ export abstract class EnvManager<T = any> {
abstract load(options: T, scene: BABYLON.Scene, engine: RoomEngine): Promise<void>;
abstract applyOptions(options: T): void;
abstract setTime(time: number): void;
abstract updateRoomLightColor(color: BABYLON.Color3): void;
abstract turnOnRoomLight(): void;
abstract turnOffRoomLight(): void;
abstract applyRoomLight(): void;
public turnOnRoomLight() {
this.isRoomLightOn = true;
this.applyRoomLight();
}
public turnOffRoomLight() {
this.isRoomLightOn = false;
this.applyRoomLight();
}
public addShadowCaster(mesh: BABYLON.AbstractMesh) {
for (const shadowGen of this.shadowGenerators) {

View file

@ -50,11 +50,12 @@ export class CustomMadoriEnvManager extends EnvManager<CustomMadoriEnvOptions> {
this.roomLight = new BABYLON.DirectionalLight('env:RoomLight', new BABYLON.Vector3(0, -1, 0), this.engine.scene);
this.roomLight.position = new BABYLON.Vector3(0, cm(300), 0);
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.roomLightColor);
this.roomLight.shadowMinZ = cm(10);
this.roomLight.shadowMaxZ = cm(500);
this.roomLight.radius = cm(30);
this.applyRoomLight();
if (this.engine.graphicsQuality >= GRAPHICS_QUALITY.MEDIUM) {
const shadowGeneratorForRoomLight = new BABYLON.ShadowGenerator(this.engine.graphicsQuality <= GRAPHICS_QUALITY.MEDIUM ? 1024 : 2048, this.roomLight);
shadowGeneratorForRoomLight.forceBackFacesOnly = true;
@ -295,29 +296,14 @@ export class CustomMadoriEnvManager extends EnvManager<CustomMadoriEnvOptions> {
}
}
public updateRoomLightColor(color: BABYLON.Color3): void {
public applyRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.diffuse = color;
}
public turnOnRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 0.0005 * WORLD_SCALE * WORLD_SCALE;
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.2;
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.light.color);
this.roomLight.intensity = 0.0005 * WORLD_SCALE * WORLD_SCALE * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0);
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025 + (0.575 * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0));
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.5, 0.5, 0.5);
}
}
}
public turnOffRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 0;
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025;
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.025, 0.025, 0.025);
m.ambientColor = this.isRoomLightOn ? new BABYLON.Color3(0.5, 0.5, 0.5) : new BABYLON.Color3(0.025, 0.025, 0.025);
}
}
}

View file

@ -9,7 +9,7 @@ import { findMaterial, GRAPHICS_QUALITY } from '../../utility.js';
import { SYSTEM_HEYA_MESH_NAMES } from '../utility.js';
import { EnvManager } from '../env.js';
import type { RoomEngine } from '../engine.js';
import type { SimpleEnvOptions, JapaneseEnvOptions } from 'misskey-world/src/room/env.js';
import type { JapaneseEnvOptions } from 'misskey-world/src/room/env.js';
export class JapaneseEnvManager extends EnvManager<JapaneseEnvOptions> {
private loaderResult: BABYLON.ISceneLoaderAsyncResult | null = null;
@ -34,11 +34,12 @@ export class JapaneseEnvManager extends EnvManager<JapaneseEnvOptions> {
this.skybox.infiniteDistance = true;
this.roomLight = new BABYLON.SpotLight('env:RoomLight', new BABYLON.Vector3(0, cm(249), 0), new BABYLON.Vector3(0, -1, 0), 16, 8, this.engine.scene);
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.roomLightColor);
this.roomLight.shadowMinZ = cm(10);
this.roomLight.shadowMaxZ = cm(300);
this.roomLight.radius = cm(30);
this.applyRoomLight();
if (this.engine.graphicsQuality >= GRAPHICS_QUALITY.MEDIUM) {
const shadowGeneratorForRoomLight = new BABYLON.ShadowGenerator(this.engine.graphicsQuality <= GRAPHICS_QUALITY.MEDIUM ? 1024 : 2048, this.roomLight);
shadowGeneratorForRoomLight.forceBackFacesOnly = true;
@ -127,34 +128,19 @@ export class JapaneseEnvManager extends EnvManager<JapaneseEnvOptions> {
}
}
public updateRoomLightColor(color: BABYLON.Color3): void {
public applyRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.diffuse = color;
}
public turnOnRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 18 * WORLD_SCALE * WORLD_SCALE;
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.6;
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.light.color);
this.roomLight.intensity = 18 * WORLD_SCALE * WORLD_SCALE * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0);
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025 + (0.575 * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0));
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.5, 0.5, 0.5);
m.ambientColor = this.isRoomLightOn ? new BABYLON.Color3(0.5, 0.5, 0.5) : new BABYLON.Color3(0.025, 0.025, 0.025);
}
}
}
public turnOffRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 0;
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025;
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.025, 0.025, 0.025);
}
}
}
public applyOptions(options: SimpleEnvOptions) {
public applyOptions(options: JapaneseEnvOptions) {
this.registerMeshes(this.meshes);
}

View file

@ -56,7 +56,6 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
this.roomLight = new BABYLON.DirectionalLight('env:RoomLight', new BABYLON.Vector3(0, -1, 0), this.engine.scene);
this.roomLight.position = new BABYLON.Vector3(0, cm(300), 0);
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.roomLightColor);
this.roomLight.shadowMinZ = cm(10);
this.roomLight.shadowMaxZ = cm(500);
this.roomLight.radius = cm(30);
@ -77,7 +76,6 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
for (const node of this.meshes.filter(mesh => mesh.name.includes('__LIGHT__'))) {
const light = new BABYLON.SpotLight('env:SubRoomLight', node.position, new BABYLON.Vector3(0, -1, 0), 16, 8, this.engine.scene, true);
light.diffuse = new BABYLON.Color3(...this.engine.roomState.roomLightColor);
light.range = cm(500);
light.radius = cm(15);
light.parent = this.meshes[0];
@ -85,6 +83,8 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
this.subRoomLights.push(light);
}
this.applyRoomLight();
for (const mesh of this.meshes) {
if (SYSTEM_HEYA_MESH_NAMES.some(name => mesh.name.includes(name))) continue;
mesh.receiveShadows = true;
@ -97,38 +97,18 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
public setTime(time: number) {
}
public updateRoomLightColor(color: BABYLON.Color3): void {
public applyRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.diffuse = color;
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.light.color);
this.roomLight.intensity = 0.00005 * WORLD_SCALE * WORLD_SCALE * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0);
for (const subLight of this.subRoomLights) {
subLight.diffuse = color;
subLight.diffuse = new BABYLON.Color3(...this.engine.roomState.light.color);
subLight.intensity = 20 * WORLD_SCALE * WORLD_SCALE * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0);
}
}
public turnOnRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 0.00005 * WORLD_SCALE * WORLD_SCALE;
for (const subLight of this.subRoomLights) {
subLight.intensity = 20 * WORLD_SCALE * WORLD_SCALE;
}
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.2;
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025 + (0.175 * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0));
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.5, 0.5, 0.5);
}
}
}
public turnOffRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 0;
for (const subLight of this.subRoomLights) {
subLight.intensity = 0;
}
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025;
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.025, 0.025, 0.025);
m.ambientColor = this.isRoomLightOn ? new BABYLON.Color3(0.5, 0.5, 0.5) : new BABYLON.Color3(0.025, 0.025, 0.025);
}
}
}

View file

@ -145,11 +145,12 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
this.skybox.infiniteDistance = true;
this.roomLight = new BABYLON.SpotLight('env:RoomLight', new BABYLON.Vector3(0, cm(249), 0), new BABYLON.Vector3(0, -1, 0), 16, 8, this.engine.scene);
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.roomLightColor);
this.roomLight.shadowMinZ = cm(10);
this.roomLight.shadowMaxZ = cm(300);
this.roomLight.radius = cm(30);
this.applyRoomLight();
if (this.engine.graphicsQuality >= GRAPHICS_QUALITY.MEDIUM) {
const shadowGeneratorForRoomLight = new BABYLON.ShadowGenerator(this.engine.graphicsQuality <= GRAPHICS_QUALITY.MEDIUM ? 1024 : 2048, this.roomLight);
shadowGeneratorForRoomLight.forceBackFacesOnly = true;
@ -435,29 +436,14 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
}
}
public updateRoomLightColor(color: BABYLON.Color3): void {
public applyRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.diffuse = color;
}
public turnOnRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 18 * WORLD_SCALE * WORLD_SCALE;
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.6;
this.roomLight.diffuse = new BABYLON.Color3(...this.engine.roomState.light.color);
this.roomLight.intensity = 18 * WORLD_SCALE * WORLD_SCALE * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0);
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025 + (0.575 * this.engine.roomState.light.brightness * (this.isRoomLightOn ? 1 : 0));
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.5, 0.5, 0.5);
}
}
}
public turnOffRoomLight(): void {
if (this.roomLight == null) return;
this.roomLight.intensity = 0;
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.025;
for (const m of this.engine.scene.materials) {
if (m.metadata?.disableEnvMap) {
m.ambientColor = new BABYLON.Color3(0.025, 0.025, 0.025);
m.ambientColor = this.isRoomLightOn ? new BABYLON.Color3(0.5, 0.5, 0.5) : new BABYLON.Color3(0.025, 0.025, 0.025);
}
}
}

View file

@ -54,7 +54,10 @@ const initialRoomDef = {
type: 'simple',
options: getDefaultSimpleEnvOptions(),
},
roomLightColor: [1.0, 0.9, 0.8],
light: {
color: [1.0, 0.9, 0.8],
brightness: 1,
},
installedFurnitures: [],
worldScale: WORLD_SCALE,
} satisfies RoomState;

View file

@ -252,6 +252,13 @@ if (initialRoomState.installedObjects != null) {
initialRoomState.installedFurnitures = initialRoomState.installedObjects;
delete initialRoomState.installedObjects;
}
if (initialRoomState.roomLightColor != null) {
initialRoomState.light = {
color: initialRoomState.roomLightColor,
brightness: 1,
};
delete initialRoomState.roomLightColor;
}
for (const obj of initialRoomState.installedFurnitures) {
if (obj.options.customPicture != null) {
obj.options.image = {

View file

@ -18,10 +18,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSelect>
<!-- debounce or throttleしないとカラーピッカー上で高速でなぞったときになぜか無限ループになるワーカーとの間でラグがあるため少し前の値がまたmodelValueとしてフィードバックされてしまうためだと思われる -->
<MkInput :modelValue="getHex(controller.roomState.value.roomLightColor)" type="color" :throttle="300" @update:modelValue="v => { const c = getRgb(v); if (c != null) controller.updateRoomLightColor(c); }">
<MkInput :modelValue="getHex(controller.roomState.value.light.color)" type="color" :throttle="300" @update:modelValue="v => { const c = getRgb(v); if (c != null) controller.updateLightSettings({ ...controller.roomState.value.light, color: c }); }">
<template #label>light color</template>
</MkInput>
<MkRange :modelValue="controller.roomState.value.light.brightness" :min="0" :max="1" :step="0.1" continuousUpdate @update:modelValue="v => controller.updateLightSettings({ ...controller.roomState.value.light, brightness: v })">
<template #label>light brightness</template>
</MkRange>
<template v-if="controller.roomState.value.env.type === 'simple'">
<XDefaultEnvOptions :options="controller.roomState.value.env.options" @update="v => updateEnvOptions(v)"></XDefaultEnvOptions>
</template>
@ -36,8 +40,6 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, defineAsyncComponent, nextTick, onMounted, onUnmounted, ref, shallowRef, useTemplateRef, watch } from 'vue';
import { getHex, getRgb } from 'misskey-world/src/utility.js';
import { throttle } from 'throttle-debounce';
import XWallOption from './room.simple-env-wall-options.vue';
import XPillarOption from './room.simple-env-pillar-options.vue';
import XDefaultEnvOptions from './room.simple-env-options.vue';
import XCustomMadoriEnvOptions from './room.custom-madori-env-options.vue';
import type { RoomController } from '@/world/room/controller.js';

View file

@ -173,8 +173,8 @@ export class RoomController extends EngineControllerBase<RoomEngine, {
this.call('updateEnvOptions', [options]);
}
public updateRoomLightColor(color: [number, number, number]) {
this.call('updateRoomLightColor', [color]);
public updateLightSettings(light: RoomState['light']) {
this.call('updateLightSettings', [light]);
}
public beginSelectedInstalledFunitureGrabbing() {

View file

@ -21,7 +21,10 @@ export type RoomState = {
type: 'customMadori';
options: CustomMadoriEnvOptions;
};
roomLightColor: [number, number, number];
light: {
color: [number, number, number];
brightness: number;
};
installedFurnitures: RoomState_InstalledFurniture[];
worldScale: number;
};