Fix handle rotation to properly match mesh orientation
Handle meshes now correctly rotate to match the target mesh's world-space orientation instead of appearing axis-aligned. Root Cause: - Handle positions from HandleGeometry are calculated in world space - Setting mesh.position treats values as local space - This created coordinate system mismatch when rotation was also set - Result: rotation appeared to have no effect Solution: - Extract rotation from mesh world matrix using quaternion decomposition - Set rotation FIRST (before position) - Use setAbsolutePosition() for world-space positioning - This ensures rotation and position work correctly together Changes: - Import Quaternion from @babylonjs/core - Update createHandleMeshes(): decompose world matrix, set rotation, then use setAbsolutePosition() - Rename updateHandlePositions() to updateHandleTransforms() - Update updateHandleTransforms(): same rotation-then-position approach - Add null check for _targetMesh in updateHandleTransforms() Technical Details: - computeWorldMatrix(true) gets complete transform including parent - decompose() extracts pure rotation as quaternion (avoids gimbal lock) - setAbsolutePosition() correctly handles world-space coords with rotation - Order matters: rotation before position for correct transformation Result: ✅ Handle box shapes visually tilt/rotate with mesh ✅ Handles remain correctly positioned on OBB ✅ Both wireframe and individual handles rotate together Files modified: - ResizeGizmoVisuals.ts: Handle rotation implementation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5fbf2b87c1
commit
af52d5992c
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "immersive",
|
"name": "immersive",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.8-22",
|
"version": "0.0.8-23",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@ -12,7 +12,8 @@ import {
|
|||||||
Color3,
|
Color3,
|
||||||
UtilityLayerRenderer,
|
UtilityLayerRenderer,
|
||||||
LinesMesh,
|
LinesMesh,
|
||||||
Vector3
|
Vector3,
|
||||||
|
Quaternion
|
||||||
} from "@babylonjs/core";
|
} from "@babylonjs/core";
|
||||||
import { HandlePosition, HandleType } from "./types";
|
import { HandlePosition, HandleType } from "./types";
|
||||||
import { ResizeGizmoConfigManager } from "./ResizeGizmoConfig";
|
import { ResizeGizmoConfigManager } from "./ResizeGizmoConfig";
|
||||||
@ -90,7 +91,7 @@ export class ResizeGizmoVisuals {
|
|||||||
|
|
||||||
// Update visuals
|
// Update visuals
|
||||||
this.updateBoundingBox();
|
this.updateBoundingBox();
|
||||||
this.updateHandlePositions();
|
this.updateHandleTransforms();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,7 +241,15 @@ export class ResizeGizmoVisuals {
|
|||||||
this._utilityLayer.utilityLayerScene
|
this._utilityLayer.utilityLayerScene
|
||||||
);
|
);
|
||||||
|
|
||||||
mesh.position = handle.position.clone();
|
// Extract and set rotation first (from world matrix)
|
||||||
|
const worldMatrix = this._targetMesh.computeWorldMatrix(true);
|
||||||
|
const rotation = new Quaternion();
|
||||||
|
worldMatrix.decompose(undefined, rotation, undefined);
|
||||||
|
mesh.rotationQuaternion = rotation;
|
||||||
|
|
||||||
|
// Set world-space position (works correctly with rotation)
|
||||||
|
mesh.setAbsolutePosition(handle.position);
|
||||||
|
|
||||||
mesh.isPickable = true;
|
mesh.isPickable = true;
|
||||||
|
|
||||||
// Create material
|
// Create material
|
||||||
@ -277,13 +286,24 @@ export class ResizeGizmoVisuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update handle positions
|
* Update handle transforms (position and rotation)
|
||||||
*/
|
*/
|
||||||
private updateHandlePositions(): void {
|
private updateHandleTransforms(): void {
|
||||||
|
if (!this._targetMesh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const handle of this._handles) {
|
for (const handle of this._handles) {
|
||||||
const mesh = this._handleMeshes.get(handle.id);
|
const mesh = this._handleMeshes.get(handle.id);
|
||||||
if (mesh) {
|
if (mesh) {
|
||||||
mesh.position = handle.position.clone();
|
// Update rotation to match target mesh (from world matrix)
|
||||||
|
const worldMatrix = this._targetMesh.computeWorldMatrix(true);
|
||||||
|
const rotation = new Quaternion();
|
||||||
|
worldMatrix.decompose(undefined, rotation, undefined);
|
||||||
|
mesh.rotationQuaternion = rotation;
|
||||||
|
|
||||||
|
// Set world-space position (works correctly with rotation)
|
||||||
|
mesh.setAbsolutePosition(handle.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user