reze-engine 0.27.4 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,9 +29,9 @@ See [Physics](#physics) and [Rendering](#rendering) for the internals.
29
29
  - [Reze Design](https://reze.design) (web-native scene composer & shader-graph styling)
30
30
  - [Reze Studio](https://reze.studio) (MMD animation editor)
31
31
  - [MiKaPo](https://mikapo.vercel.app) (motion capture)
32
+ - [Reze Rig](https://reze-rig.vercel.app) (FBX→VMD retarget)
32
33
  - [Popo](https://popo.love) (LLM-generated poses)
33
34
  - [MPL](https://mmd-mpl.vercel.app) (motion language)
34
- - [Mixamo-MMD](https://mixamo-mmd.vercel.app) (FBX→VMD retarget)
35
35
 
36
36
  ## Quick start
37
37
 
@@ -252,14 +252,14 @@ const { ok, wgsl, diagnostics } = compileGraph(HAIR_GRAPH, {
252
252
 
253
253
  The **built-in name hints**, checked top-to-bottom (first match wins), with the graph and pass-integration each category carries:
254
254
 
255
- | Category | Graph · render-class / alpha | Matches a name containing (case-insensitive substring) |
256
- | -------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
257
- | `stockings` | `STOCKINGS_GRAPH` · `hashed` alpha | 靴下 · ソックス · タイツ · ニーソ · 袜 · stocking · socks · tights |
258
- | `eye` | `EYE_GRAPH` · `eye` | 白目 · 目影 · 二重 · 睫 · まつげ · まゆ · 眉 · 目 · 瞳 · 眼 · eye · iris · pupil · lash · brow |
259
- | `face` | `FACE_GRAPH` | 顔 · 颜 · 脸 · かお · face · 舌 · tongue · 牙 · 牙齿 · 歯 · teeth · tooth · 口腔 · 口内 · mouth · 嘴 · 歯茎 · gums |
260
- | `hair` | `HAIR_GRAPH` · `hair` | 前髪 · 後髪 · 髪 · 髮 · 头发 · 頭髪 · もみあげ · アホ毛 · ヘア · hair · ahoge · bang |
261
- | `body` | `BODY_GRAPH` | 肌 · 皮肤 · skin |
262
- | `metal` | `METAL_GRAPH` | 金属 · メタル · metal · earring · 耳环 · 耳環 |
255
+ | Category | Graph · render-class / alpha | Matches a name containing (case-insensitive substring) |
256
+ | -------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
257
+ | `stockings` | `STOCKINGS_GRAPH` · `hashed` alpha | 靴下 · ソックス · タイツ · ニーソ · 袜 · stocking · socks · tights |
258
+ | `eye` | `EYE_GRAPH` · `eye` | 白目 · 目影 · 二重 · 睫 · まつげ · まゆ · 眉 · 目 · 瞳 · 眼 · eye · iris · pupil · lash · brow |
259
+ | `face` | `FACE_GRAPH` | 顔 · 颜 · 脸 · かお · face · 舌 · tongue · 牙 · 牙齿 · 歯 · teeth · tooth · 口腔 · 口内 · mouth · 嘴 · 歯茎 · gums |
260
+ | `hair` | `HAIR_GRAPH` · `hair` | 前髪 · 後髪 · 髪 · 髮 · 头发 · 頭髪 · もみあげ · アホ毛 · ヘア · hair · ahoge · bang |
261
+ | `body` | `BODY_GRAPH` | 肌 · 皮肤 · skin |
262
+ | `metal` | `METAL_GRAPH` | 金属 · メタル · metal · earring · 耳环 · 耳環 |
263
263
  | `cloth_smooth` | `CLOTH_SMOOTH_GRAPH` | 服 · 衣 · 裙 · 裤 · スカート · ワンピ · リボン · 袖 · 靴 · 鞋 · 帽 · 体 · 飾 · 饰 · 尾 · 套 · 腿 · skirt · dress · ribbon · sleeve · shoes · shirt · short · boot · hat · cloth · accessor · trigger |
264
264
 
265
265
  `cloth_rough` and `default` have **no** name hints — a material reaches them only via an explicit `overrides` entry. The group `id` is the category name, so re-running `autoStyleGroups` is idempotent; its promise resolves after every graph compiles, so `getStyleGroups(model)` is ready the moment it resolves — seed your own store from it, then edit with `applyStyleGroups`.
@@ -9,7 +9,7 @@ export declare class IKSolverSystem {
9
9
  private static solveChain;
10
10
  private static limitAngle;
11
11
  private static getDistance;
12
- private static readonly EULER_AXES;
12
+ private static readonly EULER_ORDER_NAMES;
13
13
  private static extractEulerAnglesInto;
14
14
  private static limitEulerAnglesInto;
15
15
  private static reconstructQuatFromEulerInto;
@@ -1 +1 @@
1
- {"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG7D,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAwF/E,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD,MAAM,CAAC,KAAK,CACV,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,IAAI,EAAE,EACtB,iBAAiB,EAAE,IAAI,EAAE,EACzB,aAAa,EAAE,IAAI,EAAE,EACrB,WAAW,EAAE,WAAW,EAAE,EAC1B,iBAAiB,CAAC,EAAE,mBAAmB,GACtC,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IAqFtB,OAAO,CAAC,MAAM,CAAC,UAAU;IA4HzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAW1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAOjC;IAED,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAsCrC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAMnC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAyB3C,OAAO,CAAC,MAAM,CAAC,gCAAgC;IAkB/C,OAAO,CAAC,MAAM,CAAC,iBAAiB;CA2CjC"}
1
+ {"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAmB,MAAM,QAAQ,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG7D,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAuF/E,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD,MAAM,CAAC,KAAK,CACV,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,IAAI,EAAE,EACtB,iBAAiB,EAAE,IAAI,EAAE,EACzB,aAAa,EAAE,IAAI,EAAE,EACrB,WAAW,EAAE,WAAW,EAAE,EAC1B,iBAAiB,CAAC,EAAE,mBAAmB,GACtC,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IAqFtB,OAAO,CAAC,MAAM,CAAC,UAAU;IA4HzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAU1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAA+C;IAExF,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAsCrC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAMnC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAK3C,OAAO,CAAC,MAAM,CAAC,gCAAgC;IAkB/C,OAAO,CAAC,MAAM,CAAC,iBAAiB;CA2CjC"}
package/dist/ik-solver.js CHANGED
@@ -71,7 +71,6 @@ const _ikVec = [
71
71
  ];
72
72
  const _ikQuat = [
73
73
  new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1),
74
- new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1),
75
74
  ];
76
75
  const _ikMat = [
77
76
  new Float32Array(16), new Float32Array(16), new Float32Array(16), new Float32Array(16),
@@ -151,7 +150,7 @@ export class IKSolverSystem {
151
150
  // _ikVec[0]=chainPos, [1]=ikPos, [2]=targetPos, [3]=chainTargetVec, [4]=chainIkVec,
152
151
  // [5]=rotAxis, [6]=finalAxis, [7]=eulerTmp, [8]=limitedEuler
153
152
  // _ikMat[0]=parentRot, [1]=invParentRot, [2]=quatToMatTmp
154
- // _ikQuat[0]=ikRotation, [1]=combinedRot, [2]=localRotConj, [3..5]=axisAngleTmp
153
+ // _ikQuat[0]=ikRotation, [1]=combinedRot, [2]=localRotConj
155
154
  const chainPos = Vec3.setFromMat4Translation(worldMatrices[chainBoneIndex].values, _ikVec[0]);
156
155
  const ikPos = Vec3.setFromMat4Translation(worldMatrices[ikBoneIndex].values, _ikVec[1]);
157
156
  const targetPos = Vec3.setFromMat4Translation(worldMatrices[targetBoneIndex].values, _ikVec[2]);
@@ -317,23 +316,7 @@ export class IKSolverSystem {
317
316
  out.z = this.limitAngle(euler.z, min.z, max.z, useAxis);
318
317
  }
319
318
  static reconstructQuatFromEulerInto(euler, order, out) {
320
- const axes = this.EULER_AXES[order];
321
- const a1 = axes[0], a2 = axes[1], a3 = axes[2];
322
- const ang1 = order === InternalEulerRotationOrder.YXZ ? euler.y
323
- : order === InternalEulerRotationOrder.ZYX ? euler.z
324
- : euler.x;
325
- const ang2 = order === InternalEulerRotationOrder.YXZ ? euler.x
326
- : order === InternalEulerRotationOrder.ZYX ? euler.y
327
- : euler.z;
328
- const ang3 = order === InternalEulerRotationOrder.YXZ ? euler.z
329
- : order === InternalEulerRotationOrder.ZYX ? euler.x
330
- : euler.y;
331
- // result = axisAngle(a1, ang1); then *= axisAngle(a2, ang2); then *= axisAngle(a3, ang3)
332
- Quat.fromAxisAngleInto(a1[0], a1[1], a1[2], ang1, out);
333
- Quat.fromAxisAngleInto(a2[0], a2[1], a2[2], ang2, _ikQuat[3]);
334
- Quat.multiplyInto(out, _ikQuat[3], out);
335
- Quat.fromAxisAngleInto(a3[0], a3[1], a3[2], ang3, _ikQuat[3]);
336
- Quat.multiplyInto(out, _ikQuat[3], out);
319
+ Quat.fromEulerOrderInto(euler.x, euler.y, euler.z, this.EULER_ORDER_NAMES[order], out);
337
320
  }
338
321
  // Write parent's world rotation (translation stripped) into out Float32Array.
339
322
  static getParentWorldRotationMatrixInto(boneIndex, bones, worldMatrices, out) {
@@ -396,13 +379,5 @@ export class IKSolverSystem {
396
379
  }
397
380
  IKSolverSystem.EPSILON = 1.0e-8;
398
381
  IKSolverSystem.THRESHOLD = (88 * Math.PI) / 180;
399
- // Euler axis triples for each rotation order (indexed by order enum).
400
- // Reused to avoid allocations in reconstructQuatFromEulerInto.
401
- IKSolverSystem.EULER_AXES = [
402
- // YXZ: Y, X, Z
403
- [[0, 1, 0], [1, 0, 0], [0, 0, 1]],
404
- // ZYX: Z, Y, X
405
- [[0, 0, 1], [0, 1, 0], [1, 0, 0]],
406
- // XZY: X, Z, Y
407
- [[1, 0, 0], [0, 0, 1], [0, 1, 0]],
408
- ];
382
+ // Maps InternalEulerRotationOrder to the shared math-layer order names.
383
+ IKSolverSystem.EULER_ORDER_NAMES = ["YXZ", "ZYX", "XZY"];
package/dist/index.d.ts CHANGED
@@ -15,9 +15,9 @@ export { STOCKINGS_GRAPH } from "./graph/presets/stockings";
15
15
  export { EYE_GRAPH } from "./graph/presets/eye";
16
16
  export { FACE_GRAPH } from "./graph/presets/face";
17
17
  export { Model } from "./model";
18
- export { Vec3, Quat, Mat4 } from "./math";
18
+ export { Vec3, Quat, Mat4, easeInOut, type EulerOrder } from "./math";
19
19
  export type { AnimationClip, AnimationPlayOptions, AnimationProgress, BoneKeyframe, IkKeyframe, MorphKeyframe, BoneInterpolation, ControlPoint, } from "./animation";
20
- export { FPS } from "./animation";
20
+ export { FPS, bezierInterpolate, interpolateControlPoints, rawInterpolationToBoneInterpolation, } from "./animation";
21
21
  export { VMDLoader, type CameraKeyframe, type IkFrame } from "./vmd-loader";
22
22
  export { CameraAnimation, type CameraPose } from "./camera-animation";
23
23
  export { RezePhysics } from "./physics";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,GAC5B,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACvG,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,GACX,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7G,YAAY,EACV,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3E,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,GAC5B,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACvG,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,GACX,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7G,YAAY,EACV,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAA;AACrE,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,GAAG,EACH,iBAAiB,EACjB,wBAAwB,EACxB,mCAAmC,GACpC,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3E,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -13,8 +13,8 @@ export { STOCKINGS_GRAPH } from "./graph/presets/stockings";
13
13
  export { EYE_GRAPH } from "./graph/presets/eye";
14
14
  export { FACE_GRAPH } from "./graph/presets/face";
15
15
  export { Model } from "./model";
16
- export { Vec3, Quat, Mat4 } from "./math";
17
- export { FPS } from "./animation";
16
+ export { Vec3, Quat, Mat4, easeInOut } from "./math";
17
+ export { FPS, bezierInterpolate, interpolateControlPoints, rawInterpolationToBoneInterpolation, } from "./animation";
18
18
  export { VMDLoader } from "./vmd-loader";
19
19
  export { CameraAnimation } from "./camera-animation";
20
20
  export { RezePhysics } from "./physics";
package/dist/math.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export declare function easeInOut(t: number): number;
2
+ export type EulerOrder = "XYZ" | "XZY" | "YXZ" | "YZX" | "ZXY" | "ZYX";
2
3
  export declare class Vec3 {
3
4
  x: number;
4
5
  y: number;
@@ -18,6 +19,8 @@ export declare class Vec3 {
18
19
  static crossInto(a: Vec3, b: Vec3, out: Vec3): Vec3;
19
20
  static setFromMat4Translation(m: Float32Array, out: Vec3): Vec3;
20
21
  static transformMat4RotationInto(normal: Vec3, m: Float32Array, out: Vec3): Vec3;
22
+ static mirrorZInto(v: Vec3, out: Vec3): Vec3;
23
+ static mirrorZ(v: Vec3): Vec3;
21
24
  normalizeInPlace(): Vec3;
22
25
  }
23
26
  export declare class Quat {
@@ -43,6 +46,29 @@ export declare class Quat {
43
46
  setXYZW(x: number, y: number, z: number, w: number): Quat;
44
47
  setIdentity(): Quat;
45
48
  static fromEuler(rotX: number, rotY: number, rotZ: number): Quat;
49
+ static dot(a: Quat, b: Quat): number;
50
+ static angleTo(a: Quat, b: Quat): number;
51
+ static conjugateInto(q: Quat, out: Quat): Quat;
52
+ static nlerpInto(a: Quat, b: Quat, t: number, out: Quat): Quat;
53
+ static nlerp(a: Quat, b: Quat, t: number): Quat;
54
+ static rotateVecInto(q: Quat, v: Vec3, out: Vec3): Vec3;
55
+ static rotateVec(q: Quat, v: Vec3): Vec3;
56
+ static rotateVecInvInto(q: Quat, v: Vec3, out: Vec3): Vec3;
57
+ static rotateVecInv(q: Quat, v: Vec3): Vec3;
58
+ static fromUnitVectorsInto(from: Vec3, to: Vec3, out: Quat): Quat;
59
+ static fromUnitVectors(from: Vec3, to: Vec3): Quat;
60
+ static fromBasisInto(x: Vec3, y: Vec3, z: Vec3, out: Quat): Quat;
61
+ static fromBasis(x: Vec3, y: Vec3, z: Vec3): Quat;
62
+ static twistAroundAxisInto(q: Quat, a: Vec3, out: Quat): Quat;
63
+ static twistAroundAxis(q: Quat, a: Vec3): Quat;
64
+ static lookRotationInto(forward: Vec3, up: Vec3, out: Quat): Quat;
65
+ static lookRotation(forward: Vec3, up: Vec3): Quat;
66
+ static mirrorZInto(q: Quat, out: Quat): Quat;
67
+ static mirrorZ(q: Quat): Quat;
68
+ static fromEulerOrderInto(x: number, y: number, z: number, order: EulerOrder, out: Quat): Quat;
69
+ static fromEulerOrder(x: number, y: number, z: number, order: EulerOrder): Quat;
70
+ static toEulerOrderInto(q: Quat, order: EulerOrder, out: Vec3): Vec3;
71
+ static toEulerOrder(q: Quat, order: EulerOrder): Vec3;
46
72
  }
47
73
  export declare class Mat4 {
48
74
  values: Float32Array;
@@ -1 +1 @@
1
- {"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"AACA,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAID,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAEtE,qBAAa,IAAI;IACf,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;gBAEG,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAM3C,MAAM,CAAC,KAAK,IAAI,IAAI;IAIpB,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAItB,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAI3B,MAAM,IAAI,MAAM;IAKhB,SAAS,IAAI,IAAI;IAejB,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAQxB,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM;IAIxB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK3B,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAOtB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ7C,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAQtD,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAUnD,MAAM,CAAC,sBAAsB,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAS/D,MAAM,CAAC,yBAAyB,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAShF,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAO5C,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI;IAK7B,gBAAgB,IAAI,IAAI;CAGzB;AAED,qBAAa,IAAI;IACf,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;gBAEG,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAOtD,MAAM,CAAC,QAAQ,IAAI,IAAI;IAIvB,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAItB,KAAK,IAAI,IAAI;IAIb,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAY3B,SAAS,IAAI,IAAI;IAOjB,MAAM,IAAI,MAAM;IAKhB,SAAS,IAAI,IAAI;IAkBjB,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAiBrD,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IAK3C,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAStB,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAqC/C,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAWtD,MAAM,CAAC,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAc5F,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IA4B9D,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzD,WAAW,IAAI,IAAI;IAMnB,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAkBhE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,MAAM;IAKpC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,MAAM;IAMxC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAU9C,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAe9D,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAK/C,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAcvD,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI;IAKxC,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAY1D,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI;IAO3C,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAsBjE,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,IAAI;IAMlD,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAsBhE,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI;IAOjD,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAc7D,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI;IAM9C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAgCjE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,IAAI;IAMlD,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAQ5C,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI;IAM7B,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IA6B9F,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAO/E,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAwEpE,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;CAGtD;AAED,qBAAa,IAAI;IACf,MAAM,EAAE,YAAY,CAAA;gBAER,MAAM,EAAE,YAAY;IAIhC,MAAM,CAAC,QAAQ,IAAI,IAAI;IAOvB,MAAM,CAAC,eAAe,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IASvG,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAQhF,MAAM,CAAC,UAAU,CACf,GAAG,EAAE,YAAY,EACjB,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAClC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAClC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GACjC,IAAI;IAqBP,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,IAAI;IAOtD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IA0BhH,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAqB3B,MAAM,CAAC,cAAc,CACnB,CAAC,EAAE,YAAY,EACf,OAAO,EAAE,MAAM,EACf,CAAC,EAAE,YAAY,EACf,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,MAAM,GAChB,IAAI;IAiBP,KAAK,IAAI,IAAI;IAKb,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,GAAE,MAAU,GAAG,IAAI;IA0B5G,MAAM,CAAC,kBAAkB,CACvB,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAClC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAC9C,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAClC,GAAG,EAAE,YAAY,GAChB,IAAI;IAkBP,MAAM,CAAC,wBAAwB,CAC7B,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAClC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAC9C,GAAG,EAAE,YAAY,GAChB,IAAI;IASP,MAAM,CAAC,6BAA6B,CAClC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAClC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAC9C,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,YAAY,GAChB,IAAI;IAWP,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO;IAyC/D,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,IAAI;IAOnE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAmCjE,MAAM,CAAC,oBAAoB,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAG,IAAI;IASjE,WAAW,IAAI,IAAI;IAKnB,MAAM,IAAI,IAAI;IAKd,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI;IAwC5E,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IA6C7D,WAAW,IAAI,IAAI;IAqBnB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAY1D,OAAO,IAAI,IAAI;CA8DhB;AAWD,eAAO,MAAM,iBAAiB,EAAE,YAAY,EAO3C,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,IAAI,EAS7B,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,IAAI,EAK7B,CAAA"}
package/dist/math.js CHANGED
@@ -90,6 +90,16 @@ export class Vec3 {
90
90
  out.z = m[2] * nx + m[6] * ny + m[10] * nz;
91
91
  return out;
92
92
  }
93
+ // out = (x, y, -z): right-handed Y-up ↔ left-handed Y-up (involutive). Safe when out === v.
94
+ static mirrorZInto(v, out) {
95
+ out.x = v.x;
96
+ out.y = v.y;
97
+ out.z = -v.z;
98
+ return out;
99
+ }
100
+ static mirrorZ(v) {
101
+ return new Vec3(v.x, v.y, -v.z);
102
+ }
93
103
  // In-place normalize returning length squared info via Vec3. Alias for normalize() but explicit.
94
104
  normalizeInPlace() {
95
105
  return this.normalize();
@@ -286,6 +296,318 @@ export class Quat {
286
296
  const z = cy * cx * sz - sy * sx * cz;
287
297
  return new Quat(x, y, z, w).normalize();
288
298
  }
299
+ // 4D dot product. Negative means a and b are on opposite hemispheres (same rotation
300
+ // when |dot| ≈ 1 either way — quaternion double cover).
301
+ static dot(a, b) {
302
+ return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
303
+ }
304
+ // Rotation angle between a and b in radians, insensitive to double cover.
305
+ static angleTo(a, b) {
306
+ const d = Math.abs(a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w);
307
+ return 2 * Math.acos(Math.min(1, d));
308
+ }
309
+ // out = conjugate of q (inverse for unit quaternions), without mutating q. Safe when out === q.
310
+ static conjugateInto(q, out) {
311
+ out.x = -q.x;
312
+ out.y = -q.y;
313
+ out.z = -q.z;
314
+ out.w = q.w;
315
+ return out;
316
+ }
317
+ // out = normalized lerp from a to b, taking the shorter path (negates b when dot < 0).
318
+ // Cheaper than slerp; non-constant angular velocity. Safe when out === a or out === b.
319
+ static nlerpInto(a, b, t, out) {
320
+ const d = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
321
+ const s = d < 0 ? -1 : 1;
322
+ const x = a.x + (b.x * s - a.x) * t;
323
+ const y = a.y + (b.y * s - a.y) * t;
324
+ const z = a.z + (b.z * s - a.z) * t;
325
+ const w = a.w + (b.w * s - a.w) * t;
326
+ const invLen = 1 / Math.hypot(x, y, z, w);
327
+ out.x = x * invLen;
328
+ out.y = y * invLen;
329
+ out.z = z * invLen;
330
+ out.w = w * invLen;
331
+ return out;
332
+ }
333
+ static nlerp(a, b, t) {
334
+ return Quat.nlerpInto(a, b, t, new Quat(0, 0, 0, 1));
335
+ }
336
+ // out = v rotated by unit quaternion q (no matrix, no allocation). Safe when out === v.
337
+ static rotateVecInto(q, v, out) {
338
+ // v' = v + 2*qw*(qv × v) + 2*(qv × (qv × v))
339
+ const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
340
+ const vx = v.x, vy = v.y, vz = v.z;
341
+ // t = 2 * (qv × v)
342
+ const tx = 2 * (qy * vz - qz * vy);
343
+ const ty = 2 * (qz * vx - qx * vz);
344
+ const tz = 2 * (qx * vy - qy * vx);
345
+ out.x = vx + qw * tx + qy * tz - qz * ty;
346
+ out.y = vy + qw * ty + qz * tx - qx * tz;
347
+ out.z = vz + qw * tz + qx * ty - qy * tx;
348
+ return out;
349
+ }
350
+ static rotateVec(q, v) {
351
+ return Quat.rotateVecInto(q, v, new Vec3(0, 0, 0));
352
+ }
353
+ // out = v rotated by the inverse (conjugate) of unit quaternion q. Safe when out === v.
354
+ static rotateVecInvInto(q, v, out) {
355
+ const qx = -q.x, qy = -q.y, qz = -q.z, qw = q.w;
356
+ const vx = v.x, vy = v.y, vz = v.z;
357
+ const tx = 2 * (qy * vz - qz * vy);
358
+ const ty = 2 * (qz * vx - qx * vz);
359
+ const tz = 2 * (qx * vy - qy * vx);
360
+ out.x = vx + qw * tx + qy * tz - qz * ty;
361
+ out.y = vy + qw * ty + qz * tx - qx * tz;
362
+ out.z = vz + qw * tz + qx * ty - qy * tx;
363
+ return out;
364
+ }
365
+ static rotateVecInv(q, v) {
366
+ return Quat.rotateVecInvInto(q, v, new Vec3(0, 0, 0));
367
+ }
368
+ // out = shortest-arc rotation taking unit vector `from` to unit vector `to`.
369
+ // Matches Babylon's FromUnitVectorsToRef exactly, including the near-antiparallel
370
+ // branch (w = 1 + dot < 0.001 → 180° about a perpendicular picked the same way).
371
+ static fromUnitVectorsInto(from, to, out) {
372
+ const r = from.x * to.x + from.y * to.y + from.z * to.z + 1;
373
+ if (r < 0.001) {
374
+ if (Math.abs(from.x) > Math.abs(from.z)) {
375
+ out.setXYZW(-from.y, from.x, 0, 0);
376
+ }
377
+ else {
378
+ out.setXYZW(0, -from.z, from.y, 0);
379
+ }
380
+ }
381
+ else {
382
+ // q = (from × to, 1 + from·to)
383
+ out.setXYZW(from.y * to.z - from.z * to.y, from.z * to.x - from.x * to.z, from.x * to.y - from.y * to.x, r);
384
+ }
385
+ const invLen = 1 / Math.sqrt(out.x * out.x + out.y * out.y + out.z * out.z + out.w * out.w);
386
+ out.setXYZW(out.x * invLen, out.y * invLen, out.z * invLen, out.w * invLen);
387
+ return out;
388
+ }
389
+ static fromUnitVectors(from, to) {
390
+ return Quat.fromUnitVectorsInto(from, to, new Quat(0, 0, 0, 1));
391
+ }
392
+ // out = rotation taking the standard basis onto the orthonormal axes x, y, z
393
+ // (the columns of the column-major rotation matrix): rotateVec(out, (1,0,0)) = x, etc.
394
+ static fromBasisInto(x, y, z, out) {
395
+ // Shepperd's method on the 3x3 with columns x, y, z.
396
+ const m00 = x.x, m01 = x.y, m02 = x.z;
397
+ const m10 = y.x, m11 = y.y, m12 = y.z;
398
+ const m20 = z.x, m21 = z.y, m22 = z.z;
399
+ const trace = m00 + m11 + m22;
400
+ if (trace > 0) {
401
+ const s = 0.5 / Math.sqrt(trace + 1);
402
+ out.setXYZW((m12 - m21) * s, (m20 - m02) * s, (m01 - m10) * s, 0.25 / s);
403
+ }
404
+ else if (m00 > m11 && m00 > m22) {
405
+ const s = 2 * Math.sqrt(1 + m00 - m11 - m22);
406
+ out.setXYZW(0.25 * s, (m10 + m01) / s, (m20 + m02) / s, (m12 - m21) / s);
407
+ }
408
+ else if (m11 > m22) {
409
+ const s = 2 * Math.sqrt(1 + m11 - m00 - m22);
410
+ out.setXYZW((m10 + m01) / s, 0.25 * s, (m21 + m12) / s, (m20 - m02) / s);
411
+ }
412
+ else {
413
+ const s = 2 * Math.sqrt(1 + m22 - m00 - m11);
414
+ out.setXYZW((m20 + m02) / s, (m21 + m12) / s, 0.25 * s, (m01 - m10) / s);
415
+ }
416
+ return out;
417
+ }
418
+ static fromBasis(x, y, z) {
419
+ return Quat.fromBasisInto(x, y, z, new Quat(0, 0, 0, 1));
420
+ }
421
+ // out = twist component of q around unit axis `a`, so that q = swing · twist
422
+ // (swing = Quat.multiply(q, conjugate(twist))). Singular when q is ~180° about an
423
+ // axis perpendicular to `a` — returns identity there.
424
+ static twistAroundAxisInto(q, a, out) {
425
+ const d = q.x * a.x + q.y * a.y + q.z * a.z;
426
+ const px = a.x * d;
427
+ const py = a.y * d;
428
+ const pz = a.z * d;
429
+ const len = Math.sqrt(px * px + py * py + pz * pz + q.w * q.w);
430
+ if (len < 1e-8) {
431
+ out.setIdentity();
432
+ return out;
433
+ }
434
+ out.setXYZW(px / len, py / len, pz / len, q.w / len);
435
+ return out;
436
+ }
437
+ static twistAroundAxis(q, a) {
438
+ return Quat.twistAroundAxisInto(q, a, new Quat(0, 0, 0, 1));
439
+ }
440
+ // out = orientation looking along `forward` (mapped to local +Z, the engine's LH forward)
441
+ // with local +Y toward `up`. Falls back to a reference up when forward ∥ up.
442
+ static lookRotationInto(forward, up, out) {
443
+ let zx = forward.x, zy = forward.y, zz = forward.z;
444
+ const zl = Math.sqrt(zx * zx + zy * zy + zz * zz);
445
+ if (zl === 0)
446
+ return out.setIdentity();
447
+ const zi = 1 / zl;
448
+ zx *= zi;
449
+ zy *= zi;
450
+ zz *= zi;
451
+ // x = up × z
452
+ let xx = up.y * zz - up.z * zy;
453
+ let xy = up.z * zx - up.x * zz;
454
+ let xz = up.x * zy - up.y * zx;
455
+ let xl = Math.sqrt(xx * xx + xy * xy + xz * xz);
456
+ if (xl < 1e-8) {
457
+ // forward ∥ up: substitute a reference up not parallel to forward
458
+ const uy = Math.abs(zy) < 0.99 ? 1 : 0;
459
+ const ux = 1 - uy;
460
+ xx = uy * zz;
461
+ xy = -ux * zz;
462
+ xz = ux * zy - uy * zx;
463
+ xl = Math.sqrt(xx * xx + xy * xy + xz * xz);
464
+ }
465
+ const xi = 1 / xl;
466
+ xx *= xi;
467
+ xy *= xi;
468
+ xz *= xi;
469
+ // y = z × x (unit: z ⊥ x)
470
+ const yx = zy * xz - zz * xy;
471
+ const yy = zz * xx - zx * xz;
472
+ const yz = zx * xy - zy * xx;
473
+ _lookX.setXYZ(xx, xy, xz);
474
+ _lookY.setXYZ(yx, yy, yz);
475
+ _lookZ.setXYZ(zx, zy, zz);
476
+ return Quat.fromBasisInto(_lookX, _lookY, _lookZ, out);
477
+ }
478
+ static lookRotation(forward, up) {
479
+ return Quat.lookRotationInto(forward, up, new Quat(0, 0, 0, 1));
480
+ }
481
+ // out = q with the Z axis mirrored: right-handed Y-up ↔ left-handed Y-up
482
+ // (conjugation by the Z-mirror; involutive). Pairs with Vec3.mirrorZInto. Safe when out === q.
483
+ static mirrorZInto(q, out) {
484
+ out.x = -q.x;
485
+ out.y = -q.y;
486
+ out.z = q.z;
487
+ out.w = q.w;
488
+ return out;
489
+ }
490
+ static mirrorZ(q) {
491
+ return new Quat(-q.x, -q.y, q.z, q.w);
492
+ }
493
+ // out = quaternion from euler angles (radians) applied in the given intrinsic order.
494
+ // fromEulerOrderInto(x, y, z, "YXZ", out) matches Quat.fromEuler (the MMD/PMX convention).
495
+ static fromEulerOrderInto(x, y, z, order, out) {
496
+ out.setIdentity();
497
+ for (let i = 0; i < 3; i++) {
498
+ const axis = order.charCodeAt(i) - 88; // "X" → 0, "Y" → 1, "Z" → 2
499
+ const angle = axis === 0 ? x : axis === 1 ? y : z;
500
+ const half = angle * 0.5;
501
+ const s = Math.sin(half);
502
+ const c = Math.cos(half);
503
+ const ax = out.x, ay = out.y, az = out.z, aw = out.w;
504
+ if (axis === 0) {
505
+ out.x = aw * s + ax * c;
506
+ out.y = ay * c + az * s;
507
+ out.z = az * c - ay * s;
508
+ out.w = aw * c - ax * s;
509
+ }
510
+ else if (axis === 1) {
511
+ out.x = ax * c - az * s;
512
+ out.y = aw * s + ay * c;
513
+ out.z = az * c + ax * s;
514
+ out.w = aw * c - ay * s;
515
+ }
516
+ else {
517
+ out.x = ax * c + ay * s;
518
+ out.y = ay * c - ax * s;
519
+ out.z = aw * s + az * c;
520
+ out.w = aw * c - az * s;
521
+ }
522
+ }
523
+ return out;
524
+ }
525
+ static fromEulerOrder(x, y, z, order) {
526
+ return Quat.fromEulerOrderInto(x, y, z, order, new Quat(0, 0, 0, 1));
527
+ }
528
+ // Extract euler angles (radians) in the given intrinsic order into out (out.x/y/z = rotation
529
+ // about X/Y/Z). At gimbal lock (middle angle ±90°) the split between first and third angle is
530
+ // ambiguous; the third is set to 0.
531
+ static toEulerOrderInto(q, order, out) {
532
+ Mat4.fromQuatInto(q.x, q.y, q.z, q.w, _eulerMat, 0);
533
+ const m = _eulerMat;
534
+ const m11 = m[0], m12 = m[4], m13 = m[8];
535
+ const m21 = m[1], m22 = m[5], m23 = m[9];
536
+ const m31 = m[2], m32 = m[6], m33 = m[10];
537
+ const clamp = (v) => (v < -1 ? -1 : v > 1 ? 1 : v);
538
+ switch (order) {
539
+ case "XYZ":
540
+ out.y = Math.asin(clamp(m13));
541
+ if (Math.abs(m13) < 0.9999999) {
542
+ out.x = Math.atan2(-m23, m33);
543
+ out.z = Math.atan2(-m12, m11);
544
+ }
545
+ else {
546
+ out.x = Math.atan2(m32, m22);
547
+ out.z = 0;
548
+ }
549
+ break;
550
+ case "YXZ":
551
+ out.x = Math.asin(-clamp(m23));
552
+ if (Math.abs(m23) < 0.9999999) {
553
+ out.y = Math.atan2(m13, m33);
554
+ out.z = Math.atan2(m21, m22);
555
+ }
556
+ else {
557
+ out.y = Math.atan2(-m31, m11);
558
+ out.z = 0;
559
+ }
560
+ break;
561
+ case "ZXY":
562
+ out.x = Math.asin(clamp(m32));
563
+ if (Math.abs(m32) < 0.9999999) {
564
+ out.y = Math.atan2(-m31, m33);
565
+ out.z = Math.atan2(-m12, m22);
566
+ }
567
+ else {
568
+ out.y = 0;
569
+ out.z = Math.atan2(m21, m11);
570
+ }
571
+ break;
572
+ case "ZYX":
573
+ out.y = Math.asin(-clamp(m31));
574
+ if (Math.abs(m31) < 0.9999999) {
575
+ out.x = Math.atan2(m32, m33);
576
+ out.z = Math.atan2(m21, m11);
577
+ }
578
+ else {
579
+ out.x = 0;
580
+ out.z = Math.atan2(-m12, m22);
581
+ }
582
+ break;
583
+ case "YZX":
584
+ out.z = Math.asin(clamp(m21));
585
+ if (Math.abs(m21) < 0.9999999) {
586
+ out.x = Math.atan2(-m23, m22);
587
+ out.y = Math.atan2(-m31, m11);
588
+ }
589
+ else {
590
+ out.x = 0;
591
+ out.y = Math.atan2(m13, m33);
592
+ }
593
+ break;
594
+ case "XZY":
595
+ out.z = Math.asin(-clamp(m12));
596
+ if (Math.abs(m12) < 0.9999999) {
597
+ out.x = Math.atan2(m32, m22);
598
+ out.y = Math.atan2(m13, m11);
599
+ }
600
+ else {
601
+ out.x = Math.atan2(-m23, m33);
602
+ out.y = 0;
603
+ }
604
+ break;
605
+ }
606
+ return out;
607
+ }
608
+ static toEulerOrder(q, order) {
609
+ return Quat.toEulerOrderInto(q, order, new Vec3(0, 0, 0));
610
+ }
289
611
  }
290
612
  export class Mat4 {
291
613
  constructor(values) {
@@ -762,6 +1084,12 @@ export class Mat4 {
762
1084
  return new Mat4(out);
763
1085
  }
764
1086
  }
1087
+ // Module-private scratch for Quat.toEulerOrderInto / lookRotationInto (never handed out,
1088
+ // so no cross-call stomping with the public pools below).
1089
+ const _eulerMat = new Float32Array(16);
1090
+ const _lookX = new Vec3(0, 0, 0);
1091
+ const _lookY = new Vec3(0, 0, 0);
1092
+ const _lookZ = new Vec3(0, 0, 0);
765
1093
  // Preallocated scratch instances for hot paths. Each subsystem should use its own
766
1094
  // slot to avoid cross-call stomping. Bump the count if more call sites need scratch.
767
1095
  export const scratchMat4Values = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.27.4",
3
+ "version": "0.28.0",
4
4
  "description": "A lightweight WebGPU engine for real-time 3D MMD/PMX model rendering",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/ik-solver.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // IK solver (MMD-style; see Saba MMDIkSolver.cpp)
2
2
 
3
- import { Mat4, Quat, Vec3 } from "./math"
3
+ import { Mat4, Quat, Vec3, type EulerOrder } from "./math"
4
4
  import { Bone, IKLink, IKSolver, IKChainInfo } from "./model"
5
5
 
6
6
  // Callback type for updating world matrix (provided by model to handle append transformations)
@@ -80,7 +80,6 @@ const _ikVec: Vec3[] = [
80
80
  ]
81
81
  const _ikQuat: Quat[] = [
82
82
  new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1),
83
- new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1),
84
83
  ]
85
84
  const _ikMat: Float32Array[] = [
86
85
  new Float32Array(16), new Float32Array(16), new Float32Array(16), new Float32Array(16),
@@ -215,7 +214,7 @@ export class IKSolverSystem {
215
214
  // _ikVec[0]=chainPos, [1]=ikPos, [2]=targetPos, [3]=chainTargetVec, [4]=chainIkVec,
216
215
  // [5]=rotAxis, [6]=finalAxis, [7]=eulerTmp, [8]=limitedEuler
217
216
  // _ikMat[0]=parentRot, [1]=invParentRot, [2]=quatToMatTmp
218
- // _ikQuat[0]=ikRotation, [1]=combinedRot, [2]=localRotConj, [3..5]=axisAngleTmp
217
+ // _ikQuat[0]=ikRotation, [1]=combinedRot, [2]=localRotConj
219
218
 
220
219
  const chainPos = Vec3.setFromMat4Translation(worldMatrices[chainBoneIndex].values, _ikVec[0])
221
220
  const ikPos = Vec3.setFromMat4Translation(worldMatrices[ikBoneIndex].values, _ikVec[1])
@@ -340,16 +339,8 @@ export class IKSolverSystem {
340
339
  return Math.sqrt(dx * dx + dy * dy + dz * dz)
341
340
  }
342
341
 
343
- // Euler axis triples for each rotation order (indexed by order enum).
344
- // Reused to avoid allocations in reconstructQuatFromEulerInto.
345
- private static readonly EULER_AXES: readonly [number, number, number][][] = [
346
- // YXZ: Y, X, Z
347
- [[0, 1, 0], [1, 0, 0], [0, 0, 1]],
348
- // ZYX: Z, Y, X
349
- [[0, 0, 1], [0, 1, 0], [1, 0, 0]],
350
- // XZY: X, Z, Y
351
- [[1, 0, 0], [0, 0, 1], [0, 1, 0]],
352
- ]
342
+ // Maps InternalEulerRotationOrder to the shared math-layer order names.
343
+ private static readonly EULER_ORDER_NAMES: readonly EulerOrder[] = ["YXZ", "ZYX", "XZY"]
353
344
 
354
345
  private static extractEulerAnglesInto(quat: Quat, order: InternalEulerRotationOrder, out: Vec3): void {
355
346
  Mat4.fromQuatInto(quat.x, quat.y, quat.z, quat.w, _ikMat[2], 0)
@@ -396,27 +387,7 @@ export class IKSolverSystem {
396
387
  }
397
388
 
398
389
  private static reconstructQuatFromEulerInto(euler: Vec3, order: InternalEulerRotationOrder, out: Quat): void {
399
- const axes = this.EULER_AXES[order]
400
- const a1 = axes[0], a2 = axes[1], a3 = axes[2]
401
- const ang1 =
402
- order === InternalEulerRotationOrder.YXZ ? euler.y
403
- : order === InternalEulerRotationOrder.ZYX ? euler.z
404
- : euler.x
405
- const ang2 =
406
- order === InternalEulerRotationOrder.YXZ ? euler.x
407
- : order === InternalEulerRotationOrder.ZYX ? euler.y
408
- : euler.z
409
- const ang3 =
410
- order === InternalEulerRotationOrder.YXZ ? euler.z
411
- : order === InternalEulerRotationOrder.ZYX ? euler.x
412
- : euler.y
413
-
414
- // result = axisAngle(a1, ang1); then *= axisAngle(a2, ang2); then *= axisAngle(a3, ang3)
415
- Quat.fromAxisAngleInto(a1[0], a1[1], a1[2], ang1, out)
416
- Quat.fromAxisAngleInto(a2[0], a2[1], a2[2], ang2, _ikQuat[3])
417
- Quat.multiplyInto(out, _ikQuat[3], out)
418
- Quat.fromAxisAngleInto(a3[0], a3[1], a3[2], ang3, _ikQuat[3])
419
- Quat.multiplyInto(out, _ikQuat[3], out)
390
+ Quat.fromEulerOrderInto(euler.x, euler.y, euler.z, this.EULER_ORDER_NAMES[order], out)
420
391
  }
421
392
 
422
393
  // Write parent's world rotation (translation stripped) into out Float32Array.
package/src/index.ts CHANGED
@@ -53,7 +53,7 @@ export { STOCKINGS_GRAPH } from "./graph/presets/stockings"
53
53
  export { EYE_GRAPH } from "./graph/presets/eye"
54
54
  export { FACE_GRAPH } from "./graph/presets/face"
55
55
  export { Model } from "./model"
56
- export { Vec3, Quat, Mat4 } from "./math"
56
+ export { Vec3, Quat, Mat4, easeInOut, type EulerOrder } from "./math"
57
57
  export type {
58
58
  AnimationClip,
59
59
  AnimationPlayOptions,
@@ -64,7 +64,12 @@ export type {
64
64
  BoneInterpolation,
65
65
  ControlPoint,
66
66
  } from "./animation"
67
- export { FPS } from "./animation"
67
+ export {
68
+ FPS,
69
+ bezierInterpolate,
70
+ interpolateControlPoints,
71
+ rawInterpolationToBoneInterpolation,
72
+ } from "./animation"
68
73
  export { VMDLoader, type CameraKeyframe, type IkFrame } from "./vmd-loader"
69
74
  export { CameraAnimation, type CameraPose } from "./camera-animation"
70
75
  export { RezePhysics } from "./physics"
package/src/math.ts CHANGED
@@ -3,6 +3,10 @@ export function easeInOut(t: number): number {
3
3
  return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
4
4
  }
5
5
 
6
+ // Euler rotation order, read as intrinsic axes left to right:
7
+ // "YXZ" = rotate about Y, then local X, then local Z (equals extrinsic ZXY — the MMD/PMX convention).
8
+ export type EulerOrder = "XYZ" | "XZY" | "YXZ" | "YZX" | "ZXY" | "ZYX"
9
+
6
10
  export class Vec3 {
7
11
  x: number
8
12
  y: number
@@ -113,6 +117,18 @@ export class Vec3 {
113
117
  return out
114
118
  }
115
119
 
120
+ // out = (x, y, -z): right-handed Y-up ↔ left-handed Y-up (involutive). Safe when out === v.
121
+ static mirrorZInto(v: Vec3, out: Vec3): Vec3 {
122
+ out.x = v.x
123
+ out.y = v.y
124
+ out.z = -v.z
125
+ return out
126
+ }
127
+
128
+ static mirrorZ(v: Vec3): Vec3 {
129
+ return new Vec3(v.x, v.y, -v.z)
130
+ }
131
+
116
132
  // In-place normalize returning length squared info via Vec3. Alias for normalize() but explicit.
117
133
  normalizeInPlace(): Vec3 {
118
134
  return this.normalize()
@@ -333,6 +349,328 @@ export class Quat {
333
349
 
334
350
  return new Quat(x, y, z, w).normalize()
335
351
  }
352
+
353
+ // 4D dot product. Negative means a and b are on opposite hemispheres (same rotation
354
+ // when |dot| ≈ 1 either way — quaternion double cover).
355
+ static dot(a: Quat, b: Quat): number {
356
+ return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
357
+ }
358
+
359
+ // Rotation angle between a and b in radians, insensitive to double cover.
360
+ static angleTo(a: Quat, b: Quat): number {
361
+ const d = Math.abs(a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w)
362
+ return 2 * Math.acos(Math.min(1, d))
363
+ }
364
+
365
+ // out = conjugate of q (inverse for unit quaternions), without mutating q. Safe when out === q.
366
+ static conjugateInto(q: Quat, out: Quat): Quat {
367
+ out.x = -q.x
368
+ out.y = -q.y
369
+ out.z = -q.z
370
+ out.w = q.w
371
+ return out
372
+ }
373
+
374
+ // out = normalized lerp from a to b, taking the shorter path (negates b when dot < 0).
375
+ // Cheaper than slerp; non-constant angular velocity. Safe when out === a or out === b.
376
+ static nlerpInto(a: Quat, b: Quat, t: number, out: Quat): Quat {
377
+ const d = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
378
+ const s = d < 0 ? -1 : 1
379
+ const x = a.x + (b.x * s - a.x) * t
380
+ const y = a.y + (b.y * s - a.y) * t
381
+ const z = a.z + (b.z * s - a.z) * t
382
+ const w = a.w + (b.w * s - a.w) * t
383
+ const invLen = 1 / Math.hypot(x, y, z, w)
384
+ out.x = x * invLen
385
+ out.y = y * invLen
386
+ out.z = z * invLen
387
+ out.w = w * invLen
388
+ return out
389
+ }
390
+
391
+ static nlerp(a: Quat, b: Quat, t: number): Quat {
392
+ return Quat.nlerpInto(a, b, t, new Quat(0, 0, 0, 1))
393
+ }
394
+
395
+ // out = v rotated by unit quaternion q (no matrix, no allocation). Safe when out === v.
396
+ static rotateVecInto(q: Quat, v: Vec3, out: Vec3): Vec3 {
397
+ // v' = v + 2*qw*(qv × v) + 2*(qv × (qv × v))
398
+ const qx = q.x, qy = q.y, qz = q.z, qw = q.w
399
+ const vx = v.x, vy = v.y, vz = v.z
400
+ // t = 2 * (qv × v)
401
+ const tx = 2 * (qy * vz - qz * vy)
402
+ const ty = 2 * (qz * vx - qx * vz)
403
+ const tz = 2 * (qx * vy - qy * vx)
404
+ out.x = vx + qw * tx + qy * tz - qz * ty
405
+ out.y = vy + qw * ty + qz * tx - qx * tz
406
+ out.z = vz + qw * tz + qx * ty - qy * tx
407
+ return out
408
+ }
409
+
410
+ static rotateVec(q: Quat, v: Vec3): Vec3 {
411
+ return Quat.rotateVecInto(q, v, new Vec3(0, 0, 0))
412
+ }
413
+
414
+ // out = v rotated by the inverse (conjugate) of unit quaternion q. Safe when out === v.
415
+ static rotateVecInvInto(q: Quat, v: Vec3, out: Vec3): Vec3 {
416
+ const qx = -q.x, qy = -q.y, qz = -q.z, qw = q.w
417
+ const vx = v.x, vy = v.y, vz = v.z
418
+ const tx = 2 * (qy * vz - qz * vy)
419
+ const ty = 2 * (qz * vx - qx * vz)
420
+ const tz = 2 * (qx * vy - qy * vx)
421
+ out.x = vx + qw * tx + qy * tz - qz * ty
422
+ out.y = vy + qw * ty + qz * tx - qx * tz
423
+ out.z = vz + qw * tz + qx * ty - qy * tx
424
+ return out
425
+ }
426
+
427
+ static rotateVecInv(q: Quat, v: Vec3): Vec3 {
428
+ return Quat.rotateVecInvInto(q, v, new Vec3(0, 0, 0))
429
+ }
430
+
431
+ // out = shortest-arc rotation taking unit vector `from` to unit vector `to`.
432
+ // Matches Babylon's FromUnitVectorsToRef exactly, including the near-antiparallel
433
+ // branch (w = 1 + dot < 0.001 → 180° about a perpendicular picked the same way).
434
+ static fromUnitVectorsInto(from: Vec3, to: Vec3, out: Quat): Quat {
435
+ const r = from.x * to.x + from.y * to.y + from.z * to.z + 1
436
+ if (r < 0.001) {
437
+ if (Math.abs(from.x) > Math.abs(from.z)) {
438
+ out.setXYZW(-from.y, from.x, 0, 0)
439
+ } else {
440
+ out.setXYZW(0, -from.z, from.y, 0)
441
+ }
442
+ } else {
443
+ // q = (from × to, 1 + from·to)
444
+ out.setXYZW(
445
+ from.y * to.z - from.z * to.y,
446
+ from.z * to.x - from.x * to.z,
447
+ from.x * to.y - from.y * to.x,
448
+ r
449
+ )
450
+ }
451
+ const invLen = 1 / Math.sqrt(out.x * out.x + out.y * out.y + out.z * out.z + out.w * out.w)
452
+ out.setXYZW(out.x * invLen, out.y * invLen, out.z * invLen, out.w * invLen)
453
+ return out
454
+ }
455
+
456
+ static fromUnitVectors(from: Vec3, to: Vec3): Quat {
457
+ return Quat.fromUnitVectorsInto(from, to, new Quat(0, 0, 0, 1))
458
+ }
459
+
460
+ // out = rotation taking the standard basis onto the orthonormal axes x, y, z
461
+ // (the columns of the column-major rotation matrix): rotateVec(out, (1,0,0)) = x, etc.
462
+ static fromBasisInto(x: Vec3, y: Vec3, z: Vec3, out: Quat): Quat {
463
+ // Shepperd's method on the 3x3 with columns x, y, z.
464
+ const m00 = x.x, m01 = x.y, m02 = x.z
465
+ const m10 = y.x, m11 = y.y, m12 = y.z
466
+ const m20 = z.x, m21 = z.y, m22 = z.z
467
+ const trace = m00 + m11 + m22
468
+ if (trace > 0) {
469
+ const s = 0.5 / Math.sqrt(trace + 1)
470
+ out.setXYZW((m12 - m21) * s, (m20 - m02) * s, (m01 - m10) * s, 0.25 / s)
471
+ } else if (m00 > m11 && m00 > m22) {
472
+ const s = 2 * Math.sqrt(1 + m00 - m11 - m22)
473
+ out.setXYZW(0.25 * s, (m10 + m01) / s, (m20 + m02) / s, (m12 - m21) / s)
474
+ } else if (m11 > m22) {
475
+ const s = 2 * Math.sqrt(1 + m11 - m00 - m22)
476
+ out.setXYZW((m10 + m01) / s, 0.25 * s, (m21 + m12) / s, (m20 - m02) / s)
477
+ } else {
478
+ const s = 2 * Math.sqrt(1 + m22 - m00 - m11)
479
+ out.setXYZW((m20 + m02) / s, (m21 + m12) / s, 0.25 * s, (m01 - m10) / s)
480
+ }
481
+ return out
482
+ }
483
+
484
+ static fromBasis(x: Vec3, y: Vec3, z: Vec3): Quat {
485
+ return Quat.fromBasisInto(x, y, z, new Quat(0, 0, 0, 1))
486
+ }
487
+
488
+ // out = twist component of q around unit axis `a`, so that q = swing · twist
489
+ // (swing = Quat.multiply(q, conjugate(twist))). Singular when q is ~180° about an
490
+ // axis perpendicular to `a` — returns identity there.
491
+ static twistAroundAxisInto(q: Quat, a: Vec3, out: Quat): Quat {
492
+ const d = q.x * a.x + q.y * a.y + q.z * a.z
493
+ const px = a.x * d
494
+ const py = a.y * d
495
+ const pz = a.z * d
496
+ const len = Math.sqrt(px * px + py * py + pz * pz + q.w * q.w)
497
+ if (len < 1e-8) {
498
+ out.setIdentity()
499
+ return out
500
+ }
501
+ out.setXYZW(px / len, py / len, pz / len, q.w / len)
502
+ return out
503
+ }
504
+
505
+ static twistAroundAxis(q: Quat, a: Vec3): Quat {
506
+ return Quat.twistAroundAxisInto(q, a, new Quat(0, 0, 0, 1))
507
+ }
508
+
509
+ // out = orientation looking along `forward` (mapped to local +Z, the engine's LH forward)
510
+ // with local +Y toward `up`. Falls back to a reference up when forward ∥ up.
511
+ static lookRotationInto(forward: Vec3, up: Vec3, out: Quat): Quat {
512
+ let zx = forward.x, zy = forward.y, zz = forward.z
513
+ const zl = Math.sqrt(zx * zx + zy * zy + zz * zz)
514
+ if (zl === 0) return out.setIdentity()
515
+ const zi = 1 / zl
516
+ zx *= zi; zy *= zi; zz *= zi
517
+ // x = up × z
518
+ let xx = up.y * zz - up.z * zy
519
+ let xy = up.z * zx - up.x * zz
520
+ let xz = up.x * zy - up.y * zx
521
+ let xl = Math.sqrt(xx * xx + xy * xy + xz * xz)
522
+ if (xl < 1e-8) {
523
+ // forward ∥ up: substitute a reference up not parallel to forward
524
+ const uy = Math.abs(zy) < 0.99 ? 1 : 0
525
+ const ux = 1 - uy
526
+ xx = uy * zz
527
+ xy = -ux * zz
528
+ xz = ux * zy - uy * zx
529
+ xl = Math.sqrt(xx * xx + xy * xy + xz * xz)
530
+ }
531
+ const xi = 1 / xl
532
+ xx *= xi; xy *= xi; xz *= xi
533
+ // y = z × x (unit: z ⊥ x)
534
+ const yx = zy * xz - zz * xy
535
+ const yy = zz * xx - zx * xz
536
+ const yz = zx * xy - zy * xx
537
+ _lookX.setXYZ(xx, xy, xz)
538
+ _lookY.setXYZ(yx, yy, yz)
539
+ _lookZ.setXYZ(zx, zy, zz)
540
+ return Quat.fromBasisInto(_lookX, _lookY, _lookZ, out)
541
+ }
542
+
543
+ static lookRotation(forward: Vec3, up: Vec3): Quat {
544
+ return Quat.lookRotationInto(forward, up, new Quat(0, 0, 0, 1))
545
+ }
546
+
547
+ // out = q with the Z axis mirrored: right-handed Y-up ↔ left-handed Y-up
548
+ // (conjugation by the Z-mirror; involutive). Pairs with Vec3.mirrorZInto. Safe when out === q.
549
+ static mirrorZInto(q: Quat, out: Quat): Quat {
550
+ out.x = -q.x
551
+ out.y = -q.y
552
+ out.z = q.z
553
+ out.w = q.w
554
+ return out
555
+ }
556
+
557
+ static mirrorZ(q: Quat): Quat {
558
+ return new Quat(-q.x, -q.y, q.z, q.w)
559
+ }
560
+
561
+ // out = quaternion from euler angles (radians) applied in the given intrinsic order.
562
+ // fromEulerOrderInto(x, y, z, "YXZ", out) matches Quat.fromEuler (the MMD/PMX convention).
563
+ static fromEulerOrderInto(x: number, y: number, z: number, order: EulerOrder, out: Quat): Quat {
564
+ out.setIdentity()
565
+ for (let i = 0; i < 3; i++) {
566
+ const axis = order.charCodeAt(i) - 88 // "X" → 0, "Y" → 1, "Z" → 2
567
+ const angle = axis === 0 ? x : axis === 1 ? y : z
568
+ const half = angle * 0.5
569
+ const s = Math.sin(half)
570
+ const c = Math.cos(half)
571
+ const ax = out.x, ay = out.y, az = out.z, aw = out.w
572
+ if (axis === 0) {
573
+ out.x = aw * s + ax * c
574
+ out.y = ay * c + az * s
575
+ out.z = az * c - ay * s
576
+ out.w = aw * c - ax * s
577
+ } else if (axis === 1) {
578
+ out.x = ax * c - az * s
579
+ out.y = aw * s + ay * c
580
+ out.z = az * c + ax * s
581
+ out.w = aw * c - ay * s
582
+ } else {
583
+ out.x = ax * c + ay * s
584
+ out.y = ay * c - ax * s
585
+ out.z = aw * s + az * c
586
+ out.w = aw * c - az * s
587
+ }
588
+ }
589
+ return out
590
+ }
591
+
592
+ static fromEulerOrder(x: number, y: number, z: number, order: EulerOrder): Quat {
593
+ return Quat.fromEulerOrderInto(x, y, z, order, new Quat(0, 0, 0, 1))
594
+ }
595
+
596
+ // Extract euler angles (radians) in the given intrinsic order into out (out.x/y/z = rotation
597
+ // about X/Y/Z). At gimbal lock (middle angle ±90°) the split between first and third angle is
598
+ // ambiguous; the third is set to 0.
599
+ static toEulerOrderInto(q: Quat, order: EulerOrder, out: Vec3): Vec3 {
600
+ Mat4.fromQuatInto(q.x, q.y, q.z, q.w, _eulerMat, 0)
601
+ const m = _eulerMat
602
+ const m11 = m[0], m12 = m[4], m13 = m[8]
603
+ const m21 = m[1], m22 = m[5], m23 = m[9]
604
+ const m31 = m[2], m32 = m[6], m33 = m[10]
605
+ const clamp = (v: number) => (v < -1 ? -1 : v > 1 ? 1 : v)
606
+ switch (order) {
607
+ case "XYZ":
608
+ out.y = Math.asin(clamp(m13))
609
+ if (Math.abs(m13) < 0.9999999) {
610
+ out.x = Math.atan2(-m23, m33)
611
+ out.z = Math.atan2(-m12, m11)
612
+ } else {
613
+ out.x = Math.atan2(m32, m22)
614
+ out.z = 0
615
+ }
616
+ break
617
+ case "YXZ":
618
+ out.x = Math.asin(-clamp(m23))
619
+ if (Math.abs(m23) < 0.9999999) {
620
+ out.y = Math.atan2(m13, m33)
621
+ out.z = Math.atan2(m21, m22)
622
+ } else {
623
+ out.y = Math.atan2(-m31, m11)
624
+ out.z = 0
625
+ }
626
+ break
627
+ case "ZXY":
628
+ out.x = Math.asin(clamp(m32))
629
+ if (Math.abs(m32) < 0.9999999) {
630
+ out.y = Math.atan2(-m31, m33)
631
+ out.z = Math.atan2(-m12, m22)
632
+ } else {
633
+ out.y = 0
634
+ out.z = Math.atan2(m21, m11)
635
+ }
636
+ break
637
+ case "ZYX":
638
+ out.y = Math.asin(-clamp(m31))
639
+ if (Math.abs(m31) < 0.9999999) {
640
+ out.x = Math.atan2(m32, m33)
641
+ out.z = Math.atan2(m21, m11)
642
+ } else {
643
+ out.x = 0
644
+ out.z = Math.atan2(-m12, m22)
645
+ }
646
+ break
647
+ case "YZX":
648
+ out.z = Math.asin(clamp(m21))
649
+ if (Math.abs(m21) < 0.9999999) {
650
+ out.x = Math.atan2(-m23, m22)
651
+ out.y = Math.atan2(-m31, m11)
652
+ } else {
653
+ out.x = 0
654
+ out.y = Math.atan2(m13, m33)
655
+ }
656
+ break
657
+ case "XZY":
658
+ out.z = Math.asin(-clamp(m12))
659
+ if (Math.abs(m12) < 0.9999999) {
660
+ out.x = Math.atan2(m32, m22)
661
+ out.y = Math.atan2(m13, m11)
662
+ } else {
663
+ out.x = Math.atan2(-m23, m33)
664
+ out.y = 0
665
+ }
666
+ break
667
+ }
668
+ return out
669
+ }
670
+
671
+ static toEulerOrder(q: Quat, order: EulerOrder): Vec3 {
672
+ return Quat.toEulerOrderInto(q, order, new Vec3(0, 0, 0))
673
+ }
336
674
  }
337
675
 
338
676
  export class Mat4 {
@@ -837,6 +1175,13 @@ export class Mat4 {
837
1175
  }
838
1176
  }
839
1177
 
1178
+ // Module-private scratch for Quat.toEulerOrderInto / lookRotationInto (never handed out,
1179
+ // so no cross-call stomping with the public pools below).
1180
+ const _eulerMat = new Float32Array(16)
1181
+ const _lookX = new Vec3(0, 0, 0)
1182
+ const _lookY = new Vec3(0, 0, 0)
1183
+ const _lookZ = new Vec3(0, 0, 0)
1184
+
840
1185
  // Preallocated scratch instances for hot paths. Each subsystem should use its own
841
1186
  // slot to avoid cross-call stomping. Bump the count if more call sites need scratch.
842
1187
  export const scratchMat4Values: Float32Array[] = [