fix(web): correct transform rotation and lay-flat sign

Conjugate the rotation by the Z-reflection (negate rotX/rotY, keep rotZ) and use -90 degrees about X to lay extruded meshes flat so faces point up.
This commit is contained in:
2026-08-08 20:19:13 +08:00
parent dcba0ac4bd
commit a6e9d2dd07
+11 -7
View File
@@ -7,8 +7,10 @@ import type { TTSObject } from '@tts/shared';
* *
* TTS is left-handed with Y up and +Z toward the player; three.js is * TTS is left-handed with Y up and +Z toward the player; three.js is
* right-handed with Y up. Converting by reflecting the Z axis (negating Z in * right-handed with Y up. Converting by reflecting the Z axis (negating Z in
* position and every rotation angle) maps a TTS placement onto the three.js * position) maps a TTS placement onto the three.js scene while preserving the
* scene while preserving the physical layout. * physical layout. The rotation is conjugated by the reflection: `R_three =
* M·R_tts·M` with `M = diag(1,1,-1)`, which negates `rotX`/`rotY` but leaves
* `rotZ` unchanged.
* *
* Our viewer meshes are authored for inspection (standing up, extruded along * Our viewer meshes are authored for inspection (standing up, extruded along
* Z, sized in arbitrary units), so each class also gets a base-size correction * Z, sized in arbitrary units), so each class also gets a base-size correction
@@ -43,11 +45,12 @@ const CORRECTION: Record<string, number> = {
/** /**
* Rotation that lays an extruded mesh flat. The tile/token/card meshes extrude * Rotation that lays an extruded mesh flat. The tile/token/card meshes extrude
* along +Z with their face in the XY plane (standing up); rotating +90° about * along +Z with their face in the XY plane (standing up); rotating 90° about
* X puts the face up (+Y), matching how objects lie on a TTS table. Custom * X maps the +Z face normal to +Y, so the face points up like an object lying
* models are authored standing up already, so they need no correction. * on a TTS table. Custom models are authored standing up already, so they need
* no correction.
*/ */
const LAY_FLAT: [number, number, number] = [Math.PI / 2, 0, 0]; const LAY_FLAT: [number, number, number] = [-Math.PI / 2, 0, 0];
const FLAT_CLASSES = new Set([ const FLAT_CLASSES = new Set([
'Card', 'Card',
@@ -78,7 +81,8 @@ export function objectPlacement(object: TTSObject): ObjectPlacement | null {
const corr = CORRECTION[object.Name] ?? 1; const corr = CORRECTION[object.Name] ?? 1;
return { return {
position: [t.posX, t.posY, -t.posZ], position: [t.posX, t.posY, -t.posZ],
rotation: [-rad(t.rotX), -rad(t.rotY), -rad(t.rotZ)], // Reflection conjugates the rotation: Rx/Ry negate, Rz keeps its sign.
rotation: [-rad(t.rotX), -rad(t.rotY), rad(t.rotZ)],
scale: [t.scaleX * corr, t.scaleY * corr, t.scaleZ * corr], scale: [t.scaleX * corr, t.scaleY * corr, t.scaleZ * corr],
layFlat: FLAT_CLASSES.has(object.Name) ? LAY_FLAT : [0, 0, 0], layFlat: FLAT_CLASSES.has(object.Name) ? LAY_FLAT : [0, 0, 0],
}; };