mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-16 09:45:26 +02:00
wip
This commit is contained in:
BIN
packages/frontend/assets/room/envs/japanese/ceiling.png
Normal file
BIN
packages/frontend/assets/room/envs/japanese/ceiling.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
packages/frontend/assets/room/envs/japanese/japanese.blend
Normal file
BIN
packages/frontend/assets/room/envs/japanese/japanese.blend
Normal file
Binary file not shown.
BIN
packages/frontend/assets/room/envs/japanese/japanese.glb
Normal file
BIN
packages/frontend/assets/room/envs/japanese/japanese.glb
Normal file
Binary file not shown.
BIN
packages/frontend/assets/room/envs/japanese/tatami.png
Normal file
BIN
packages/frontend/assets/room/envs/japanese/tatami.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 879 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkSelect
|
||||
:items="[
|
||||
{ label: 'Simple', value: 'simple' },
|
||||
{ label: 'Japanese', value: 'japanese' },
|
||||
{ label: 'Museum', value: 'museum' },
|
||||
]" :modelValue="controller.roomState.value.env.type" @update:modelValue="v => controller.changeEnvType(v)"
|
||||
>
|
||||
|
||||
@@ -18,7 +18,7 @@ import { EventEmitter } from 'eventemitter3';
|
||||
import { TIME_MAP, scaleMorph, camelToKebab, cm, WORLD_SCALE, getMeshesBoundingBox, Timer, getYRotationDirection, FreeCameraManualInput, remap } from '../utility.js';
|
||||
import { getObjectDef } from './object-defs.js';
|
||||
import { findMaterial, GRAPHICS_QUALITY, ModelManager, SYSTEM_HEYA_MESH_NAMES, SYSTEM_MESH_NAMES } from './utility.js';
|
||||
import { MuseumEnvManager, SimpleEnvManager } from './env.js';
|
||||
import { JapaneseEnvManager, MuseumEnvManager, SimpleEnvManager } from './env.js';
|
||||
import type { GridMaterial } from '@babylonjs/materials';
|
||||
import type { EnvManager, JapaneseEnvOptions, SimpleEnvOptions } from './env.js';
|
||||
import type { ObjectDef, RoomObjectInstance, RoomStateObject } from './object.js';
|
||||
@@ -313,7 +313,7 @@ export class RoomEngine extends EventEmitter {
|
||||
this.gizmoManager = new BABYLON.GizmoManager(this.scene);
|
||||
this.gizmoManager.positionGizmoEnabled = false;
|
||||
|
||||
this.gridPlane = BABYLON.MeshBuilder.CreatePlane('gridPlane', { width: cm(1000), height: cm(1000) }, this.scene);
|
||||
this.gridPlane = BABYLON.MeshBuilder.CreatePlane('gridPlane', { width: cm(10000), height: cm(10000) }, this.scene);
|
||||
this.gridPlane.isPickable = false;
|
||||
this.gridPlane.isVisible = false;
|
||||
this.gridPlane.setEnabled(false);
|
||||
@@ -564,7 +564,7 @@ export class RoomEngine extends EventEmitter {
|
||||
if (this.roomState.env.type === 'simple') {
|
||||
envManager = new SimpleEnvManager(this, onMeshUpdatedCallback);
|
||||
} else if (this.roomState.env.type === 'japanese') {
|
||||
// TODO
|
||||
envManager = new JapaneseEnvManager(this, onMeshUpdatedCallback);
|
||||
} else if (this.roomState.env.type === 'museum') {
|
||||
envManager = new MuseumEnvManager(this, onMeshUpdatedCallback);
|
||||
}
|
||||
|
||||
@@ -80,10 +80,6 @@ export type SimpleEnvOptions = {
|
||||
};
|
||||
};
|
||||
|
||||
export type JapaneseEnvOptions = {
|
||||
window: 'none' | 'kosidakamado' | 'demado' | 'hakidasimado';
|
||||
};
|
||||
|
||||
// TODO: マテリアルは必要になるまで作成しないようにする
|
||||
|
||||
export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
@@ -440,16 +436,6 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
this.floorMaterial.freeze();
|
||||
}
|
||||
|
||||
for (const mesh of this.meshes) {
|
||||
if (mesh.material != null) {
|
||||
if ((mesh.material as BABYLON.PBRMaterial).metadata?.disableEnvMap) {
|
||||
(mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.5, 0.5, 0.5);
|
||||
} else {
|
||||
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.envMapIndoor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.onMeshUpdatedCallback?.(this.meshes);
|
||||
}
|
||||
|
||||
@@ -483,6 +469,187 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
}
|
||||
}
|
||||
|
||||
export type JapaneseEnvOptions = {
|
||||
window: 'none' | 'kosidakamado' | 'demado' | 'hakidasimado';
|
||||
};
|
||||
|
||||
export class JapaneseEnvManager extends EnvManager<JapaneseEnvOptions> {
|
||||
private loaderResult: BABYLON.ISceneLoaderAsyncResult | null = null;
|
||||
private meshes: BABYLON.Mesh[] = [];
|
||||
private skybox: BABYLON.Mesh | null = null;
|
||||
private skyboxMat: BABYLON.StandardMaterial | null = null;
|
||||
private roomLight: BABYLON.SpotLight | null = null;
|
||||
private sunLight: BABYLON.DirectionalLight | null = null;
|
||||
public envMapIndoor: BABYLON.CubeTexture | null = null;
|
||||
public maxCameraZ = cm(1000);
|
||||
|
||||
constructor(engine: RoomEngine, onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null) {
|
||||
super(engine, onMeshUpdatedCallback);
|
||||
}
|
||||
|
||||
public async load(options: JapaneseEnvOptions) {
|
||||
this.skybox = BABYLON.MeshBuilder.CreateBox('skybox', { size: cm(1000) }, this.engine.scene);
|
||||
this.skyboxMat = new BABYLON.StandardMaterial('skyboxMat', this.engine.scene);
|
||||
this.skyboxMat.backFaceCulling = false;
|
||||
this.skyboxMat.disableLighting = true;
|
||||
this.skybox.material = this.skyboxMat;
|
||||
this.skybox.infiniteDistance = true;
|
||||
|
||||
this.roomLight = new BABYLON.SpotLight('simpleEnv: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);
|
||||
|
||||
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;
|
||||
shadowGeneratorForRoomLight.bias = 0.0005;
|
||||
shadowGeneratorForRoomLight.usePercentageCloserFiltering = true;
|
||||
shadowGeneratorForRoomLight.filteringQuality = BABYLON.ShadowGenerator.QUALITY_HIGH;
|
||||
if (this.engine.graphicsQuality <= GRAPHICS_QUALITY.MEDIUM) {
|
||||
shadowGeneratorForRoomLight.getShadowMap().refreshRate = 60;
|
||||
}
|
||||
//shadowGeneratorForRoomLight.useContactHardeningShadow = true;
|
||||
//shadowGeneratorForRoomLight.contactHardeningLightSizeUVRatio = 0.01;
|
||||
this.shadowGenerators.push(shadowGeneratorForRoomLight);
|
||||
}
|
||||
|
||||
if (this.engine.graphicsQuality >= GRAPHICS_QUALITY.MEDIUM) {
|
||||
this.sunLight = new BABYLON.DirectionalLight('simpleEnv:SunLight', new BABYLON.Vector3(0.2, -1, -1), this.engine.scene);
|
||||
this.sunLight.position = new BABYLON.Vector3(cm(-20), cm(1000), cm(1000));
|
||||
this.sunLight.shadowMinZ = cm(1000);
|
||||
this.sunLight.shadowMaxZ = cm(2000);
|
||||
|
||||
const shadowGeneratorForSunLight = new BABYLON.ShadowGenerator(this.engine.graphicsQuality <= GRAPHICS_QUALITY.MEDIUM ? 1024 : 2048, this.sunLight);
|
||||
shadowGeneratorForSunLight.forceBackFacesOnly = true;
|
||||
shadowGeneratorForSunLight.bias = 0.00001;
|
||||
shadowGeneratorForSunLight.usePercentageCloserFiltering = true;
|
||||
shadowGeneratorForSunLight.usePoissonSampling = true;
|
||||
if (this.engine.graphicsQuality <= GRAPHICS_QUALITY.MEDIUM) {
|
||||
shadowGeneratorForSunLight.getShadowMap().refreshRate = 60;
|
||||
}
|
||||
this.shadowGenerators.push(shadowGeneratorForSunLight);
|
||||
}
|
||||
|
||||
this.loaderResult = await BABYLON.ImportMeshAsync('/client-assets/room/envs/japanese/japanese.glb', this.engine.scene);
|
||||
|
||||
this.envMapIndoor = BABYLON.CubeTexture.CreateFromPrefilteredData('/client-assets/room/indoor.env', this.engine.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;
|
||||
this.meshes[0].rotation = new BABYLON.Vector3(0, 0, 0);
|
||||
|
||||
// instanced mesh を通常の mesh に変換 (そうしないとマテリアルが共有される)
|
||||
for (const mesh of this.loaderResult.meshes) {
|
||||
if (mesh instanceof BABYLON.InstancedMesh) {
|
||||
const realizedMesh = mesh.sourceMesh.clone(mesh.name, null, true);
|
||||
|
||||
realizedMesh.position = mesh.position.clone();
|
||||
if (mesh.rotationQuaternion) {
|
||||
realizedMesh.rotationQuaternion = mesh.rotationQuaternion.clone();
|
||||
} else {
|
||||
realizedMesh.rotation = mesh.rotation.clone();
|
||||
}
|
||||
realizedMesh.scaling = mesh.scaling.clone();
|
||||
realizedMesh.parent = mesh.parent;
|
||||
|
||||
mesh.dispose();
|
||||
this.engine.scene.removeMesh(mesh);
|
||||
this.meshes.push(realizedMesh);
|
||||
}
|
||||
}
|
||||
|
||||
for (const mesh of this.meshes) {
|
||||
if (SYSTEM_HEYA_MESH_NAMES.some(name => mesh.name.includes(name))) continue;
|
||||
mesh.receiveShadows = true;
|
||||
|
||||
this.addShadowCaster(mesh);
|
||||
|
||||
const mat = mesh.material;
|
||||
if (mat instanceof BABYLON.MultiMaterial) {
|
||||
for (const subMat of mat.subMaterials) {
|
||||
subMat.reflectionTexture = this.envMapIndoor;
|
||||
}
|
||||
} else if (mat instanceof BABYLON.PBRMaterial) {
|
||||
mat.reflectionTexture = this.envMapIndoor;
|
||||
}
|
||||
}
|
||||
|
||||
await this.applyOptions(options);
|
||||
}
|
||||
|
||||
public setTime(time: number) {
|
||||
if (this.skyboxMat == null) return;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (this.sunLight != null) {
|
||||
this.sunLight.diffuse = time === 0 ? new BABYLON.Color3(1.0, 0.9, 0.8) : time === 1 ? new BABYLON.Color3(1.0, 0.8, 0.6) : new BABYLON.Color3(0.6, 0.8, 1.0);
|
||||
this.sunLight.intensity = time === 0 ? 3 : time === 1 ? 1 : 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
public updateRoomLightColor(color: BABYLON.Color3): 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public applyOptions(options: SimpleEnvOptions) {
|
||||
this.onMeshUpdatedCallback?.(this.meshes);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
for (const m of this.meshes) {
|
||||
m.dispose(false, true);
|
||||
}
|
||||
this.skybox?.dispose();
|
||||
this.skyboxMat?.dispose();
|
||||
this.envMapIndoor?.dispose();
|
||||
this.roomLight?.dispose();
|
||||
this.sunLight?.dispose();
|
||||
if (this.loaderResult != null) {
|
||||
for (const m of this.loaderResult.meshes) {
|
||||
m.dispose(false, true);
|
||||
}
|
||||
for (const t of this.loaderResult.transformNodes) {
|
||||
t.dispose(false, true);
|
||||
}
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export type MuseumEnvOptions = any;
|
||||
|
||||
export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
||||
@@ -550,10 +717,11 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
||||
}
|
||||
|
||||
for (const node of this.loaderResult.transformNodes.filter(node => node.name.includes('__LIGHT__'))) {
|
||||
const light = new BABYLON.SpotLight('museumEnv:SubRoomLight', node.position.scale(WORLD_SCALE), new BABYLON.Vector3(0, -1, 0), 16, 8, this.engine.scene, true);
|
||||
const light = new BABYLON.SpotLight('museumEnv: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];
|
||||
this.engine.lightContainer.addLight(light);
|
||||
this.subRoomLights.push(light);
|
||||
}
|
||||
@@ -591,7 +759,7 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
||||
if (this.roomLight == null) return;
|
||||
this.roomLight.intensity = 0.00005 * WORLD_SCALE * WORLD_SCALE;
|
||||
for (const subLight of this.subRoomLights) {
|
||||
subLight.intensity = 10 * WORLD_SCALE * WORLD_SCALE;
|
||||
subLight.intensity = 20 * WORLD_SCALE * WORLD_SCALE;
|
||||
}
|
||||
if (this.envMapIndoor != null) this.envMapIndoor.level = 0.2;
|
||||
for (const m of this.engine.scene.materials) {
|
||||
|
||||
Reference in New Issue
Block a user