diff --git a/packages/frontend-misskey-world-engine/src/room/envs/customMadori.ts b/packages/frontend-misskey-world-engine/src/room/envs/customMadori.ts index 57c87c7031..36a967f786 100644 --- a/packages/frontend-misskey-world-engine/src/room/envs/customMadori.ts +++ b/packages/frontend-misskey-world-engine/src/room/envs/customMadori.ts @@ -194,10 +194,10 @@ export class CustomMadoriEnvManager extends EnvManager { const unitDef = options.units[posToIndex(x, z)]; if (unitDef == null) return; - const unitNDef = options.units[posToIndex(x, z + 1)]; - const unitSDef = options.units[posToIndex(x, z - 1)]; - const unitWDef = options.units[posToIndex(x + 1, z)]; - const unitEDef = options.units[posToIndex(x - 1, z)]; + const unitZPositiveDef = options.units[posToIndex(x, z + 1)]; + const unitZNegativeDef = options.units[posToIndex(x, z - 1)]; + const unitXPositiveDef = options.units[posToIndex(x + 1, z)]; + const unitXNegativeDef = options.units[posToIndex(x - 1, z)]; const shiftedX = x - (options.dimension[0] / 2) + 0.5; @@ -215,24 +215,24 @@ export class CustomMadoriEnvManager extends EnvManager { ceilingMesh.material = unitDef.ceiling?.material != null && this.ceilingMaterials[unitDef.ceiling.material] != null ? this.ceilingMaterials[unitDef.ceiling.material] : defaultCeilingMaterial; const defaultWallMaterial = this.wallMaterials[options.wallMaterials[0].id]; - const createWall = (dir: 'n' | 's' | 'w' | 'e') => { + const createWall = (dir: 'zPositive' | 'zNegative' | 'xPositive' | 'xNegative') => { const wallDef = unitDef.walls?.[dir] ?? {}; const wallRootNode = this.wallRootNode.clone(`unit_${x}_${z}_wall_${dir}`, unitRoot)!; wallRootNode.scaling = new BABYLON.Vector3(-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE); switch (dir) { - case 'n': + case 'zPositive': wallRootNode.rotation = new BABYLON.Vector3(0, Math.PI, 0); wallRootNode.position = new BABYLON.Vector3(0, 0, cm(50)); break; - case 's': + case 'zNegative': wallRootNode.position = new BABYLON.Vector3(0, 0, cm(-50)); break; - case 'w': + case 'xPositive': wallRootNode.rotation = new BABYLON.Vector3(0, -Math.PI / 2, 0); wallRootNode.position = new BABYLON.Vector3(cm(50), 0, 0); break; - case 'e': + case 'xNegative': wallRootNode.rotation = new BABYLON.Vector3(0, Math.PI / 2, 0); wallRootNode.position = new BABYLON.Vector3(cm(-50), 0, 0); break; @@ -264,10 +264,10 @@ export class CustomMadoriEnvManager extends EnvManager { } }; - if (unitNDef == null) createWall('n'); - if (unitSDef == null) createWall('s'); - if (unitWDef == null) createWall('w'); - if (unitEDef == null) createWall('e'); + if (unitZPositiveDef == null) createWall('zPositive'); + if (unitZNegativeDef == null) createWall('zNegative'); + if (unitXPositiveDef == null) createWall('xPositive'); + if (unitXNegativeDef == null) createWall('xNegative'); for (const mesh of unitRoot.getChildMeshes()) { this.meshes.push(mesh); diff --git a/packages/frontend-misskey-world-engine/src/room/envs/simple.ts b/packages/frontend-misskey-world-engine/src/room/envs/simple.ts index e0a7c31cc1..42914fed85 100644 --- a/packages/frontend-misskey-world-engine/src/room/envs/simple.ts +++ b/packages/frontend-misskey-world-engine/src/room/envs/simple.ts @@ -5,7 +5,7 @@ import * as BABYLON from '@babylonjs/core/pure.js'; import { cm, WORLD_SCALE } from 'misskey-world/src/utility.js'; -import { findMaterial, GRAPHICS_QUALITY } from '../../utility.js'; +import { findMaterial, GRAPHICS_QUALITY, treeClone } from '../../utility.js'; import { SYSTEM_HEYA_MESH_NAMES } from '../utility.js'; import { EnvManager } from '../env.js'; import type { RoomEngine } from '../engine.js'; @@ -15,14 +15,15 @@ import type { SimpleEnvOptions } from 'misskey-world/src/room/env.js'; export class SimpleEnvManager extends EnvManager { private loaderResult: BABYLON.ISceneLoaderAsyncResult | null = null; - private meshes: BABYLON.Mesh[] = []; - private wallRoots: Record<'n' | 's' | 'w' | 'e', BABYLON.TransformNode> = null as any; - private wallMaterials: Record<'n' | 's' | 'w' | 'e', BABYLON.PBRMaterial> | null = null; - private wallBeamMaterials: Record<'n' | 's' | 'w' | 'e', BABYLON.PBRMaterial> | null = null; - private pillarRoots: Record<'nw' | 'ne' | 'sw' | 'se', BABYLON.TransformNode> | null = null; - private pillarMaterials: Record<'nw' | 'ne' | 'sw' | 'se', BABYLON.PBRMaterial> | null = null; - private ceilingMaterial: BABYLON.PBRMaterial | null = null; - private floorMaterial: BABYLON.PBRMaterial | null = null; + private rootNode: BABYLON.TransformNode; + private wallRoots: Record<'zPositive' | 'zNegative' | 'xPositive' | 'xNegative', BABYLON.TransformNode>; + private wallScalingContainers: Record<'zPositive' | 'zNegative' | 'xPositive' | 'xNegative', BABYLON.TransformNode>; + private wallMaterials: Record<'zPositive' | 'zNegative' | 'xPositive' | 'xNegative', BABYLON.PBRMaterial>; + private wallBeamMaterials: Record<'zPositive' | 'zNegative' | 'xPositive' | 'xNegative', BABYLON.PBRMaterial>; + private pillarRoots: Record<'zp_xp' | 'zp_xn' | 'zn_xp' | 'zn_xn', BABYLON.TransformNode>; + private pillarMaterials: Record<'zp_xp' | 'zp_xn' | 'zn_xp' | 'zn_xn', BABYLON.PBRMaterial>; + private ceilingMaterial: BABYLON.PBRMaterial; + private floorMaterial: BABYLON.PBRMaterial; private skybox: BABYLON.Mesh | null = null; private skyboxMat: BABYLON.StandardMaterial | null = null; private roomLight: BABYLON.SpotLight | null = null; @@ -32,9 +33,89 @@ export class SimpleEnvManager extends EnvManager { constructor(engine: RoomEngine) { super(engine); - } - public async load(options: SimpleEnvOptions) { + this.rootNode = new BABYLON.TransformNode('simpleEnvRoot', this.engine.scene); + + this.wallRoots = { + zPositive: new BABYLON.TransformNode('wallRootZPositive', this.engine.scene), + zNegative: new BABYLON.TransformNode('wallRootZNegative', this.engine.scene), + xPositive: new BABYLON.TransformNode('wallRootXPositive', this.engine.scene), + xNegative: new BABYLON.TransformNode('wallRootXNegative', this.engine.scene), + }; + this.wallRoots.zPositive.parent = this.rootNode; + this.wallRoots.zPositive.position.z = cm(150); + this.wallRoots.zPositive.rotation.y = Math.PI; + this.wallRoots.zNegative.parent = this.rootNode; + this.wallRoots.zNegative.position.z = -cm(150); + this.wallRoots.xPositive.parent = this.rootNode; + this.wallRoots.xPositive.position.x = cm(150); + this.wallRoots.xPositive.rotation.y = -Math.PI / 2; + this.wallRoots.xNegative.parent = this.rootNode; + this.wallRoots.xNegative.position.x = -cm(150); + this.wallRoots.xNegative.rotation.y = Math.PI / 2; + + this.wallScalingContainers = { + zPositive: new BABYLON.TransformNode('wallScalingContainerZPositive', this.engine.scene), + zNegative: new BABYLON.TransformNode('wallScalingContainerZNegative', this.engine.scene), + xPositive: new BABYLON.TransformNode('wallScalingContainerXPositive', this.engine.scene), + xNegative: new BABYLON.TransformNode('wallScalingContainerXNegative', this.engine.scene), + }; + for (const [k, v] of Object.entries(this.wallScalingContainers)) { + v.parent = this.wallRoots[k as keyof typeof this.wallRoots]; + v.scaling = new BABYLON.Vector3(-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE); + } + + this.pillarRoots = { + zp_xp: new BABYLON.TransformNode('pillarRootZpXp', this.engine.scene), + zp_xn: new BABYLON.TransformNode('pillarRootZpXn', this.engine.scene), + zn_xp: new BABYLON.TransformNode('pillarRootZnXp', this.engine.scene), + zn_xn: new BABYLON.TransformNode('pillarRootZnXn', this.engine.scene), + }; + for (const v of Object.values(this.pillarRoots)) { + v.parent = this.rootNode; + } + + const wallMaterial = new BABYLON.PBRMaterial('wallMaterial', this.engine.scene); + wallMaterial.albedoColor = new BABYLON.Color3(0.8, 0.8, 0.8); + wallMaterial.roughness = 1; + wallMaterial.metallic = 0; + this.wallMaterials = { + zPositive: wallMaterial.clone('wallZPositiveMaterial'), + zNegative: wallMaterial.clone('wallZNegativeMaterial'), + xPositive: wallMaterial.clone('wallXPositiveMaterial'), + xNegative: wallMaterial.clone('wallXNegativeMaterial'), + }; + + const beamMaterial = wallMaterial.clone('beamMaterial'); + this.wallBeamMaterials = { + zPositive: beamMaterial.clone('beamZPositiveMaterial'), + zNegative: beamMaterial.clone('beamZNegativeMaterial'), + xPositive: beamMaterial.clone('beamXPositiveMaterial'), + xNegative: beamMaterial.clone('beamXNegativeMaterial'), + }; + + const pillarMaterial = wallMaterial.clone('pillarMaterial'); + this.pillarMaterials = { + zp_xp: pillarMaterial.clone('pillarMaterialZpXp'), + zp_xn: pillarMaterial.clone('pillarMaterialZpXn'), + zn_xp: pillarMaterial.clone('pillarMaterialZnXp'), + zn_xn: pillarMaterial.clone('pillarMaterialZnXn'), + }; + + this.ceilingMaterial = new BABYLON.PBRMaterial('ceilingMaterial', this.engine.scene); + this.ceilingMaterial.albedoColor = new BABYLON.Color3(0.8, 0.8, 0.8); + this.ceilingMaterial.roughness = 1; + this.ceilingMaterial.metallic = 0; + this.floorMaterial = new BABYLON.PBRMaterial('floorMaterial', this.engine.scene); + this.floorMaterial.albedoColor = new BABYLON.Color3(0.8, 0.8, 0.8); + this.floorMaterial.roughness = 1; + this.floorMaterial.metallic = 0; + + const baseboardMaterial = new BABYLON.PBRMaterial('baseboardMaterial', this.engine.scene); + baseboardMaterial.albedoColor = new BABYLON.Color3(0.8, 0.8, 0.8); + baseboardMaterial.roughness = 1; + baseboardMaterial.metallic = 0; + 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; @@ -79,111 +160,219 @@ export class SimpleEnvManager extends EnvManager { this.shadowGenerators.push(shadowGeneratorForSunLight); } - this.loaderResult = await BABYLON.ImportMeshAsync('/client-assets/room/envs/default/300.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); + public async load(options: SimpleEnvOptions) { + this.loaderResult = await BABYLON.LoadAssetContainerAsync('/client-assets/room/envs/simple/300.glb', this.engine.scene); - // instanced mesh を通常の mesh に変換 (そうしないとマテリアルが共有される) - for (const mesh of this.loaderResult.meshes) { - if (mesh instanceof BABYLON.InstancedMesh) { - const realizedMesh = mesh.sourceMesh.clone(mesh.name, null, true); + const collisionScalingContainer = new BABYLON.TransformNode('collisionScalingContainer', this.engine.scene); + collisionScalingContainer.scaling = new BABYLON.Vector3(-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE); + collisionScalingContainer.parent = this.rootNode; + const collision = this.loaderResult.meshes.find(m => m.name.includes('__COLLISION__'))!; + collision.parent = collisionScalingContainer; - 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; + const lightBlockerScalingContainer = new BABYLON.TransformNode('lightBlockerScalingContainer', this.engine.scene); + lightBlockerScalingContainer.scaling = new BABYLON.Vector3(-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE); + lightBlockerScalingContainer.parent = this.rootNode; + const lightBlocker = this.loaderResult.meshes.find(m => m.name.includes('__LIGHT_BLOCKER__'))!; + lightBlocker.parent = lightBlockerScalingContainer; + lightBlocker.rotationQuaternion = null; + lightBlocker.rotation.y = Math.PI; - mesh.dispose(); - this.engine.scene.removeMesh(mesh); - this.meshes.push(realizedMesh); + const originalFloorRoot = this.loaderResult.transformNodes.find(t => t.name.includes('__FLOOR__'))!; + originalFloorRoot.scaling = new BABYLON.Vector3(-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE); + originalFloorRoot.parent = this.rootNode; + for (const child of originalFloorRoot.getChildMeshes()) { + if (child.material.name.includes('__FLOOR__')) { + child.material = this.floorMaterial; } } - this.wallRoots = { - n: this.loaderResult.transformNodes.find(t => t.name.includes('__WALL_N__'))!, - s: this.loaderResult.transformNodes.find(t => t.name.includes('__WALL_S__'))!, - w: this.loaderResult.transformNodes.find(t => t.name.includes('__WALL_W__'))!, - e: this.loaderResult.transformNodes.find(t => t.name.includes('__WALL_E__'))!, - }; - - this.pillarRoots = { - nw: this.loaderResult.transformNodes.find(t => t.name.includes('__PILLAR_NW__'))!, - ne: this.loaderResult.transformNodes.find(t => t.name.includes('__PILLAR_NE__'))!, - sw: this.loaderResult.transformNodes.find(t => t.name.includes('__PILLAR_SW__'))!, - se: this.loaderResult.transformNodes.find(t => t.name.includes('__PILLAR_SE__'))!, - }; - - const wallMaterial = findMaterial(this.meshes[0], '__WALL__'); - //wallMaterial.metadata.disableEnvMap = true; - this.wallMaterials = { - n: wallMaterial.clone('wallNMaterial'), - s: wallMaterial.clone('wallSMaterial'), - w: wallMaterial.clone('wallWMaterial'), - e: wallMaterial.clone('wallEMaterial'), - }; - - const beamMaterial = findMaterial(this.meshes[0], '__BEAM__'); - //beamMaterial.metadata.disableEnvMap = true; - this.wallBeamMaterials = { - n: beamMaterial.clone('wallNBeamMaterial'), - s: beamMaterial.clone('wallSBeamMaterial'), - w: beamMaterial.clone('wallWBeamMaterial'), - e: beamMaterial.clone('wallEBeamMaterial'), - }; - - const pillarMaterial = findMaterial(this.meshes[0], '__PILLAR__'); - //pillarMaterial.metadata.disableEnvMap = true; - this.pillarMaterials = { - nw: pillarMaterial.clone('pillarNWMaterial'), - ne: pillarMaterial.clone('pillarNEMaterial'), - sw: pillarMaterial.clone('pillarSWMaterial'), - se: pillarMaterial.clone('pillarSEMaterial'), - }; - - for (const [k, v] of Object.entries(this.wallRoots)) { - for (const m of v.getChildMeshes().filter(m => m.material === wallMaterial)) { - m.material = this.wallMaterials[k]; - } - for (const m of v.getChildMeshes().filter(m => m.material === beamMaterial)) { - m.material = this.wallBeamMaterials[k]; - } - } - for (const [k, v] of Object.entries(this.pillarRoots)) { - for (const m of v.getChildMeshes().filter(m => m.material === pillarMaterial)) { - m.material = this.pillarMaterials[k]; - } - } - - this.ceilingMaterial = findMaterial(this.meshes[0], '__CEILING__'); - //this.ceilingMaterial.metadata.disableEnvMap = true; - this.floorMaterial = findMaterial(this.meshes[0], '__FLOOR__'); - //this.floorMaterial.metadata.disableEnvMap = true; - - const baseboardMaterial = findMaterial(this.meshes[0], '__BASEBOARD__'); - //baseboardMaterial.metadata.disableEnvMap = true; - - for (const mesh of this.meshes) { - if (SYSTEM_HEYA_MESH_NAMES.some(name => mesh.name.includes(name))) continue; - mesh.receiveShadows = true; - - if (mesh.material !== this.floorMaterial) { // 床は他の何にも影を落とさないことが確定している - this.addShadowCaster(mesh); + const originalCeilingRoot = this.loaderResult.transformNodes.find(t => t.name.includes('__CEILING__'))!; + originalCeilingRoot.scaling = new BABYLON.Vector3(-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE); + originalCeilingRoot.parent = this.rootNode; + for (const child of originalCeilingRoot.getChildMeshes()) { + if (child.material.name.includes('__CEILING__')) { + child.material = this.ceilingMaterial; } } await this.applyOptions(options); } + public applyOptions(options: SimpleEnvOptions) { + // clean up + for (const type of ['zPositive', 'zNegative', 'xPositive', 'xNegative'] as const) { + const wallRoot = this.wallScalingContainers[type]; + for (const mesh of wallRoot.getChildMeshes()) { + mesh.dispose(); + this.engine.scene.removeMesh(mesh); + this.removeShadowCaster(mesh); + } + } + + // TODO: 返り値をpromiseにしてちゃんとテクスチャが読み終わってからresolveする + + for (const type of ['zPositive', 'zNegative', 'xPositive', 'xNegative'] as const) { + const wallRoot = this.wallScalingContainers[type]; + const wallOptions = options.walls[type]; + + const originalRoot = + type === 'zPositive' ? + options.window === 'kosidakamado' ? this.loaderResult!.transformNodes.find(t => t.name.includes('__WALL_KOSIDAKAMADO__'))! : + options.window === 'demado' ? this.loaderResult!.transformNodes.find(t => t.name.includes('__WALL_DEMADO__'))! : + this.loaderResult!.transformNodes.find(t => t.name.includes('__WALL__'))! + : type === 'zNegative' + ? this.loaderResult!.transformNodes.find(t => t.name.includes('__WALL_DOOR__'))! + : this.loaderResult!.transformNodes.find(t => t.name.includes('__WALL__'))!; + + for (const child of treeClone(originalRoot).getChildren()) { + child.parent = wallRoot; + } + + for (const child of wallRoot.getChildMeshes()) { + if (child.material.name.includes('__WALL__')) { + child.material = this.wallMaterials[type]; + } else if (child.material.name.includes('__BEAM__')) { + child.material = this.wallBeamMaterials[type]; + } + } + + for (const mesh of wallRoot.getChildMeshes()) { + if (mesh.name.includes('__BEAM__')) { + mesh.setEnabled(wallOptions.withBeam); + } else if (mesh.name.includes('__BASEBOARD__')) { + mesh.setEnabled(wallOptions.withBaseboard); + } + } + + { + const targetMaterial = this.wallMaterials[type]; + + targetMaterial.unfreeze(); + targetMaterial.albedoColor = new BABYLON.Color3(...wallOptions.color); + + const texPath = wallOptions.material === 'wood' ? '/client-assets/room/textures/wall-wood2.png' + : wallOptions.material === 'concrete' ? '/client-assets/room/textures/concrete1.png' + : null; + + if (texPath != null) { + const tex = new BABYLON.Texture(texPath, this.engine.scene, false, false); + targetMaterial.albedoTexture = tex; + } else { + targetMaterial.albedoTexture = null; + } + + targetMaterial.freeze(); + } + + { + const targetMaterial = this.wallBeamMaterials[type]; + + targetMaterial.unfreeze(); + targetMaterial.albedoColor = new BABYLON.Color3(...wallOptions.beamColor); + + const texPath = wallOptions.beamMaterial === 'wood' ? '/client-assets/room/textures/wall-wood2.png' + : wallOptions.beamMaterial === 'concrete' ? '/client-assets/room/textures/concrete1.png' + : null; + + if (texPath != null) { + const tex = new BABYLON.Texture(texPath, this.engine.scene, false, false); + targetMaterial.albedoTexture = tex; + } else { + targetMaterial.albedoTexture = null; + } + + targetMaterial.freeze(); + } + } + + for (const type of ['zp_xp', 'zp_xn', 'zn_xp', 'zn_xn'] as const) { + const pillarRoot = this.pillarRoots[type]; + const pillarOptions = options.pillars[type]; + + let isEnabled = pillarOptions.show; + if (!isEnabled) { + // 梁同士が直交することは許さない(z-fightingが発生する)ので柱を強制追加 + if (type === 'zp_xp') { + isEnabled = options.walls.zPositive.withBeam && options.walls.xPositive.withBeam; + } else if (type === 'zp_xn') { + isEnabled = options.walls.zPositive.withBeam && options.walls.xNegative.withBeam; + } else if (type === 'zn_xp') { + isEnabled = options.walls.zNegative.withBeam && options.walls.xPositive.withBeam; + } else if (type === 'zn_xn') { + isEnabled = options.walls.zNegative.withBeam && options.walls.xNegative.withBeam; + } + } + pillarRoot.setEnabled(isEnabled); + + const targetMaterial = this.pillarMaterials[type]; + + targetMaterial.unfreeze(); + targetMaterial.albedoColor = new BABYLON.Color3(...pillarOptions.color); + + const texPath = pillarOptions.material === 'wood' ? '/client-assets/room/textures/wall-wood2.png' + : pillarOptions.material === 'concrete' ? '/client-assets/room/textures/concrete1.png' + : null; + + if (texPath != null) { + const tex = new BABYLON.Texture(texPath, this.engine.scene, false, false); + targetMaterial.albedoTexture = tex; + } else { + targetMaterial.albedoTexture = null; + } + + targetMaterial.freeze(); + } + + { + this.ceilingMaterial.unfreeze(); + this.ceilingMaterial.albedoColor = new BABYLON.Color3(...options.ceiling.color); + + const texPath = options.ceiling.material === 'wood' ? '/client-assets/room/textures/ceiling-wood.png' + : options.ceiling.material === 'concrete' ? '/client-assets/room/textures/concrete3.png' + : null; + + if (texPath != null) { + const tex = new BABYLON.Texture(texPath, this.engine.scene, false, false); + this.ceilingMaterial.albedoTexture = tex; + } else { + this.ceilingMaterial.albedoTexture = null; + } + + this.ceilingMaterial.freeze(); + } + + { + this.floorMaterial.unfreeze(); + this.floorMaterial.albedoColor = new BABYLON.Color3(...options.flooring.color); + + const texPath = options.flooring.material === 'wood' ? '/client-assets/room/textures/flooring-wood.png' + : options.flooring.material === 'concrete' ? '/client-assets/room/textures/concrete3.png' + : null; + + if (texPath != null) { + const tex = new BABYLON.Texture(texPath, this.engine.scene, false, false); + this.floorMaterial.albedoTexture = tex; + } else { + this.floorMaterial.albedoTexture = null; + } + + this.floorMaterial.freeze(); + } + + for (const mesh of this.rootNode.getChildMeshes()) { + if (SYSTEM_HEYA_MESH_NAMES.some(name => mesh.name.includes(name))) continue; + mesh.receiveShadows = true; + //if (mesh.material !== this.floorMaterial) { // 床は他の何にも影を落とさないことが確定している + this.addShadowCaster(mesh); + //} + } + this.registerMeshes(this.rootNode.getChildMeshes()); + } + public setTime(time: number) { if (this.skyboxMat == null) return; @@ -228,141 +417,8 @@ export class SimpleEnvManager extends EnvManager { } } - public applyOptions(options: SimpleEnvOptions) { - // TODO: 返り値をpromiseにしてちゃんとテクスチャが読み終わってからresolveする - - for (const type of ['n', 's', 'w', 'e'] as const) { - const wallRoot = this.wallRoots[type]; - const wallOptions = options.walls[type]; - - for (const mesh of wallRoot.getChildMeshes()) { - if (mesh.name.includes('__BEAM__')) { - mesh.setEnabled(wallOptions.withBeam); - } else if (mesh.name.includes('__BASEBOARD__')) { - mesh.setEnabled(wallOptions.withBaseboard); - } - } - - { - const targetMaterial = this.wallMaterials[type]; - - targetMaterial.unfreeze(); - targetMaterial.albedoColor = new BABYLON.Color3(...wallOptions.color); - - const texPath = wallOptions.material === 'wood' ? '/client-assets/room/textures/wall-wood2.png' - : wallOptions.material === 'concrete' ? '/client-assets/room/textures/concrete1.png' - : null; - - if (texPath != null) { - const tex = new BABYLON.Texture(texPath, this.meshes[0].getScene(), false, false); - targetMaterial.albedoTexture = tex; - } else { - targetMaterial.albedoTexture = null; - } - - targetMaterial.freeze(); - } - - { - const targetMaterial = this.wallBeamMaterials[type]; - - targetMaterial.unfreeze(); - targetMaterial.albedoColor = new BABYLON.Color3(...wallOptions.beamColor); - - const texPath = wallOptions.beamMaterial === 'wood' ? '/client-assets/room/textures/wall-wood2.png' - : wallOptions.beamMaterial === 'concrete' ? '/client-assets/room/textures/concrete1.png' - : null; - - if (texPath != null) { - const tex = new BABYLON.Texture(texPath, this.meshes[0].getScene(), false, false); - targetMaterial.albedoTexture = tex; - } else { - targetMaterial.albedoTexture = null; - } - - targetMaterial.freeze(); - } - } - - for (const type of ['nw', 'ne', 'sw', 'se'] as const) { - const pillarRoot = this.pillarRoots[type]; - const pillarOptions = options.pillars[type]; - - let isEnabled = pillarOptions.show; - if (!isEnabled) { - // 梁同士が直交することは許さない(z-fightingが発生する)ので柱を強制追加 - if (type === 'nw') { - isEnabled = options.walls.n.withBeam && options.walls.w.withBeam; - } else if (type === 'ne') { - isEnabled = options.walls.n.withBeam && options.walls.e.withBeam; - } else if (type === 'sw') { - isEnabled = options.walls.s.withBeam && options.walls.w.withBeam; - } else if (type === 'se') { - isEnabled = options.walls.s.withBeam && options.walls.e.withBeam; - } - } - pillarRoot.setEnabled(isEnabled); - - const targetMaterial = this.pillarMaterials[type]; - - targetMaterial.unfreeze(); - targetMaterial.albedoColor = new BABYLON.Color3(...pillarOptions.color); - - const texPath = pillarOptions.material === 'wood' ? '/client-assets/room/textures/wall-wood2.png' - : pillarOptions.material === 'concrete' ? '/client-assets/room/textures/concrete1.png' - : null; - - if (texPath != null) { - const tex = new BABYLON.Texture(texPath, this.meshes[0].getScene(), false, false); - targetMaterial.albedoTexture = tex; - } else { - targetMaterial.albedoTexture = null; - } - - targetMaterial.freeze(); - } - - { - this.ceilingMaterial.unfreeze(); - this.ceilingMaterial.albedoColor = new BABYLON.Color3(...options.ceiling.color); - - const texPath = options.ceiling.material === 'wood' ? '/client-assets/room/textures/ceiling-wood.png' - : options.ceiling.material === 'concrete' ? '/client-assets/room/textures/concrete3.png' - : null; - - if (texPath != null) { - const tex = new BABYLON.Texture(texPath, this.meshes[0].getScene(), false, false); - this.ceilingMaterial.albedoTexture = tex; - } else { - this.ceilingMaterial.albedoTexture = null; - } - - this.ceilingMaterial.freeze(); - } - - { - this.floorMaterial.unfreeze(); - this.floorMaterial.albedoColor = new BABYLON.Color3(...options.flooring.color); - - const texPath = options.flooring.material === 'wood' ? '/client-assets/room/textures/flooring-wood.png' - : options.flooring.material === 'concrete' ? '/client-assets/room/textures/concrete3.png' - : null; - - if (texPath != null) { - const tex = new BABYLON.Texture(texPath, this.meshes[0].getScene(), false, false); - this.floorMaterial.albedoTexture = tex; - } else { - this.floorMaterial.albedoTexture = null; - } - - this.floorMaterial.freeze(); - } - - this.registerMeshes(this.meshes); - } - public dispose() { - for (const m of this.meshes) { + for (const m of this.rootNode.getChildMeshes()) { m.dispose(false, true); } for (const m of Object.values(this.wallMaterials ?? {})) { diff --git a/packages/frontend-misskey-world-engine/src/utility.ts b/packages/frontend-misskey-world-engine/src/utility.ts index 5f9cf06b69..f111ef7e04 100644 --- a/packages/frontend-misskey-world-engine/src/utility.ts +++ b/packages/frontend-misskey-world-engine/src/utility.ts @@ -769,3 +769,18 @@ export class ModelExplorer { return node; } } + +export function treeClone(source: BABYLON.TransformNode) { + const clonedRoot = source.clone(source.name, null, true)!; + + for (const child of source.getChildren()) { + if (child instanceof BABYLON.TransformNode) { + const clonedChild = treeClone(child); + clonedChild.parent = clonedRoot; + } else { + child.clone(child.name, clonedRoot); + } + } + + return clonedRoot; +} diff --git a/packages/frontend/assets/room/envs/default/default.blend b/packages/frontend/assets/room/envs/default/default.blend deleted file mode 100644 index 8699c02f92..0000000000 Binary files a/packages/frontend/assets/room/envs/default/default.blend and /dev/null differ diff --git a/packages/frontend/assets/room/envs/default/300.glb b/packages/frontend/assets/room/envs/simple/300.glb similarity index 84% rename from packages/frontend/assets/room/envs/default/300.glb rename to packages/frontend/assets/room/envs/simple/300.glb index e4bb059274..2aa5b8a52f 100644 Binary files a/packages/frontend/assets/room/envs/default/300.glb and b/packages/frontend/assets/room/envs/simple/300.glb differ diff --git a/packages/frontend/assets/room/envs/simple/simple.blend b/packages/frontend/assets/room/envs/simple/simple.blend new file mode 100644 index 0000000000..5f6ad4cd32 Binary files /dev/null and b/packages/frontend/assets/room/envs/simple/simple.blend differ diff --git a/packages/frontend/src/pages/rooms/room.simple-env-options.vue b/packages/frontend/src/pages/rooms/room.simple-env-options.vue index 3f25b84159..d4bb57df05 100644 --- a/packages/frontend/src/pages/rooms/room.simple-env-options.vue +++ b/packages/frontend/src/pages/rooms/room.simple-env-options.vue @@ -11,23 +11,23 @@ SPDX-License-Identifier: AGPL-3.0-only
- - + + - - + + - + - +
@@ -36,24 +36,24 @@ SPDX-License-Identifier: AGPL-3.0-only
- - - - - - - - - - - + - + + + + + + + + + + +
diff --git a/packages/frontend/src/pages/rooms/room.simple-env-pillar-options.vue b/packages/frontend/src/pages/rooms/room.simple-env-pillar-options.vue index 71299dace7..fbce769fea 100644 --- a/packages/frontend/src/pages/rooms/room.simple-env-pillar-options.vue +++ b/packages/frontend/src/pages/rooms/room.simple-env-pillar-options.vue @@ -39,14 +39,14 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkRange from '@/components/MkRange.vue'; const props = defineProps<{ - options: SimpleEnvOptions['pillars']['nw' | 'ne' | 'sw' | 'se']; + options: SimpleEnvOptions['pillars']['zp_xp' | 'zp_xn' | 'zn_xp' | 'zn_xn']; }>(); const emit = defineEmits<{ - (ev: 'update', v: SimpleEnvOptions['pillars']['nw' | 'ne' | 'sw' | 'se']): void; + (ev: 'update', v: SimpleEnvOptions['pillars']['zp_xp' | 'zp_xn' | 'zn_xp' | 'zn_xn']): void; }>(); -function update(v: Partial) { +function update(v: Partial) { emit('update', { ...props.options, ...v }); } diff --git a/packages/frontend/src/pages/rooms/room.simple-env-wall-options.vue b/packages/frontend/src/pages/rooms/room.simple-env-wall-options.vue index ba93e36201..0f0639cb73 100644 --- a/packages/frontend/src/pages/rooms/room.simple-env-wall-options.vue +++ b/packages/frontend/src/pages/rooms/room.simple-env-wall-options.vue @@ -57,14 +57,14 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkRange from '@/components/MkRange.vue'; const props = defineProps<{ - options: SimpleEnvOptions['walls']['n' | 's' | 'w' | 'e']; + options: SimpleEnvOptions['walls']['zPositive' | 'zNegative' | 'xPositive' | 'xNegative']; }>(); const emit = defineEmits<{ - (ev: 'update', v: SimpleEnvOptions['walls']['n' | 's' | 'w' | 'e']): void; + (ev: 'update', v: SimpleEnvOptions['walls']['zPositive' | 'zNegative' | 'xPositive' | 'xNegative']): void; }>(); -function update(v: Partial) { +function update(v: Partial) { emit('update', { ...props.options, ...v }); } diff --git a/packages/misskey-world/src/room/env.ts b/packages/misskey-world/src/room/env.ts index d2e8de87d3..9f78d4391a 100644 --- a/packages/misskey-world/src/room/env.ts +++ b/packages/misskey-world/src/room/env.ts @@ -10,7 +10,7 @@ export type JapaneseEnvOptions = { export type SimpleEnvOptions = { dimension: [number, number]; window: 'none' | 'kosidakamado' | 'demado' | 'hakidasimado'; - walls: Record<'n' | 's' | 'w' | 'e', { + walls: Record<'zPositive' | 'zNegative' | 'xPositive' | 'xNegative', { material: null | 'wood' | 'concrete'; color: [number, number, number]; withBeam: boolean; @@ -18,7 +18,7 @@ export type SimpleEnvOptions = { beamColor: [number, number, number]; withBaseboard: boolean; }>; - pillars: Record<'nw' | 'ne' | 'sw' | 'se', { + pillars: Record<'zp_xp' | 'zp_xn' | 'zn_xp' | 'zn_xn', { material: null | 'wood' | 'concrete'; color: [number, number, number]; show: boolean; @@ -62,8 +62,8 @@ export type CustomMadoriEnvOptions = { }[]; units: ({ type: 'floor'; - walls?: Record<'n' | 's' | 'w' | 'e', CustomMadoriEnvWall | undefined>; - pillars?: Record<'nw' | 'ne' | 'sw' | 'se', { + walls?: Record<'zPositive' | 'zNegative' | 'xPositive' | 'xNegative', CustomMadoriEnvWall | undefined>; + pillars?: Record<'zp_xp' | 'zp_xn' | 'zn_xp' | 'zn_xn', { material?: string; show?: boolean; }>; @@ -81,7 +81,7 @@ export function getDefaultSimpleEnvOptions(): SimpleEnvOptions { dimension: [300, 300], window: 'demado', walls: { - n: { + zPositive: { material: null, color: [0.9, 0.9, 0.9], withBeam: false, @@ -89,7 +89,7 @@ export function getDefaultSimpleEnvOptions(): SimpleEnvOptions { beamColor: [0.8, 0.8, 0.8], withBaseboard: true, }, - e: { + zNegative: { material: null, color: [0.9, 0.9, 0.9], withBeam: false, @@ -97,7 +97,7 @@ export function getDefaultSimpleEnvOptions(): SimpleEnvOptions { beamColor: [0.8, 0.8, 0.8], withBaseboard: true, }, - s: { + xPositive: { material: null, color: [0.9, 0.9, 0.9], withBeam: false, @@ -105,7 +105,7 @@ export function getDefaultSimpleEnvOptions(): SimpleEnvOptions { beamColor: [0.8, 0.8, 0.8], withBaseboard: true, }, - w: { + xNegative: { material: null, color: [0.9, 0.9, 0.9], withBeam: false, @@ -115,22 +115,22 @@ export function getDefaultSimpleEnvOptions(): SimpleEnvOptions { }, }, pillars: { - nw: { + zp_xp: { material: null, color: [0.9, 0.9, 0.9], show: false, }, - ne: { + zp_xn: { material: null, color: [0.9, 0.9, 0.9], show: false, }, - sw: { + zn_xp: { material: null, color: [0.9, 0.9, 0.9], show: false, }, - se: { + zn_xn: { material: null, color: [0.9, 0.9, 0.9], show: false, @@ -161,16 +161,16 @@ export function getDefaultCustomMadoriEnvOptions(): CustomMadoriEnvOptions { const units = Array.from({ length: 15 * 15 }, () => ({ type: 'floor', walls: { - n: { + zPositive: { material: '0', }, - e: { + zNegative: { material: '0', }, - s: { + xPositive: { material: '0', }, - w: { + xNegative: { material: '0', }, },