From f58de15d458b2741132fd4480dd329fcc959acde Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Mon, 16 Feb 2026 15:14:37 +0900 Subject: [PATCH] wip --- packages/frontend/src/pages/room.vue | 2 +- packages/frontend/src/utility/room/engine.ts | 82 ++++++++++++------- .../src/utility/room/objects/aquarium.ts | 2 +- .../src/utility/room/objects/blind.ts | 20 ++--- .../frontend/src/utility/room/objects/book.ts | 4 +- .../src/utility/room/objects/cardboardBox.ts | 6 +- .../utility/room/objects/ceilingFanLight.ts | 2 +- .../src/utility/room/objects/lavaLamp.ts | 2 +- .../src/utility/room/objects/wallClock.ts | 2 +- 9 files changed, 71 insertions(+), 51 deletions(-) diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index 9ed68640a7..bbe7e9d8d0 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -291,7 +291,7 @@ onMounted(() => { if (v == null) { interacions.value = []; } else { - const o = engine.value.def.objects.find(o => o.id === v)!; + const o = engine.value.roomSetting.objects.find(o => o.id === v)!; const obji = engine.value.objectInstances.get(o.id)!; interacions.value = Object.entries(obji.interactions).map(([interactionId, interactionInfo]) => ({ id: interactionId, diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index 4a4422b75a..fdc0c35c97 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -66,7 +66,7 @@ type ObjectDef> = { createInstance: (args: { room: RoomEngine; root: BABYLON.Mesh; - o: RoomSettingObject; + options: Options; loaderResult: BABYLON.ISceneLoaderAsyncResult; meshUpdated: () => void; }) => RoomObjectInstance; @@ -121,7 +121,7 @@ export class RoomEngine { public selectedObjectId = ref(null); private time: 0 | 1 | 2 = 2; // 0: 昼, 1: 夕, 2: 夜 private roomCollisionMeshes: BABYLON.AbstractMesh[] = []; - public def: RoomSetting; + public roomSetting: RoomSetting; public enableGridSnapping = ref(true); public gridSnappingScale = ref(8/*cm*/); private putParticleSystem: BABYLON.ParticleSystem; @@ -136,10 +136,10 @@ export class RoomEngine { public isEditMode = ref(false); public isSitting = ref(false); - constructor(def: RoomSetting, options: { + constructor(roomSetting: RoomSetting, options: { canvas: HTMLCanvasElement; }) { - this.def = def; + this.roomSetting = roomSetting; this.canvas = options.canvas; registerBuiltInLoaders(); @@ -426,9 +426,16 @@ export class RoomEngine { } public async init() { - await this.loadRoomModel(this.def.roomType); + await this.loadRoomModel(this.roomSetting.roomType); await this.loadEnvModel(); - await Promise.all(this.def.objects.map(o => this.loadObject(o))); + await Promise.all(this.roomSetting.objects.map(o => this.loadObject({ + id: o.id, + type: o.type, + position: new BABYLON.Vector3(...o.position), + rotation: new BABYLON.Vector3(o.rotation[0], -o.rotation[1], o.rotation[2]), + options: o.options, + isMainLight: o.isMainLight, + }))); //const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 1/*cm*/ }, this.scene); @@ -446,7 +453,7 @@ export class RoomEngine { const applyTvTexture = (tlIndex: number) => { const [index, duration] = tvProgram.timeline[tlIndex]; - const tvIds = this.def.objects.entries().filter(([id, o]) => o.type === 'tv').map(([id, o]) => o.id); + const tvIds = this.roomSetting.objects.entries().filter(([id, o]) => o.type === 'tv').map(([id, o]) => o.id); for (const tvId of tvIds) { const tvMesh = this.objectMeshs.get(tvId); @@ -519,7 +526,7 @@ export class RoomEngine { if (this.grabbingCtx == null) return; const grabbing = this.grabbingCtx; - const placement = getObjectDef(this.def.objects.find(o => o.id === grabbing.objectId)!.type).placement; + const placement = getObjectDef(this.roomSetting.objects.find(o => o.id === grabbing.objectId)!.type).placement; const dir = this.camera.getDirection(BABYLON.Axis.Z).scale(this.scene.useRightHandedSystem ? -1 : 1); const newPos = this.camera.position.add(dir.scale(grabbing.distance)).add(grabbing.originalDiffOfPosition); @@ -607,9 +614,9 @@ export class RoomEngine { } if (sticky != null) { - this.def.objects.find(o => o.id === grabbing.objectId)!.sticky = sticky; + this.roomSetting.objects.find(o => o.id === grabbing.objectId)!.sticky = sticky; } else { - this.def.objects.find(o => o.id === grabbing.objectId)!.sticky = null; + this.roomSetting.objects.find(o => o.id === grabbing.objectId)!.sticky = null; } grabbing.mesh.rotation = newRotation; @@ -757,14 +764,21 @@ export class RoomEngine { } } - private async loadObject(o: RoomSetting['objects'][0]) { - const def = getObjectDef(o.type); + private async loadObject(args: { + type: string; + id: string; + position: BABYLON.Vector3; + rotation: BABYLON.Vector3; + options: any; + isMainLight?: boolean; + }) { + const def = getObjectDef(args.type); const camelToKebab = (str: string) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); - const root = new BABYLON.Mesh(`object_${o.id}_${o.type}`, this.scene); + const root = new BABYLON.Mesh(`object_${args.id}_${args.type}`, this.scene); - const loaderResult = await BABYLON.ImportMeshAsync(`/client-assets/room/objects/${camelToKebab(o.type)}/${camelToKebab(o.type)}.glb`, this.scene); + const loaderResult = await BABYLON.ImportMeshAsync(`/client-assets/room/objects/${camelToKebab(args.type)}/${camelToKebab(args.type)}.glb`, this.scene); let hasCollisionMesh = false; for (const mesh of loaderResult.meshes) { @@ -776,8 +790,8 @@ export class RoomEngine { const metadata = { isObject: true, - objectId: o.id, - objectType: o.type, + objectId: args.id, + objectType: args.type, isCollision: !hasCollisionMesh, }; @@ -787,8 +801,8 @@ export class RoomEngine { root.addChild(subRoot); - root.position = new BABYLON.Vector3(...o.position); - root.rotation = new BABYLON.Vector3(o.rotation[0], -o.rotation[1], o.rotation[2]); + root.position = args.position.clone(); + root.rotation = args.rotation.clone(); root.metadata = metadata; const meshUpdated = (meshes: BABYLON.Mesh[]) => { @@ -811,13 +825,13 @@ export class RoomEngine { mesh.receiveShadows = false; mesh.isVisible = false; } else { - if (!o.isMainLight && def.receiveShadows !== false) mesh.receiveShadows = true; - if (!o.isMainLight && def.castShadows !== false) { + if (!args.isMainLight && def.receiveShadows !== false) mesh.receiveShadows = true; + if (!args.isMainLight && def.castShadows !== false) { this.shadowGenerator1.addShadowCaster(mesh); this.shadowGenerator2.addShadowCaster(mesh); } - mesh.renderOutline = this.selectedObjectId.value === o.id; + mesh.renderOutline = this.selectedObjectId.value === args.id; mesh.outlineWidth = 0.003; mesh.outlineColor = new BABYLON.Color3(1, 0, 0); //if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2); @@ -830,25 +844,25 @@ export class RoomEngine { meshUpdated(loaderResult.meshes); - this.objectMeshs.set(o.id, root); + this.objectMeshs.set(args.id, root); const objectInstance = def.createInstance({ room: this, root, - o: o, + options: args.options, loaderResult: loaderResult, meshUpdated: () => { - meshUpdated(this.objectMeshs.get(o.id)!.getChildMeshes() as BABYLON.Mesh[]); + meshUpdated(this.objectMeshs.get(args.id)!.getChildMeshes() as BABYLON.Mesh[]); }, }); - this.objectInstances.set(o.id, objectInstance); + this.objectInstances.set(args.id, objectInstance); if (objectInstance.onInited != null) { objectInstance.onInited(); } - if (o.isMainLight) { + if (args.isMainLight) { this.roomLight.position = root.position.add(new BABYLON.Vector3(0, -1/*cm*/, 0)); } } @@ -857,7 +871,7 @@ export class RoomEngine { if (this.grabbingCtx != null) { // 親から先に外していく const removeStickyParentRecursively = (mesh: BABYLON.Mesh) => { - const stickyObjectIds = Array.from(this.def.objects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id); + const stickyObjectIds = Array.from(this.roomSetting.objects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id); for (const soid of stickyObjectIds) { const soMesh = this.objectMeshs.get(soid)!; soMesh.setParent(null); @@ -868,6 +882,7 @@ export class RoomEngine { const pos = this.grabbingCtx.mesh.position.clone(); //this.grabbing.ghost.dispose(false, true); this.grabbingCtx.ghost.dispose(false, false); + this.grabbingCtx.onDone?.(); this.grabbingCtx = null; this.selectObject(null); @@ -908,7 +923,7 @@ export class RoomEngine { // 子から先に適用していく const setStickyParentRecursively = (mesh: BABYLON.AbstractMesh) => { - const stickyObjectIds = Array.from(this.def.objects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id); + const stickyObjectIds = Array.from(this.roomSetting.objects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id); for (const soid of stickyObjectIds) { const soMesh = this.objectMeshs.get(soid)!; setStickyParentRecursively(soMesh); @@ -919,7 +934,7 @@ export class RoomEngine { const descendantStickyObjectIds: string[] = []; const collectDescendantStickyObjectIds = (parentId: string) => { - const childIds = Array.from(this.def.objects.filter(o => o.sticky === parentId)).map(o => o.id); + const childIds = Array.from(this.roomSetting.objects.filter(o => o.sticky === parentId)).map(o => o.id); for (const cid of childIds) { descendantStickyObjectIds.push(cid); collectDescendantStickyObjectIds(cid); @@ -940,7 +955,7 @@ export class RoomEngine { rotation: 0, ghost: ghost, descendantStickyObjectIds, - isMainLight: this.def.objects.find(o => o.id === selectedObject.metadata.objectId)?.isMainLight ?? false, + isMainLight: this.roomSetting.objects.find(o => o.id === selectedObject.metadata.objectId)?.isMainLight ?? false, }; const intervalId = window.setInterval(() => { @@ -956,7 +971,7 @@ export class RoomEngine { } private interact(oid: string) { - const o = this.def.objects.find(o => o.id === oid)!; + const o = this.roomSetting.objects.find(o => o.id === oid)!; const mesh = this.objectMeshs.get(o.id)!; const objDef = getObjectDef(o.type); const obji = this.objectInstances.get(o.id)!; @@ -1008,6 +1023,11 @@ export class RoomEngine { const id = genId(); + this.loadObject({ + id: id, + + }); + sound.playUrl('/client-assets/room/sfx/grab.mp3', { volume: 1, playbackRate: 1, diff --git a/packages/frontend/src/utility/room/objects/aquarium.ts b/packages/frontend/src/utility/room/objects/aquarium.ts index 91f29d36e8..420f5eaca0 100644 --- a/packages/frontend/src/utility/room/objects/aquarium.ts +++ b/packages/frontend/src/utility/room/objects/aquarium.ts @@ -10,7 +10,7 @@ export const aquarium = defineObject({ id: 'aquarium', defaultOptions: {}, placement: 'top', - createInstance: ({ room, o, root }) => { + createInstance: ({ room, root }) => { return { onInited: () => { const noiseTexture = new BABYLON.NoiseProceduralTexture('perlin', 256, room.scene); diff --git a/packages/frontend/src/utility/room/objects/blind.ts b/packages/frontend/src/utility/room/objects/blind.ts index e1a81610f2..0c2864e2f7 100644 --- a/packages/frontend/src/utility/room/objects/blind.ts +++ b/packages/frontend/src/utility/room/objects/blind.ts @@ -14,9 +14,9 @@ export const blind = defineObject({ open: 1, }, placement: 'bottom', - createInstance: ({ room, o, loaderResult, meshUpdated }) => { + createInstance: ({ options, loaderResult, meshUpdated }) => { const blade = loaderResult.meshes[0].getChildMeshes().find(m => m.name === 'Blade') as BABYLON.Mesh; - blade.rotation = new BABYLON.Vector3(o.options.angle, 0, 0); + blade.rotation = new BABYLON.Vector3(options.angle, 0, 0); let blades = [] as BABYLON.Mesh[]; @@ -26,12 +26,12 @@ export const blind = defineObject({ } blades = []; - for (let i = 0; i < o.options.blades; i++) { + for (let i = 0; i < options.blades; i++) { const b = blade.clone(); - if (i / o.options.blades < o.options.open) { + if (i / options.blades < options.open) { b.position.y -= (i * 4/*cm*/) / WORLD_SCALE; } else { - b.position.y -= (((o.options.blades - 1) * o.options.open * 4/*cm*/) + (i * 0.3/*cm*/)) / WORLD_SCALE; + b.position.y -= (((options.blades - 1) * options.open * 4/*cm*/) + (i * 0.3/*cm*/)) / WORLD_SCALE; } blades.push(b); } @@ -41,7 +41,7 @@ export const blind = defineObject({ const applyAngle = () => { for (const b of [blade, ...blades]) { - b.rotation.x = o.options.angle; + b.rotation.x = options.angle; b.rotation.x += Math.random() * 0.3 - 0.15; } }; @@ -57,16 +57,16 @@ export const blind = defineObject({ adjustBladeRotation: { label: 'Adjust blade rotation', fn: () => { - o.options.angle += Math.PI / 8; - if (o.options.angle >= Math.PI / 2) o.options.angle = -Math.PI / 2; + options.angle += Math.PI / 8; + if (options.angle >= Math.PI / 2) options.angle = -Math.PI / 2; applyAngle(); }, }, openClose: { label: 'Open/close', fn: () => { - o.options.open -= 0.25; - if (o.options.open < 0) o.options.open = 1; + options.open -= 0.25; + if (options.open < 0) options.open = 1; applyOpeningState(); }, }, diff --git a/packages/frontend/src/utility/room/objects/book.ts b/packages/frontend/src/utility/room/objects/book.ts index ac2f81c5c0..cca2f2319b 100644 --- a/packages/frontend/src/utility/room/objects/book.ts +++ b/packages/frontend/src/utility/room/objects/book.ts @@ -11,12 +11,12 @@ export const book = defineObject({ variation: null as number | null, }, placement: 'top', - createInstance: ({ room, o, root }) => { + createInstance: ({ options, root }) => { return { onInited: () => { const mesh = root.getChildMeshes()[1] as BABYLON.Mesh; mesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); - const index = o.options.variation ?? 0; + const index = options.variation ?? 0; const x = index % 8; const y = Math.floor(index / 8); diff --git a/packages/frontend/src/utility/room/objects/cardboardBox.ts b/packages/frontend/src/utility/room/objects/cardboardBox.ts index e3debf509e..d5f17a4334 100644 --- a/packages/frontend/src/utility/room/objects/cardboardBox.ts +++ b/packages/frontend/src/utility/room/objects/cardboardBox.ts @@ -12,15 +12,15 @@ export const cardboardBox = defineObject({ variation: null as string | null, }, placement: 'top', - createInstance: ({ room, o, root }) => { + createInstance: ({ room, options, root }) => { return { onInited: () => { const boxMesh = root.getChildMeshes().find(m => m.name === 'Box') as BABYLON.Mesh; - if (o.options.variation === 'mikan') { + if (options.variation === 'mikan') { const tex = new BABYLON.Texture('/client-assets/room/objects/cardboard-box/textures/mikan.png', room.scene, false, false); (boxMesh.material as BABYLON.PBRMaterial).albedoTexture = tex; (boxMesh.material as BABYLON.PBRMaterial).albedoColor = new BABYLON.Color3(1, 1, 1); - } else if (o.options.variation === 'aizon') { + } else if (options.variation === 'aizon') { const tex = new BABYLON.Texture('/client-assets/room/objects/cardboard-box/textures/aizon.png', room.scene, false, false); (boxMesh.material as BABYLON.PBRMaterial).albedoTexture = tex; (boxMesh.material as BABYLON.PBRMaterial).albedoColor = new BABYLON.Color3(1, 1, 1); diff --git a/packages/frontend/src/utility/room/objects/ceilingFanLight.ts b/packages/frontend/src/utility/room/objects/ceilingFanLight.ts index bd6d52ea99..1a7a7c3ed3 100644 --- a/packages/frontend/src/utility/room/objects/ceilingFanLight.ts +++ b/packages/frontend/src/utility/room/objects/ceilingFanLight.ts @@ -10,7 +10,7 @@ export const ceilingFanLight = defineObject({ id: 'ceilingFanLight', defaultOptions: {}, placement: 'ceiling', - createInstance: ({ room, o, root }) => { + createInstance: ({ room, root }) => { return { onInited: () => { const rotor = root.getChildMeshes().find(m => m.name === 'Rotor') as BABYLON.Mesh; diff --git a/packages/frontend/src/utility/room/objects/lavaLamp.ts b/packages/frontend/src/utility/room/objects/lavaLamp.ts index b08aab90e0..551f5ed8a2 100644 --- a/packages/frontend/src/utility/room/objects/lavaLamp.ts +++ b/packages/frontend/src/utility/room/objects/lavaLamp.ts @@ -9,7 +9,7 @@ export const lavaLamp = defineObject({ id: 'lavaLamp', defaultOptions: {}, placement: 'top', - createInstance: ({ room, o, root }) => { + createInstance: ({ room, root }) => { return { onInited: () => { const light = new BABYLON.PointLight('lavaLampLight', new BABYLON.Vector3(0, 11/*cm*/, 0), room.scene); diff --git a/packages/frontend/src/utility/room/objects/wallClock.ts b/packages/frontend/src/utility/room/objects/wallClock.ts index 49a39f4773..5437696639 100644 --- a/packages/frontend/src/utility/room/objects/wallClock.ts +++ b/packages/frontend/src/utility/room/objects/wallClock.ts @@ -10,7 +10,7 @@ export const wallClock = defineObject({ id: 'wallClock', defaultOptions: {}, placement: 'side', - createInstance: ({ room, o, root }) => { + createInstance: ({ room, root }) => { return { onInited: () => { const hourHand = root.getChildMeshes().find(m => m.name === 'HandH') as BABYLON.Mesh;