From efaf7bdd95e6767c84bd3f904d90af592f30a55d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:28:27 +0900 Subject: [PATCH] side grid snap --- packages/frontend/src/world/room/engine.ts | 20 +++++++++++++------- packages/frontend/src/world/utility.ts | 13 +++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index 56a533e217..2bdd3d3e76 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -14,7 +14,7 @@ import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic'; import { BoundingBoxRenderer } from '@babylonjs/core/Rendering/boundingBoxRenderer'; import { GridMaterial } from '@babylonjs/materials'; import { EventEmitter } from 'eventemitter3'; -import { TIME_MAP, scaleMorph, camelToKebab, cm, WORLD_SCALE, getMeshesBoundingBox, Timer } from '../utility.js'; +import { TIME_MAP, scaleMorph, camelToKebab, cm, WORLD_SCALE, getMeshesBoundingBox, Timer, getYRotationDirection } from '../utility.js'; import { getObjectDef } from './object-defs.js'; import { findMaterial, ModelManager, SYSTEM_HEYA_MESH_NAMES, SYSTEM_MESH_NAMES } from './utility.js'; import { SimpleHeyaManager } from './heya.js'; @@ -605,18 +605,21 @@ export class RoomEngine extends EventEmitter { const ray = new BABYLON.Ray(this.camera.position, dir, cm(1000)); const hit = this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_WALL__') || m.name.includes('__SIDE__'))); if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) { - newPos.x = hit.pickedPoint.x; - newPos.z = hit.pickedPoint.z; const pickedMeshNormal = hit.getNormal(true, true); const targetRotationY = Math.atan2(pickedMeshNormal.x, pickedMeshNormal.z); newRotation.y = targetRotationY; + if (getYRotationDirection(targetRotationY) === '+x' || getYRotationDirection(targetRotationY) === '-x') { + newPos.x = hit.pickedPoint.x; + } else { + newPos.z = hit.pickedPoint.z; + } sticky = hit.pickedMesh.metadata?.objectId ?? null; if (this.gridSnapping.enabled) { this.gridPlane.rotationQuaternion = null; this.gridPlane.rotation.x = Math.PI; this.gridPlane.rotation.y = targetRotationY; - this.gridPlane.position = new BABYLON.Vector3(hit.pickedPoint.x, 0, hit.pickedPoint.z).addInPlace(pickedMeshNormal.scale(cm(0.1))); + this.gridPlane.position = new BABYLON.Vector3(newPos.x, newPos.y, newPos.z).addInPlace(pickedMeshNormal.scale(cm(0.1))); this.gridPlane.isVisible = true; } } @@ -625,17 +628,20 @@ export class RoomEngine extends EventEmitter { const ray = new BABYLON.Ray(this.camera.position, dir, cm(1000)); const hit = this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_WALL__'))); if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) { - newPos.x = hit.pickedPoint.x; - newPos.z = hit.pickedPoint.z; const pickedMeshNormal = hit.getNormal(true, true); const targetRotationY = Math.atan2(pickedMeshNormal.x, pickedMeshNormal.z); newRotation.y = targetRotationY; + if (getYRotationDirection(targetRotationY) === '+x' || getYRotationDirection(targetRotationY) === '-x') { + newPos.x = hit.pickedPoint.x; + } else { + newPos.z = hit.pickedPoint.z; + } if (this.gridSnapping.enabled) { this.gridPlane.rotationQuaternion = null; this.gridPlane.rotation.x = Math.PI; this.gridPlane.rotation.y = targetRotationY; - this.gridPlane.position = new BABYLON.Vector3(hit.pickedPoint.x, 0, hit.pickedPoint.z).addInPlace(pickedMeshNormal.scale(cm(0.1))); + this.gridPlane.position = new BABYLON.Vector3(newPos.x, newPos.y, newPos.z).addInPlace(pickedMeshNormal.scale(cm(0.1))); this.gridPlane.isVisible = true; } } diff --git a/packages/frontend/src/world/utility.ts b/packages/frontend/src/world/utility.ts index 5ec953dacc..24a0156791 100644 --- a/packages/frontend/src/world/utility.ts +++ b/packages/frontend/src/world/utility.ts @@ -576,3 +576,16 @@ export class Timer { this.intervalIds = []; } } + +export function getYRotationDirection(rotationY: number): '+x' | '+z' | '-x' | '-z' { + const angle = rotationY % (2 * Math.PI); + if ((angle >= 0 && angle < Math.PI / 4) || (angle >= 7 * Math.PI / 4 && angle < 2 * Math.PI)) { + return '-z'; + } else if (angle >= Math.PI / 4 && angle < 3 * Math.PI / 4) { + return '-x'; + } else if (angle >= 3 * Math.PI / 4 && angle < 5 * Math.PI / 4) { + return '+z'; + } else { + return '+x'; + } +}