mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-13 23:25:41 +02:00
wip
This commit is contained in:
@@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkSelect
|
||||
:items="[
|
||||
{ label: 'Simple', value: 'simple' },
|
||||
{ label: 'Museum', value: 'museum' },
|
||||
]" :modelValue="controller.roomState.value.env.type" @update:modelValue="v => controller.changeEnvType(v)"
|
||||
>
|
||||
<template #label>Env type</template>
|
||||
|
||||
@@ -27,7 +27,6 @@ import { deepClone } from '@/utility/clone.js';
|
||||
|
||||
const BAKE_TRANSFORM = false; // 実験的
|
||||
const IGNORE_OBJECTS: string[] = ['aquarium']; // for debug
|
||||
const RENDER_OUTDOOR_ENV = false;
|
||||
const IN_WEB_WORKER = typeof window === 'undefined';
|
||||
|
||||
export type RoomState = {
|
||||
@@ -237,6 +236,7 @@ export class RoomEngine extends EventEmitter {
|
||||
|
||||
this.fps = options.fps;
|
||||
this.useGlow = options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM;
|
||||
this.time = TIME_MAP[new Date().getHours() as keyof typeof TIME_MAP];
|
||||
|
||||
registerBuiltInLoaders();
|
||||
|
||||
@@ -250,32 +250,14 @@ export class RoomEngine extends EventEmitter {
|
||||
this.scene.skipPointerMovePicking = true;
|
||||
this.scene.skipFrustumClipping = true; // snapshot renderingでは全てのメッシュがアクティブになっている必要があるため
|
||||
this.scene.gravity = new BABYLON.Vector3(0, -0.1, 0).scale(WORLD_SCALE);
|
||||
this.scene.ambientColor = new BABYLON.Color3(1.0, 0.9, 0.8);
|
||||
this.scene.collisionsEnabled = true;
|
||||
|
||||
this.sr = new BABYLON.SnapshotRenderingHelper(this.scene);
|
||||
|
||||
const skybox = BABYLON.MeshBuilder.CreateBox('skybox', { size: cm(1000) }, this.scene);
|
||||
const skyboxMat = new BABYLON.StandardMaterial('skyboxMat', this.scene);
|
||||
skyboxMat.backFaceCulling = false;
|
||||
skyboxMat.disableLighting = true;
|
||||
skybox.material = skyboxMat;
|
||||
skybox.infiniteDistance = true;
|
||||
|
||||
this.time = TIME_MAP[new Date().getHours() as keyof typeof TIME_MAP];
|
||||
|
||||
if (this.time === 0) {
|
||||
skyboxMat.emissiveColor = new BABYLON.Color3(0.7, 0.9, 1.0);
|
||||
} else if (this.time === 1) {
|
||||
skyboxMat.emissiveColor = new BABYLON.Color3(0.8, 0.5, 0.3);
|
||||
} else {
|
||||
skyboxMat.emissiveColor = new BABYLON.Color3(0.05, 0.05, 0.2);
|
||||
}
|
||||
this.scene.ambientColor = new BABYLON.Color3(1.0, 0.9, 0.8);
|
||||
|
||||
this.scene.collisionsEnabled = true;
|
||||
|
||||
this.camera = new BABYLON.FreeCamera('camera', new BABYLON.Vector3(0, cm(130), cm(0)), this.scene);
|
||||
this.camera.minZ = cm(1);
|
||||
this.camera.maxZ = RENDER_OUTDOOR_ENV ? cm(10000) : cm(1000);
|
||||
this.camera.maxZ = cm(1000);
|
||||
this.camera.fov = 1;
|
||||
this.camera.ellipsoid = new BABYLON.Vector3(cm(15), cm(65), cm(15));
|
||||
this.camera.checkCollisions = true;
|
||||
@@ -580,7 +562,7 @@ export class RoomEngine extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public pauseRender() {
|
||||
public pauseRender() { // TODO: srと同じく参照カウント方式にした方が便利そう
|
||||
this.engine.stopRenderLoop();
|
||||
if (this.currentRafId != null) {
|
||||
// workerで実行される可能性がある
|
||||
@@ -828,9 +810,8 @@ export class RoomEngine extends EventEmitter {
|
||||
public async changeEnvType(type: RoomState['env']['type'], forInit = false) {
|
||||
this.roomState.env.type = type;
|
||||
|
||||
if (this.envManager != null) {
|
||||
this.envManager.dispose();
|
||||
}
|
||||
this.sr.disableSnapshotRendering();
|
||||
this.pauseRender();
|
||||
|
||||
const onMeshUpdatedCallback = (meshes: BABYLON.AbstractMesh[]) => {
|
||||
for (const m of meshes) {
|
||||
@@ -856,18 +837,41 @@ export class RoomEngine extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
let envManager: EnvManager;
|
||||
|
||||
if (this.roomState.env.type === 'simple') {
|
||||
const envManager = new SimpleEnvManager(onMeshUpdatedCallback);
|
||||
await envManager.load(this.roomState.env.options, this.scene);
|
||||
this.envManager = envManager;
|
||||
envManager = new SimpleEnvManager(onMeshUpdatedCallback);
|
||||
} else if (this.roomState.env.type === 'japanese') {
|
||||
// TODO
|
||||
} else if (this.roomState.env.type === 'museum') {
|
||||
const envManager = new MuseumEnvManager(onMeshUpdatedCallback);
|
||||
await envManager.load(this.roomState.env.options, this.scene);
|
||||
this.envManager = envManager;
|
||||
envManager = new MuseumEnvManager(onMeshUpdatedCallback);
|
||||
}
|
||||
|
||||
await envManager.load(this.roomState.env.options, this.scene);
|
||||
envManager.setTime(this.time);
|
||||
|
||||
for (const mat of this.scene.materials) {
|
||||
mat.unfreeze();
|
||||
if (mat instanceof BABYLON.MultiMaterial) {
|
||||
for (const subMat of mat.subMaterials) {
|
||||
if (subMat.metadata.useEnvMapAsObjectMaterial) subMat.reflectionTexture = envManager.envMapIndoor;
|
||||
}
|
||||
} else {
|
||||
if (mat.metadata?.useEnvMapAsObjectMaterial) mat.reflectionTexture = envManager.envMapIndoor;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.envManager != null) {
|
||||
this.envManager.dispose();
|
||||
}
|
||||
|
||||
this.envManager = envManager;
|
||||
|
||||
this.camera.maxZ = this.envManager.maxCameraZ;
|
||||
|
||||
this.resumeRender();
|
||||
this.sr.enableSnapshotRendering();
|
||||
|
||||
if (!forInit) {
|
||||
this.ev('changeRoomState', { roomState: this.roomState });
|
||||
}
|
||||
@@ -1118,6 +1122,7 @@ export class RoomEngine extends EventEmitter {
|
||||
(subMat as BABYLON.PBRMaterial).transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
||||
}
|
||||
(subMat as BABYLON.PBRMaterial).reflectionTexture = this.envManager?.envMapIndoor;
|
||||
(subMat as BABYLON.PBRMaterial).metadata.useEnvMapAsObjectMaterial = true;
|
||||
(subMat as BABYLON.PBRMaterial).useGLTFLightFalloff = true; // Clustered Lightingではphysical falloffを持つマテリアルはアーチファクトが発生する https://doc.babylonjs.com/features/featuresDeepDive/lights/clusteredLighting/#materials-with-a-physical-falloff-may-cause-artefacts
|
||||
(subMat as BABYLON.PBRMaterial).anisotropy.isEnabled = false; // なんかきれいにレンダリングされないため
|
||||
}
|
||||
@@ -1127,6 +1132,7 @@ export class RoomEngine extends EventEmitter {
|
||||
(mesh.material as BABYLON.PBRMaterial).transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
||||
}
|
||||
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.envManager?.envMapIndoor;
|
||||
(mesh.material as BABYLON.PBRMaterial).metadata.useEnvMapAsObjectMaterial = true;
|
||||
(mesh.material as BABYLON.PBRMaterial).useGLTFLightFalloff = true; // Clustered Lightingではphysical falloffを持つマテリアルはアーチファクトが発生する https://doc.babylonjs.com/features/featuresDeepDive/lights/clusteredLighting/#materials-with-a-physical-falloff-may-cause-artefacts
|
||||
(mesh.material as BABYLON.PBRMaterial).anisotropy.isEnabled = false; // なんかきれいにレンダリングされないため
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ import { findMaterial } from './utility.js';
|
||||
|
||||
export abstract class EnvManager<T = any> {
|
||||
protected onMeshUpdatedCallback: ((meshes: BABYLON.AbstractMesh[]) => void) | null = null;
|
||||
abstract envMapIndoor: BABYLON.CubeTexture | null;
|
||||
public abstract envMapIndoor: BABYLON.CubeTexture | null;
|
||||
public abstract maxCameraZ: number;
|
||||
|
||||
constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null) {
|
||||
this.onMeshUpdatedCallback = onMeshUpdatedCallback ?? null;
|
||||
@@ -25,6 +26,7 @@ export abstract class EnvManager<T = any> {
|
||||
|
||||
abstract load(options: T, scene: BABYLON.Scene): Promise<void>;
|
||||
abstract applyOptions(options: T): void;
|
||||
abstract setTime(time: number): void;
|
||||
abstract dispose(): void;
|
||||
}
|
||||
|
||||
@@ -70,13 +72,23 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
private pillarMaterials: Record<'nw' | 'ne' | 'sw' | 'se', BABYLON.PBRMaterial> | null = null;
|
||||
private ceilingMaterial: BABYLON.PBRMaterial | null = null;
|
||||
private floorMaterial: BABYLON.PBRMaterial | null = null;
|
||||
private skybox: BABYLON.Mesh | null = null;
|
||||
private skyboxMat: BABYLON.StandardMaterial | null = null;
|
||||
public envMapIndoor: BABYLON.CubeTexture | null = null;
|
||||
public maxCameraZ = cm(1000);
|
||||
|
||||
constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null) {
|
||||
super(onMeshUpdatedCallback);
|
||||
}
|
||||
|
||||
public async load(options: SimpleEnvOptions, scene: BABYLON.Scene) {
|
||||
this.skybox = BABYLON.MeshBuilder.CreateBox('skybox', { size: cm(1000) }, scene);
|
||||
this.skyboxMat = new BABYLON.StandardMaterial('skyboxMat', scene);
|
||||
this.skyboxMat.backFaceCulling = false;
|
||||
this.skyboxMat.disableLighting = true;
|
||||
this.skybox.material = this.skyboxMat;
|
||||
this.skybox.infiniteDistance = true;
|
||||
|
||||
this.loaderResult = await BABYLON.ImportMeshAsync('/client-assets/room/envs/default/300.glb', scene);
|
||||
|
||||
this.envMapIndoor = BABYLON.CubeTexture.CreateFromPrefilteredData('/client-assets/room/indoor.env', scene);
|
||||
@@ -173,6 +185,16 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
await this.applyOptions(options);
|
||||
}
|
||||
|
||||
public setTime(time: number) {
|
||||
if (time === 0) {
|
||||
this.skyboxMat.emissiveColor = new BABYLON.Color3(0.7, 0.9, 1.0);
|
||||
} else if (time === 1) {
|
||||
this.skyboxMat.emissiveColor = new BABYLON.Color3(0.8, 0.5, 0.3);
|
||||
} else {
|
||||
this.skyboxMat.emissiveColor = new BABYLON.Color3(0.05, 0.05, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
public applyOptions(options: SimpleEnvOptions) {
|
||||
// TODO: 返り値をpromiseにしてちゃんとテクスチャが読み終わってからresolveする
|
||||
|
||||
@@ -337,6 +359,9 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
for (const m of Object.values(this.pillarMaterials ?? {})) {
|
||||
m.dispose();
|
||||
}
|
||||
this.skybox?.dispose();
|
||||
this.skyboxMat?.dispose();
|
||||
this.envMapIndoor?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,6 +370,8 @@ export type MuseumEnvOptions = any;
|
||||
export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
||||
private loaderResult: BABYLON.ISceneLoaderAsyncResult | null = null;
|
||||
private meshes: BABYLON.Mesh[] = [];
|
||||
public envMapIndoor: BABYLON.CubeTexture | null = null;
|
||||
public maxCameraZ = cm(5000);
|
||||
|
||||
constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null) {
|
||||
super(onMeshUpdatedCallback);
|
||||
@@ -353,6 +380,9 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
||||
public async load(options: MuseumEnvOptions, scene: BABYLON.Scene) {
|
||||
this.loaderResult = await BABYLON.ImportMeshAsync('/client-assets/room/envs/museum/museum.glb', scene);
|
||||
|
||||
this.envMapIndoor = BABYLON.CubeTexture.CreateFromPrefilteredData('/client-assets/room/indoor.env', scene);
|
||||
this.envMapIndoor.boundingBoxSize = new BABYLON.Vector3(cm(500), cm(500), cm(500));
|
||||
|
||||
this.meshes = this.loaderResult.meshes.filter(m => m instanceof BABYLON.Mesh);
|
||||
this.meshes[0].scaling = this.meshes[0].scaling.scale(WORLD_SCALE);
|
||||
this.meshes[0].rotationQuaternion = null;
|
||||
@@ -381,6 +411,9 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
||||
await this.applyOptions(options);
|
||||
}
|
||||
|
||||
public setTime(time: number) {
|
||||
}
|
||||
|
||||
public applyOptions(options: MuseumEnvOptions) {
|
||||
this.onMeshUpdatedCallback?.(this.meshes);
|
||||
}
|
||||
@@ -397,5 +430,6 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
||||
for (const m of this.meshes) {
|
||||
m.dispose(false, true);
|
||||
}
|
||||
this.envMapIndoor?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user