From f3d0edf54621764e633c093b574d6cdb584c2919 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Mon, 11 May 2026 13:35:32 +0900 Subject: [PATCH] wip: sr in edit mode --- packages/frontend/src/pages/room.vue | 2 +- packages/frontend/src/world/room/engine.ts | 55 ++++++++++++++++--- packages/frontend/src/world/room/object.ts | 8 ++- .../src/world/room/objects/laptopPc.ts | 5 +- .../frontend/src/world/room/previewEngine.ts | 11 +++- 5 files changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index 3a1749dbe8..1f309b0aa2 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -264,7 +264,7 @@ const roomControllerOptions = computed(() => ({ resolution: resolution.value, antialias: antialias.value, useVirtualJoystick, - workerMode: true, + workerMode: false, })); const controller = markRaw(new RoomController(data, roomControllerOptions.value)); diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index 86a3ddf732..e296b0e04e 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -198,6 +198,7 @@ export class RoomEngine extends EventEmitter { } public isSitting = false; + private inited = false; private fps: number | null = null; private disposed = false; @@ -477,6 +478,8 @@ export class RoomEngine extends EventEmitter { } } }); + + this.inited = true; } private currentRafId: number | null = null; @@ -878,7 +881,18 @@ export class RoomEngine extends EventEmitter { const objectInstance = await def.createInstance({ scene: this.scene, - sr: this.sr, + sr: { + updateMesh: (mesh) => { + if (!this.inited) return; + this.sr.updateMesh(mesh); + }, + reset: () => { + if (!this.inited) return; + this.sr.disableSnapshotRendering(); + this.sr.enableSnapshotRendering(); + }, + fixParticleSystem: (ps) => this.sr.fixParticleSystem(ps), + }, lc: this.lightContainer, root, options: args.options, @@ -981,6 +995,7 @@ export class RoomEngine extends EventEmitter { } else if (placement === 'top' || placement === 'floor') { arrowMesh.scaling = new BABYLON.Vector3(1, -1, 1); } + this.sr.updateMesh(arrowMesh); let stickyOtherObject: string | null = null; let sticky = false; @@ -1031,7 +1046,8 @@ export class RoomEngine extends EventEmitter { this.gridPlane.position.y = 0; this.gridPlane.position.x = 0; } - this.gridPlane.isVisible = true; + //this.gridPlane.isVisible = true; + this.gridPlane.scaling = new BABYLON.Vector3(1, 1, 1); } } } else if (placement === 'bottom' || placement === 'ceiling') { @@ -1053,7 +1069,8 @@ export class RoomEngine extends EventEmitter { this.gridPlane.position = new BABYLON.Vector3(grabbing.mesh.position.x, grabbing.mesh.position.y - cm(0.1), grabbing.mesh.position.z); this.gridPlane.position.x = 0; this.gridPlane.position.z = 0; - this.gridPlane.isVisible = true; + //this.gridPlane.isVisible = true; + this.gridPlane.scaling = new BABYLON.Vector3(1, 1, 1); } } } else { // top or floor @@ -1075,7 +1092,8 @@ export class RoomEngine extends EventEmitter { this.gridPlane.position = new BABYLON.Vector3(grabbing.mesh.position.x, grabbing.mesh.position.y + cm(0.1), grabbing.mesh.position.z); this.gridPlane.position.x = 0; this.gridPlane.position.z = 0; - this.gridPlane.isVisible = true; + //this.gridPlane.isVisible = true; + this.gridPlane.scaling = new BABYLON.Vector3(1, 1, 1); } } } @@ -1093,10 +1111,14 @@ export class RoomEngine extends EventEmitter { grabbing.mesh.position = newPos; grabbing.mesh.rotation = newRotation; + + this.sr.updateMesh(grabbing.mesh.getChildMeshes()); } if (!sticky) { - this.gridPlane.isVisible = false; + //this.gridPlane.isVisible = false; + this.gridPlane.scaling = new BABYLON.Vector3(0, 0, 0); + //for (const mesh of grabbing.ghost.getChildMeshes()) { //if (mesh.material instanceof BABYLON.MultiMaterial) { // for (const subMat of mesh.material.subMaterials) { @@ -1110,6 +1132,8 @@ export class RoomEngine extends EventEmitter { //} } + this.sr.updateMesh(this.gridPlane); + //const pos = new BABYLON.Vector3(this.camera.position.x, this.camera.position.y, this.camera.position.z); //const _dir = newPos.subtract(pos).normalize(); //for (let i = 0; i < grabbing.distance; i++) { @@ -1241,6 +1265,7 @@ export class RoomEngine extends EventEmitter { sticky = info.sticky; }, onCancel: () => { + this.sr.disableSnapshotRendering(); selectedObject.position = initialPosition.clone(); selectedObject.rotation = initialRotation.clone(); @@ -1255,6 +1280,7 @@ export class RoomEngine extends EventEmitter { } }; removeStickyParentRecursively(selectedObject); + this.sr.enableSnapshotRendering(); }, onDone: () => { // todo: sticky状態などを引数でもらうようにしたい this.putParticleSystem.emitter = selectedObject.position.clone(); @@ -1265,6 +1291,8 @@ export class RoomEngine extends EventEmitter { playbackRate: 1, }); + this.sr.disableSnapshotRendering(); + // put animation const animTarget = new BABYLON.Animation( '', @@ -1284,9 +1312,11 @@ export class RoomEngine extends EventEmitter { selectedObject.animations.push(animTarget); const animating = Promise.withResolvers(); this.scene.beginAnimation(selectedObject, 0, 60, false, 3, () => { animating.resolve(); }); + this.sr.enableSnapshotRendering(); // TODO: アニメーションの完了まで親子関係の解除を遅延するため、一瞬「grabbingが終わっているのに親子関係が解除されていない」状態が発生する。その間に新たにgrabbingを開始するなどの別の操作が発生すると不具合のもとになるのでそれを防止する仕組みを作る animating.promise.then(() => { + this.sr.disableSnapshotRendering(); // 親から先に外していく const removeStickyParentRecursively = (mesh: BABYLON.Mesh) => { const stickyObjectIds = Array.from(this.roomState.installedObjects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id); @@ -1303,6 +1333,7 @@ export class RoomEngine extends EventEmitter { } }; removeStickyParentRecursively(selectedObject); + this.sr.enableSnapshotRendering(); const pos = selectedObject.position.clone(); const rotation = selectedObject.rotation.clone(); @@ -1315,6 +1346,8 @@ export class RoomEngine extends EventEmitter { }, }; + this.gridPlane.isVisible = true; + this.timer.setInterval(() => { this.handleGrabbing(); }, 10); @@ -1481,6 +1514,8 @@ export class RoomEngine extends EventEmitter { playbackRate: 1, }); + this.sr.disableSnapshotRendering(); + // put animation const animTarget = new BABYLON.Animation( '', @@ -1500,6 +1535,8 @@ export class RoomEngine extends EventEmitter { root.animations.push(animTarget); this.scene.beginAnimation(root, 0, 60, false, 3); + this.sr.enableSnapshotRendering(); + this.roomState.installedObjects.push({ id, type, @@ -1513,6 +1550,8 @@ export class RoomEngine extends EventEmitter { }, }; + this.gridPlane.isVisible = true; + this.timer.setInterval(() => { this.handleGrabbing(); }, 10); @@ -1530,13 +1569,14 @@ export class RoomEngine extends EventEmitter { entity.instance.resetTemporaryState?.(); } - this.sr.disableSnapshotRendering(); //if (this.gl != null) { // this.gl.isEnabled = false; // 重いので切る //} if (this.gridPlane.material == null) { import('@babylonjs/materials').then(m => { + this.sr.disableSnapshotRendering(); + this.gridMaterial = new m.GridMaterial('grid', this.scene); this.gridMaterial.lineColor = new BABYLON.Color3(0.5, 0.5, 0.5); this.gridMaterial.mainColor = new BABYLON.Color3(0, 0, 0); @@ -1548,6 +1588,8 @@ export class RoomEngine extends EventEmitter { this.gridPlane.material = this.gridMaterial; this.gridPlane.setEnabled(true); + + this.sr.enableSnapshotRendering(); }); } } @@ -1558,7 +1600,6 @@ export class RoomEngine extends EventEmitter { await this.bake(); - this.sr.enableSnapshotRendering(); //if (this.gl != null) { // this.gl.isEnabled = true; //} diff --git a/packages/frontend/src/world/room/object.ts b/packages/frontend/src/world/room/object.ts index eb9f320cd0..25fba4ecc2 100644 --- a/packages/frontend/src/world/room/object.ts +++ b/packages/frontend/src/world/room/object.ts @@ -113,7 +113,13 @@ export type ObjectDef = { treatLoaderResult?: (loaderResult: BABYLON.AssetContainer) => void; createInstance: (args: { scene: BABYLON.Scene; - sr: BABYLON.SnapshotRenderingHelper; + // TODO: snapshot renderingの関心を隠蔽した方が綺麗かもしれない + // 例えばmaterialUpdatedというメソッドを用意して内部的にresetを呼ぶなど + sr: { + updateMesh: (meshes: BABYLON.Mesh[]) => void; + reset: () => void; + fixParticleSystem: (ps: BABYLON.ParticleSystem) => void; + }; lc: BABYLON.ClusteredLightContainer | null; root: BABYLON.Mesh; options: Readonly>; diff --git a/packages/frontend/src/world/room/objects/laptopPc.ts b/packages/frontend/src/world/room/objects/laptopPc.ts index dd6e058406..204145ebc7 100644 --- a/packages/frontend/src/world/room/objects/laptopPc.ts +++ b/packages/frontend/src/world/room/objects/laptopPc.ts @@ -57,7 +57,7 @@ export const laptopPc = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, - createInstance: async ({ lc, scene, options, model, graphicsQuality }) => { + createInstance: async ({ lc, sr, scene, options, model, graphicsQuality }) => { const matrix = model.root.getWorldMatrix(true); const scale = new BABYLON.Vector3(); matrix.decompose(scale); @@ -131,11 +131,13 @@ export const laptopPc = defineObject({ const applyBodyColor = () => { const [r, g, b] = options.bodyColor; bodyMaterial.albedoColor = new BABYLON.Color3(r, g, b); + sr.reset(); }; const applyBezelColor = () => { const [r, g, b] = options.bezelColor; bezelMaterial.albedoColor = new BABYLON.Color3(r, g, b); + sr.reset(); }; applyBodyColor(); @@ -151,6 +153,7 @@ export const laptopPc = defineObject({ light.intensity = (2 * options.screenBrightness) * WORLD_SCALE * WORLD_SCALE; } model.updated(); + sr.updateMesh(hutaNode.getChildMeshes()); }; applyOpenAngle(); diff --git a/packages/frontend/src/world/room/previewEngine.ts b/packages/frontend/src/world/room/previewEngine.ts index 0be68db42d..f187c6ba6f 100644 --- a/packages/frontend/src/world/room/previewEngine.ts +++ b/packages/frontend/src/world/room/previewEngine.ts @@ -332,7 +332,16 @@ export class RoomObjectPreviewEngine { const objectInstance = await def.createInstance({ scene: this.scene, - sr: this.sr, + sr: { + updateMesh: (mesh) => { + this.sr.updateMesh(mesh); + }, + reset: () => { + this.sr.disableSnapshotRendering(); + this.sr.enableSnapshotRendering(); + }, + fixParticleSystem: (ps) => this.sr.fixParticleSystem(ps), + }, root, options: args.options, model,