diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index bbe7e9d8d0..2070a0e96b 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only + addObject @@ -33,6 +34,7 @@ import { ensureSignin } from '@/i'; import MkButton from '@/components/MkButton.vue'; import { RoomEngine } from '@/utility/room/engine.js'; import { getObjectDef } from '@/utility/room/object-defs.js'; +import MkSelect from '@/components/MkSelect.vue'; const canvas = useTemplateRef('canvas'); @@ -291,8 +293,7 @@ onMounted(() => { if (v == null) { interacions.value = []; } else { - const o = engine.value.roomSetting.objects.find(o => o.id === v)!; - const obji = engine.value.objectInstances.get(o.id)!; + const obji = engine.value.objectInstances.get(v)!; interacions.value = Object.entries(obji.interactions).map(([interactionId, interactionInfo]) => ({ id: interactionId, label: interactionInfo.label, @@ -328,6 +329,11 @@ function toggleEditMode() { canvas.value!.focus(); } +function addObject() { + engine.value?.addObject('mug'); + canvas.value!.focus(); +} + definePage(() => ({ title: 'Room', icon: 'ti ti-door', diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index fdc0c35c97..045a08fffb 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -27,6 +27,7 @@ import { getObjectDef, OBJECT_DEFS } from './object-defs.js'; import { HorizontalCameraKeyboardMoveInput } from './utility.js'; import * as sound from '@/utility/sound.js'; +// babylonのドメイン知識は持たない type RoomSettingObject = { id: string; type: string; @@ -105,9 +106,11 @@ export class RoomEngine { public objectInstances: Map> = new Map(); private grabbingCtx: { objectId: string; + objectType: string; mesh: BABYLON.Mesh; initialPosition: BABYLON.Vector3; initialRotation: BABYLON.Vector3; + initialSticky: string | null; originalDiffOfPosition: BABYLON.Vector3; originalDiffOfRotationY: number; distance: number; @@ -122,6 +125,7 @@ export class RoomEngine { private time: 0 | 1 | 2 = 2; // 0: 昼, 1: 夕, 2: 夜 private roomCollisionMeshes: BABYLON.AbstractMesh[] = []; public roomSetting: RoomSetting; + private stickyMap: Map = new Map(); // 何が何に吸着しているか public enableGridSnapping = ref(true); public gridSnappingScale = ref(8/*cm*/); private putParticleSystem: BABYLON.ParticleSystem; @@ -437,6 +441,10 @@ export class RoomEngine { isMainLight: o.isMainLight, }))); + for (const o of this.roomSetting.objects) { + this.stickyMap.set(o.id, o.sticky ?? null); + } + //const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 1/*cm*/ }, this.scene); // update tv texure @@ -526,7 +534,7 @@ export class RoomEngine { if (this.grabbingCtx == null) return; const grabbing = this.grabbingCtx; - const placement = getObjectDef(this.roomSetting.objects.find(o => o.id === grabbing.objectId)!.type).placement; + const placement = getObjectDef(grabbing.objectType).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); @@ -613,11 +621,7 @@ export class RoomEngine { } } - if (sticky != null) { - this.roomSetting.objects.find(o => o.id === grabbing.objectId)!.sticky = sticky; - } else { - this.roomSetting.objects.find(o => o.id === grabbing.objectId)!.sticky = null; - } + this.stickyMap.set(grabbing.objectId, sticky); grabbing.mesh.rotation = newRotation; grabbing.mesh.position = newPos; @@ -871,7 +875,7 @@ export class RoomEngine { if (this.grabbingCtx != null) { // 親から先に外していく const removeStickyParentRecursively = (mesh: BABYLON.Mesh) => { - const stickyObjectIds = Array.from(this.roomSetting.objects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id); + const stickyObjectIds = this.stickyMap.entries().filter(([k, v]) => v === mesh.metadata.objectId).map(([k, v]) => k); for (const soid of stickyObjectIds) { const soMesh = this.objectMeshs.get(soid)!; soMesh.setParent(null); @@ -923,7 +927,7 @@ export class RoomEngine { // 子から先に適用していく const setStickyParentRecursively = (mesh: BABYLON.AbstractMesh) => { - const stickyObjectIds = Array.from(this.roomSetting.objects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id); + const stickyObjectIds = this.stickyMap.entries().filter(([k, v]) => v === mesh.metadata.objectId).map(([k, v]) => k); for (const soid of stickyObjectIds) { const soMesh = this.objectMeshs.get(soid)!; setStickyParentRecursively(soMesh); @@ -934,7 +938,7 @@ export class RoomEngine { const descendantStickyObjectIds: string[] = []; const collectDescendantStickyObjectIds = (parentId: string) => { - const childIds = Array.from(this.roomSetting.objects.filter(o => o.sticky === parentId)).map(o => o.id); + const childIds = this.stickyMap.entries().filter(([k, v]) => v === parentId).map(([k, v]) => k); for (const cid of childIds) { descendantStickyObjectIds.push(cid); collectDescendantStickyObjectIds(cid); @@ -946,9 +950,11 @@ export class RoomEngine { this.grabbingCtx = { objectId: selectedObject.metadata.objectId, + objectType: selectedObject.metadata.objectType, mesh: selectedObject, initialPosition: selectedObject.position.clone(), initialRotation: selectedObject.rotation.clone(), + initialSticky: this.stickyMap.get(selectedObject.metadata.objectId) ?? null, originalDiffOfPosition: selectedObject.position.subtract(this.camera.position.add(dir.scale(distance))), originalDiffOfRotationY: selectedObject.rotation.subtract(this.camera.rotation).y, distance: distance, @@ -1018,16 +1024,35 @@ export class RoomEngine { } } - public addObject() { + public async addObject(type: string) { + this.isEditMode.value = true; const dir = this.camera.getDirection(BABYLON.Axis.Z).scale(this.scene.useRightHandedSystem ? -1 : 1); const id = genId(); - this.loadObject({ - id: id, + const def = getObjectDef(type); + await this.loadObject({ + id: id, + type, + position: new BABYLON.Vector3(0, 0, 0), + rotation: new BABYLON.Vector3(0, 0, 0), + options: def.defaultOptions, }); + this.selectedObjectId.value = id; + + this.toggleGrab(); + + // todo: grab確定したら + //this.roomSetting.objects.push({ + // id, + // type, + // position: [0, 0, 0], + // rotation: [0, 0, 0], + // options: def.defaultOptions, + //}); + sound.playUrl('/client-assets/room/sfx/grab.mp3', { volume: 1, playbackRate: 1,