t2-mapper/src/scene/coordinates.ts

115 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Matrix4, Quaternion } from "three";
import type { MatrixF, Vec3 } from "./types";
/**
* Convert a Torque Vec3 (X-right, Y-forward, Z-up) to Three.js (X-right, Y-up, Z-backward).
* Swizzle: three.x = torque.y, three.y = torque.z, three.z = torque.x
*
* Note: this is the same swizzle used by getPosition() in mission.ts.
*/
export function torqueToThree(v: Vec3): [number, number, number] {
return [v.y, v.z, v.x];
}
/** Convert a Torque scale Vec3 to Three.js axis order. */
export function torqueScaleToThree(v: Vec3): [number, number, number] {
return [v.y, v.z, v.x];
}
/**
* Convert a Torque MatrixF to a Three.js Quaternion.
*
* Torque MatrixF layout (row-major, idx = row*4 + col):
* [0] [1] [2] [3] m00 m01 m02 tx
* [4] [5] [6] [7] = m10 m11 m12 ty
* [8] [9] [10] [11] m20 m21 m22 tz
* [12] [13] [14] [15] 0 0 0 1
*
* Three.js Matrix4 is column-major, with elements in the same index layout
* but interpreted differently. We extract the 3×3 rotation, apply the
* Torque→Three.js coordinate transform, then decompose to quaternion.
*/
export function matrixFToQuaternion(m: MatrixF): Quaternion {
const e = m.elements;
// Extract the Torque 3×3 rotation columns (row-major storage)
// Column 0: e[0], e[1], e[2]
// Column 1: e[4], e[5], e[6]
// Column 2: e[8], e[9], e[10]
// Apply Torque→Three.js coordinate transform to the rotation matrix.
// Torque (X,Y,Z) → Three.js (Y,Z,X) means:
// Three.js row i, col j = Torque row swizzle[i], col swizzle[j]
// where swizzle maps Three axis → Torque axis: X→Y(1), Y→Z(2), Z→X(0)
//
// Build a Three.js column-major Matrix4 from the transformed rotation.
const mat4 = new Matrix4();
// Three.js Matrix4.elements is column-major:
// [m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44]
const t = mat4.elements;
// Torque col 0 (X-axis) maps to Three.js col 2 (Z-axis after swizzle)
// Torque col 1 (Y-axis) maps to Three.js col 0 (X-axis after swizzle)
// Torque col 2 (Z-axis) maps to Three.js col 1 (Y-axis after swizzle)
//
// Within each column, rows are also swizzled:
// Torque row 0 (X) → Three.js row 2 (Z)
// Torque row 1 (Y) → Three.js row 0 (X)
// Torque row 2 (Z) → Three.js row 1 (Y)
// Three.js column 0 ← Torque column 1 (Y→X), rows swizzled
t[0] = e[5]; // T_Y_Y → Three X_X
t[1] = e[6]; // T_Z_Y → Three Y_X
t[2] = e[4]; // T_X_Y → Three Z_X
t[3] = 0;
// Three.js column 1 ← Torque column 2 (Z→Y), rows swizzled
t[4] = e[9]; // T_Y_Z → Three X_Y
t[5] = e[10]; // T_Z_Z → Three Y_Y
t[6] = e[8]; // T_X_Z → Three Z_Y
t[7] = 0;
// Three.js column 2 ← Torque column 0 (X→Z), rows swizzled
t[8] = e[1]; // T_Y_X → Three X_Z
t[9] = e[2]; // T_Z_X → Three Y_Z
t[10] = e[0]; // T_X_X → Three Z_Z
t[11] = 0;
// Translation column (not used for quaternion, but set for completeness)
t[12] = 0;
t[13] = 0;
t[14] = 0;
t[15] = 1;
const q = new Quaternion();
q.setFromRotationMatrix(mat4);
// Torque uses row-vector convention (v * M), Three.js uses column-vector (M * v).
// The extracted rotation is the transpose, so conjugate to invert.
q.conjugate();
return q;
}
/**
* Convert a Torque axis-angle rotation string ("ax ay az angleDeg") to a Quaternion.
* This is the format used in .mis files. The axis is in Torque coordinates.
*/
export function torqueAxisAngleToQuaternion(
ax: number,
ay: number,
az: number,
angleDeg: number,
): Quaternion {
// Swizzle axis: Torque (X,Y,Z) → Three.js (Y,Z,X)
const threeAx = ay;
const threeAy = az;
const threeAz = ax;
const len = Math.sqrt(
threeAx * threeAx + threeAy * threeAy + threeAz * threeAz,
);
if (len < 1e-8) return new Quaternion();
const angleRad = -angleDeg * (Math.PI / 180);
return new Quaternion().setFromAxisAngle(
{ x: threeAx / len, y: threeAy / len, z: threeAz / len } as any,
angleRad,
);
}