reze-engine 0.3.10 → 0.3.12

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
@@ -1,66 +1,66 @@
1
- # Reze Engine
2
-
3
- A lightweight engine built with WebGPU and TypeScript for real-time 3D anime character MMD model rendering.
4
-
5
- ## Features
6
-
7
- - Physics
8
- - Alpha blending
9
- - Post alpha eye rendering
10
- - Rim lighting
11
- - Bloom
12
- - Outlines
13
- - MSAA 4x anti-aliasing
14
- - Bone and morph api
15
- - VMD animation
16
- - Ik solver
17
-
18
- ## Usage
19
-
20
- ```javascript
21
- export default function Scene() {
22
- const canvasRef = useRef < HTMLCanvasElement > null
23
- const engineRef = useRef < Engine > null
24
-
25
- const initEngine = useCallback(async () => {
26
- if (canvasRef.current) {
27
- try {
28
- const engine = new Engine(canvasRef.current)
29
- engineRef.current = engine
30
- await engine.init()
31
- await engine.loadModel("/models/塞尔凯特/塞尔凯特.pmx")
32
-
33
- engine.runRenderLoop(() => {})
34
- } catch (error) {
35
- console.error(error)
36
- }
37
- }
38
- }, [])
39
-
40
- useEffect(() => {
41
- void (async () => {
42
- initEngine()
43
- })()
44
-
45
- return () => {
46
- if (engineRef.current) {
47
- engineRef.current.dispose()
48
- }
49
- }
50
- }, [initEngine])
51
-
52
- return <canvas ref={canvasRef} className="w-full h-full" />
53
- }
54
- ```
55
-
56
- ## Projects Using This Engine
57
-
58
- - **[MiKaPo](https://mikapo.vercel.app)** - Online real-time motion capture for MMD using webcam and MediaPipe
59
- - **[Popo](https://popo.love)** - Fine-tuned LLM that generates MMD poses from natural language descriptions
60
- - **[MPL](https://mmd-mpl.vercel.app)** - Semantic motion programming language for scripting MMD animations with intuitive syntax
61
-
62
- ## Tutorial
63
-
64
- Learn WebGPU from scratch by building an anime character renderer in incremental steps. The tutorial covers the complete rendering pipeline from a simple triangle to fully textured, skeletal-animated characters.
65
-
66
- [How to Render an Anime Character with WebGPU](https://reze.one/tutorial)
1
+ # Reze Engine
2
+
3
+ A lightweight engine built with WebGPU and TypeScript for real-time 3D anime character MMD model rendering.
4
+
5
+ ## Features
6
+
7
+ - Physics
8
+ - Alpha blending
9
+ - Post alpha eye rendering
10
+ - Rim lighting
11
+ - Bloom
12
+ - Outlines
13
+ - MSAA 4x anti-aliasing
14
+ - Bone and morph api
15
+ - VMD animation
16
+ - Ik solver
17
+
18
+ ## Usage
19
+
20
+ ```javascript
21
+ export default function Scene() {
22
+ const canvasRef = useRef < HTMLCanvasElement > null
23
+ const engineRef = useRef < Engine > null
24
+
25
+ const initEngine = useCallback(async () => {
26
+ if (canvasRef.current) {
27
+ try {
28
+ const engine = new Engine(canvasRef.current)
29
+ engineRef.current = engine
30
+ await engine.init()
31
+ await engine.loadModel("/models/塞尔凯特/塞尔凯特.pmx")
32
+
33
+ engine.runRenderLoop(() => {})
34
+ } catch (error) {
35
+ console.error(error)
36
+ }
37
+ }
38
+ }, [])
39
+
40
+ useEffect(() => {
41
+ void (async () => {
42
+ initEngine()
43
+ })()
44
+
45
+ return () => {
46
+ if (engineRef.current) {
47
+ engineRef.current.dispose()
48
+ }
49
+ }
50
+ }, [initEngine])
51
+
52
+ return <canvas ref={canvasRef} className="w-full h-full" />
53
+ }
54
+ ```
55
+
56
+ ## Projects Using This Engine
57
+
58
+ - **[MiKaPo](https://mikapo.vercel.app)** - Online real-time motion capture for MMD using webcam and MediaPipe
59
+ - **[Popo](https://popo.love)** - Fine-tuned LLM that generates MMD poses from natural language descriptions
60
+ - **[MPL](https://mmd-mpl.vercel.app)** - Semantic motion programming language for scripting MMD animations with intuitive syntax
61
+
62
+ ## Tutorial
63
+
64
+ Learn WebGPU from scratch by building an anime character renderer in incremental steps. The tutorial covers the complete rendering pipeline from a simple triangle to fully textured, skeletal-animated characters.
65
+
66
+ [How to Render an Anime Character with WebGPU](https://reze.one/tutorial)
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Audio system for generating procedural explosion sounds
3
+ * Based on Web Audio API synthesis
4
+ */
5
+ export declare class AudioSystem {
6
+ private ctx;
7
+ private enabled;
8
+ private volume;
9
+ private limiter;
10
+ /**
11
+ * Initialize audio context and compressor
12
+ * Must be called after user interaction (browser requirement)
13
+ */
14
+ init(): void;
15
+ /**
16
+ * Play deep bass explosion sound (firework burst)
17
+ * Combines sub-bass, noise, and crack sounds for realistic explosion
18
+ */
19
+ playExplosion(): void;
20
+ /**
21
+ * Play lighter "whoosh" sound for rocket launch
22
+ */
23
+ playRocketLaunch(): void;
24
+ setVolume(volume: number): void;
25
+ getVolume(): number;
26
+ isEnabled(): boolean;
27
+ setEnabled(enabled: boolean): void;
28
+ }
29
+ //# sourceMappingURL=audio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAA4B;IACvC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAAsC;IAErD;;;OAGG;IACH,IAAI,IAAI,IAAI;IAiBZ;;;OAGG;IACH,aAAa,IAAI,IAAI;IA8DrB;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAsBxB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,MAAM;IAInB,SAAS,IAAI,OAAO;IAIpB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAGnC"}
package/dist/audio.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Audio system for generating procedural explosion sounds
3
+ * Based on Web Audio API synthesis
4
+ */
5
+ export class AudioSystem {
6
+ constructor() {
7
+ this.ctx = null;
8
+ this.enabled = false;
9
+ this.volume = 1;
10
+ this.limiter = null;
11
+ }
12
+ /**
13
+ * Initialize audio context and compressor
14
+ * Must be called after user interaction (browser requirement)
15
+ */
16
+ init() {
17
+ if (!this.ctx) {
18
+ this.ctx = new (window.AudioContext || window.webkitAudioContext)();
19
+ this.limiter = this.ctx.createDynamicsCompressor();
20
+ this.limiter.threshold.value = -10;
21
+ this.limiter.knee.value = 40;
22
+ this.limiter.ratio.value = 12;
23
+ this.limiter.connect(this.ctx.destination);
24
+ this.enabled = true;
25
+ }
26
+ // Resume if suspended (iOS/Safari requirement)
27
+ if (this.ctx.state === "suspended") {
28
+ this.ctx.resume();
29
+ }
30
+ }
31
+ /**
32
+ * Play deep bass explosion sound (firework burst)
33
+ * Combines sub-bass, noise, and crack sounds for realistic explosion
34
+ */
35
+ playExplosion() {
36
+ if (!this.enabled || !this.ctx || !this.limiter)
37
+ return;
38
+ const t = this.ctx.currentTime;
39
+ // --- 1. SUB-BASS KICK (deep rumble) ---
40
+ const osc = this.ctx.createOscillator();
41
+ const oscGain = this.ctx.createGain();
42
+ osc.type = "sine";
43
+ osc.frequency.setValueAtTime(50, t); // Start at 50Hz
44
+ osc.frequency.exponentialRampToValueAtTime(20, t + 2.5); // Drop to 20Hz
45
+ oscGain.gain.setValueAtTime(this.volume * 1.5, t);
46
+ oscGain.gain.exponentialRampToValueAtTime(0.01, t + 5.0); // Long decay
47
+ osc.connect(oscGain);
48
+ oscGain.connect(this.limiter);
49
+ osc.start(t);
50
+ osc.stop(t + 5.0);
51
+ // --- 2. NOISE (crackling/rumble texture) ---
52
+ const bufferSize = this.ctx.sampleRate * 5.0;
53
+ const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
54
+ const data = buffer.getChannelData(0);
55
+ for (let i = 0; i < bufferSize; i++) {
56
+ data[i] = Math.random() * 2 - 1;
57
+ }
58
+ const noise = this.ctx.createBufferSource();
59
+ noise.buffer = buffer;
60
+ const noiseFilter = this.ctx.createBiquadFilter();
61
+ noiseFilter.type = "lowpass";
62
+ noiseFilter.frequency.setValueAtTime(150, t);
63
+ noiseFilter.frequency.exponentialRampToValueAtTime(30, t + 4.0);
64
+ const noiseGain = this.ctx.createGain();
65
+ noiseGain.gain.setValueAtTime(this.volume * 1.0, t);
66
+ noiseGain.gain.exponentialRampToValueAtTime(0.01, t + 4.5);
67
+ noise.connect(noiseFilter);
68
+ noiseFilter.connect(noiseGain);
69
+ noiseGain.connect(this.limiter);
70
+ noise.start(t);
71
+ // --- 3. CRACK (sharp attack) ---
72
+ const crack = this.ctx.createOscillator();
73
+ crack.type = "triangle";
74
+ const crackGain = this.ctx.createGain();
75
+ crack.frequency.setValueAtTime(200, t);
76
+ crack.frequency.exponentialRampToValueAtTime(50, t + 0.1);
77
+ crackGain.gain.setValueAtTime(this.volume * 0.3, t);
78
+ crackGain.gain.exponentialRampToValueAtTime(0.01, t + 0.1);
79
+ crack.connect(crackGain);
80
+ crackGain.connect(this.limiter);
81
+ crack.start(t);
82
+ crack.stop(t + 0.1);
83
+ }
84
+ /**
85
+ * Play lighter "whoosh" sound for rocket launch
86
+ */
87
+ playRocketLaunch() {
88
+ if (!this.enabled || !this.ctx || !this.limiter)
89
+ return;
90
+ const t = this.ctx.currentTime;
91
+ // Rising tone
92
+ const osc = this.ctx.createOscillator();
93
+ const oscGain = this.ctx.createGain();
94
+ osc.type = "sawtooth";
95
+ osc.frequency.setValueAtTime(100, t);
96
+ osc.frequency.exponentialRampToValueAtTime(400, t + 0.5);
97
+ oscGain.gain.setValueAtTime(this.volume * 0.3, t);
98
+ oscGain.gain.exponentialRampToValueAtTime(0.01, t + 0.5);
99
+ osc.connect(oscGain);
100
+ oscGain.connect(this.limiter);
101
+ osc.start(t);
102
+ osc.stop(t + 0.5);
103
+ }
104
+ setVolume(volume) {
105
+ this.volume = Math.max(0, Math.min(1, volume));
106
+ }
107
+ getVolume() {
108
+ return this.volume;
109
+ }
110
+ isEnabled() {
111
+ return this.enabled;
112
+ }
113
+ setEnabled(enabled) {
114
+ this.enabled = enabled;
115
+ }
116
+ }
package/dist/engine.js CHANGED
@@ -250,12 +250,12 @@ export class Engine {
250
250
 
251
251
  let lightAccum = light.ambientColor;
252
252
 
253
- // Rim light calculation
253
+ // Rim light calculation - proper Fresnel for edge-only highlights
254
254
  let viewDir = normalize(camera.viewPos - input.worldPos);
255
- var rimFactor = 1.0 - max(dot(n, viewDir), 0.0);
256
- rimFactor = rimFactor * rimFactor; // Optimized: direct multiply instead of pow(x, 2.0)
255
+ let fresnel = 1.0 - abs(dot(n, viewDir));
256
+ let rimFactor = pow(fresnel, 4.0); // Higher power for sharper edge-only effect
257
257
  let rimLight = material.rimColor * material.rimIntensity * rimFactor;
258
-
258
+
259
259
  let color = albedo * lightAccum + rimLight;
260
260
 
261
261
  return vec4f(color, finalAlpha);
@@ -3,6 +3,7 @@
3
3
  * Based on reference from babylon-mmd and Saba MMD library
4
4
  * https://github.com/benikabocha/saba/blob/master/src/Saba/Model/MMD/MMDIkSolver.cpp
5
5
  */
6
+ import { Mat4, Quat, Vec3 } from "./math";
6
7
  import { Bone, IKSolver, IKChainInfo } from "./model";
7
8
  /**
8
9
  * Solve IK chains for a model
@@ -13,14 +14,12 @@ export declare class IKSolverSystem {
13
14
  /**
14
15
  * Solve all IK chains
15
16
  */
16
- static solve(ikSolvers: IKSolver[], bones: Bone[], localRotations: Float32Array, localTranslations: Float32Array, worldMatrices: Float32Array, ikChainInfo: IKChainInfo[]): void;
17
+ static solve(ikSolvers: IKSolver[], bones: Bone[], localRotations: Quat[], localTranslations: Vec3[], worldMatrices: Mat4[], ikChainInfo: IKChainInfo[]): void;
17
18
  private static solveIK;
18
19
  private static solveChain;
19
20
  private static limitAngle;
20
21
  private static getDistance;
21
22
  private static getWorldTranslation;
22
- private static getQuatFromArray;
23
- private static setQuatToArray;
24
23
  private static extractEulerAngles;
25
24
  private static limitEulerAngles;
26
25
  private static reconstructQuatFromEuler;
@@ -1 +1 @@
1
- {"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAoE7D;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD;;OAEG;WACW,KAAK,CACjB,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,YAAY,EAC5B,iBAAiB,EAAE,YAAY,EAC/B,aAAa,EAAE,YAAY,EAC3B,WAAW,EAAE,WAAW,EAAE,GACzB,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IA4EtB,OAAO,CAAC,MAAM,CAAC,UAAU;IA2FzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAM1B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAKlC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAI/B,OAAO,CAAC,MAAM,CAAC,cAAc;IAO7B,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAmCjC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAQ/B,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAqBvC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAe3C,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,iBAAiB;CA2CjC"}
1
+ {"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAoE7D;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD;;OAEG;WACW,KAAK,CACjB,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,GACzB,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IA2EtB,OAAO,CAAC,MAAM,CAAC,UAAU;IA2FzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAM1B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAKlC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAmCjC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAQ/B,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAqBvC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAc3C,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAoCjC"}
package/dist/ik-solver.js CHANGED
@@ -122,10 +122,9 @@ export class IKSolverSystem {
122
122
  for (const link of solver.links) {
123
123
  const chainInfo = ikChainInfo[link.boneIndex];
124
124
  if (chainInfo?.ikRotation) {
125
- const qi = link.boneIndex * 4;
126
- const localRot = this.getQuatFromArray(localRotations, qi);
125
+ const localRot = localRotations[link.boneIndex];
127
126
  const finalRot = chainInfo.ikRotation.multiply(localRot).normalize();
128
- this.setQuatToArray(localRotations, qi, finalRot);
127
+ localRot.set(finalRot);
129
128
  }
130
129
  }
131
130
  }
@@ -182,14 +181,14 @@ export class IKSolverSystem {
182
181
  chainInfo.ikRotation = ikRotation.multiply(chainInfo.ikRotation);
183
182
  // Apply angle constraints if present
184
183
  if (chain.minimumAngle && chain.maximumAngle) {
185
- const qi = chainBoneIndex * 4;
186
- const localRot = this.getQuatFromArray(localRotations, qi);
184
+ const localRot = localRotations[chainBoneIndex];
187
185
  chainInfo.localRotation = localRot.clone();
188
186
  const combinedRot = chainInfo.ikRotation.multiply(localRot);
189
187
  const euler = this.extractEulerAngles(combinedRot, chain.rotationOrder);
190
188
  const limited = this.limitEulerAngles(euler, chain.minimumAngle, chain.maximumAngle, useAxis);
191
189
  chainInfo.ikRotation = this.reconstructQuatFromEuler(limited, chain.rotationOrder);
192
- chainInfo.ikRotation = chainInfo.ikRotation.multiply(localRot.conjugate().normalize());
190
+ // Clone localRot to avoid mutating, then conjugate and normalize
191
+ chainInfo.ikRotation = chainInfo.ikRotation.multiply(localRot.clone().conjugate().normalize());
193
192
  }
194
193
  }
195
194
  // Update world matrices for affected bones (using IK-modified rotations)
@@ -218,17 +217,8 @@ export class IKSolverSystem {
218
217
  return pos1.subtract(pos2).length();
219
218
  }
220
219
  static getWorldTranslation(boneIndex, worldMatrices) {
221
- const offset = boneIndex * 16;
222
- return new Vec3(worldMatrices[offset + 12], worldMatrices[offset + 13], worldMatrices[offset + 14]);
223
- }
224
- static getQuatFromArray(array, offset) {
225
- return new Quat(array[offset], array[offset + 1], array[offset + 2], array[offset + 3]);
226
- }
227
- static setQuatToArray(array, offset, quat) {
228
- array[offset] = quat.x;
229
- array[offset + 1] = quat.y;
230
- array[offset + 2] = quat.z;
231
- array[offset + 3] = quat.w;
220
+ const mat = worldMatrices[boneIndex];
221
+ return new Vec3(mat.values[12], mat.values[13], mat.values[14]);
232
222
  }
233
223
  static extractEulerAngles(quat, order) {
234
224
  const rotMatrix = Mat4.fromQuat(quat.x, quat.y, quat.z, quat.w);
@@ -292,8 +282,7 @@ export class IKSolverSystem {
292
282
  static getParentWorldRotationMatrix(boneIndex, bones, worldMatrices) {
293
283
  const bone = bones[boneIndex];
294
284
  if (bone.parentIndex >= 0) {
295
- const parentOffset = bone.parentIndex * 16;
296
- const parentMat = new Mat4(worldMatrices.subarray(parentOffset, parentOffset + 16));
285
+ const parentMat = worldMatrices[bone.parentIndex];
297
286
  // Remove translation
298
287
  const rotMat = Mat4.identity();
299
288
  const m = parentMat.values;
@@ -310,9 +299,8 @@ export class IKSolverSystem {
310
299
  }
311
300
  static updateWorldMatrix(boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo) {
312
301
  const bone = bones[boneIndex];
313
- const qi = boneIndex * 4;
314
- const ti = boneIndex * 3;
315
- const localRot = this.getQuatFromArray(localRotations, qi);
302
+ const localRot = localRotations[boneIndex];
303
+ const localTrans = localTranslations[boneIndex];
316
304
  // Apply IK rotation if available
317
305
  let finalRot = localRot;
318
306
  if (ikChainInfo) {
@@ -322,22 +310,18 @@ export class IKSolverSystem {
322
310
  }
323
311
  }
324
312
  const rotateM = Mat4.fromQuat(finalRot.x, finalRot.y, finalRot.z, finalRot.w);
325
- const localTx = localTranslations[ti];
326
- const localTy = localTranslations[ti + 1];
327
- const localTz = localTranslations[ti + 2];
328
313
  const localM = Mat4.identity()
329
314
  .translateInPlace(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2])
330
315
  .multiply(rotateM)
331
- .translateInPlace(localTx, localTy, localTz);
332
- const worldOffset = boneIndex * 16;
316
+ .translateInPlace(localTrans.x, localTrans.y, localTrans.z);
317
+ const worldMat = worldMatrices[boneIndex];
333
318
  if (bone.parentIndex >= 0) {
334
- const parentOffset = bone.parentIndex * 16;
335
- const parentMat = new Mat4(worldMatrices.subarray(parentOffset, parentOffset + 16));
336
- const worldMat = parentMat.multiply(localM);
337
- worldMatrices.subarray(worldOffset, worldOffset + 16).set(worldMat.values);
319
+ const parentMat = worldMatrices[bone.parentIndex];
320
+ const result = parentMat.multiply(localM);
321
+ worldMat.values.set(result.values);
338
322
  }
339
323
  else {
340
- worldMatrices.subarray(worldOffset, worldOffset + 16).set(localM.values);
324
+ worldMat.values.set(localM.values);
341
325
  }
342
326
  }
343
327
  }
package/dist/math.d.ts CHANGED
@@ -4,6 +4,7 @@ export declare class Vec3 {
4
4
  y: number;
5
5
  z: number;
6
6
  constructor(x: number, y: number, z: number);
7
+ static zeros(): Vec3;
7
8
  add(other: Vec3): Vec3;
8
9
  subtract(other: Vec3): Vec3;
9
10
  length(): number;
@@ -11,6 +12,7 @@ export declare class Vec3 {
11
12
  cross(other: Vec3): Vec3;
12
13
  dot(other: Vec3): number;
13
14
  scale(scalar: number): Vec3;
15
+ set(other: Vec3): Vec3;
14
16
  }
15
17
  export declare class Quat {
16
18
  x: number;
@@ -18,6 +20,7 @@ export declare class Quat {
18
20
  z: number;
19
21
  w: number;
20
22
  constructor(x: number, y: number, z: number, w: number);
23
+ static identity(): Quat;
21
24
  add(other: Quat): Quat;
22
25
  clone(): Quat;
23
26
  multiply(other: Quat): Quat;
@@ -26,6 +29,7 @@ export declare class Quat {
26
29
  normalize(): Quat;
27
30
  static fromAxisAngle(axis: Vec3, angle: number): Quat;
28
31
  toArray(): [number, number, number, number];
32
+ set(other: Quat): Quat;
29
33
  static slerp(a: Quat, b: Quat, t: number): Quat;
30
34
  static fromEuler(rotX: number, rotY: number, rotZ: number): Quat;
31
35
  }
@@ -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":"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"}
package/dist/math.js CHANGED
@@ -8,6 +8,9 @@ export class Vec3 {
8
8
  this.y = y;
9
9
  this.z = z;
10
10
  }
11
+ static zeros() {
12
+ return new Vec3(0, 0, 0);
13
+ }
11
14
  add(other) {
12
15
  return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
13
16
  }
@@ -17,11 +20,21 @@ export class Vec3 {
17
20
  length() {
18
21
  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
19
22
  }
23
+ // Normalize this vector in-place (mutates this object)
20
24
  normalize() {
21
25
  const len = this.length();
22
- if (len === 0)
23
- return new Vec3(0, 0, 0);
24
- return new Vec3(this.x / len, this.y / len, this.z / len);
26
+ if (len === 0) {
27
+ this.x = 0;
28
+ this.y = 0;
29
+ this.z = 0;
30
+ }
31
+ else {
32
+ const invLen = 1 / len;
33
+ this.x *= invLen;
34
+ this.y *= invLen;
35
+ this.z *= invLen;
36
+ }
37
+ return this;
25
38
  }
26
39
  cross(other) {
27
40
  return new Vec3(this.y * other.z - this.z * other.y, this.z * other.x - this.x * other.z, this.x * other.y - this.y * other.x);
@@ -32,6 +45,13 @@ export class Vec3 {
32
45
  scale(scalar) {
33
46
  return new Vec3(this.x * scalar, this.y * scalar, this.z * scalar);
34
47
  }
48
+ // Set this vector's components from another vector (in-place mutation)
49
+ set(other) {
50
+ this.x = other.x;
51
+ this.y = other.y;
52
+ this.z = other.z;
53
+ return this;
54
+ }
35
55
  }
36
56
  export class Quat {
37
57
  constructor(x, y, z, w) {
@@ -40,6 +60,9 @@ export class Quat {
40
60
  this.z = z;
41
61
  this.w = w;
42
62
  }
63
+ static identity() {
64
+ return new Quat(0, 0, 0, 1);
65
+ }
43
66
  add(other) {
44
67
  return new Quat(this.x + other.x, this.y + other.y, this.z + other.z, this.w + other.w);
45
68
  }
@@ -50,30 +73,62 @@ export class Quat {
50
73
  // Proper quaternion multiplication (not component-wise)
51
74
  return new Quat(this.w * other.x + this.x * other.w + this.y * other.z - this.z * other.y, this.w * other.y - this.x * other.z + this.y * other.w + this.z * other.x, this.w * other.z + this.x * other.y - this.y * other.x + this.z * other.w, this.w * other.w - this.x * other.x - this.y * other.y - this.z * other.z);
52
75
  }
76
+ // Conjugate this quaternion in-place (mutates this object)
77
+ // Conjugate (inverse for unit quaternions): (x, y, z, w) -> (-x, -y, -z, w)
53
78
  conjugate() {
54
- // Conjugate (inverse for unit quaternions): (x, y, z, w) -> (-x, -y, -z, w)
55
- return new Quat(-this.x, -this.y, -this.z, this.w);
79
+ this.x = -this.x;
80
+ this.y = -this.y;
81
+ this.z = -this.z;
82
+ return this;
56
83
  }
57
84
  length() {
58
85
  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
59
86
  }
87
+ // Normalize this quaternion in-place (mutates this object)
60
88
  normalize() {
61
89
  const len = this.length();
62
- if (len === 0)
63
- return new Quat(0, 0, 0, 1);
64
- return new Quat(this.x / len, this.y / len, this.z / len, this.w / len);
90
+ if (len === 0) {
91
+ this.x = 0;
92
+ this.y = 0;
93
+ this.z = 0;
94
+ this.w = 1;
95
+ }
96
+ else {
97
+ const invLen = 1 / len;
98
+ this.x *= invLen;
99
+ this.y *= invLen;
100
+ this.z *= invLen;
101
+ this.w *= invLen;
102
+ }
103
+ return this;
65
104
  }
66
105
  // Static method: create quaternion from rotation axis and angle
67
106
  static fromAxisAngle(axis, angle) {
68
- const normalizedAxis = axis.normalize();
107
+ // Clone to avoid mutating input, then normalize
108
+ const nx = axis.x;
109
+ const ny = axis.y;
110
+ const nz = axis.z;
111
+ const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
112
+ const invLen = len > 0 ? 1 / len : 0;
113
+ const normalizedX = nx * invLen;
114
+ const normalizedY = ny * invLen;
115
+ const normalizedZ = nz * invLen;
69
116
  const halfAngle = angle * 0.5;
70
117
  const sinHalf = Math.sin(halfAngle);
71
118
  const cosHalf = Math.cos(halfAngle);
72
- return new Quat(normalizedAxis.x * sinHalf, normalizedAxis.y * sinHalf, normalizedAxis.z * sinHalf, cosHalf);
119
+ return new Quat(normalizedX * sinHalf, normalizedY * sinHalf, normalizedZ * sinHalf, cosHalf);
73
120
  }
74
121
  toArray() {
75
122
  return [this.x, this.y, this.z, this.w];
76
123
  }
124
+ // Set this quaternion's components from another quaternion (in-place mutation)
125
+ set(other) {
126
+ this.x = other.x;
127
+ this.y = other.y;
128
+ this.z = other.z;
129
+ this.w = other.w;
130
+ return this;
131
+ }
77
132
  // Spherical linear interpolation between two quaternions
78
133
  static slerp(a, b, t) {
79
134
  let cos = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
@@ -153,9 +208,13 @@ export class Mat4 {
153
208
  // For left-handed: camera looks along +Z direction
154
209
  static lookAt(eye, target, up) {
155
210
  // In left-handed: forward = target - eye (Z+ direction)
156
- const forward = target.subtract(eye).normalize();
157
- const right = up.cross(forward).normalize(); // X+ is right
158
- const upVec = forward.cross(right).normalize(); // Y+ is up
211
+ // These operations create new Vec3 objects, so normalize() mutates those new objects
212
+ const forward = target.subtract(eye);
213
+ forward.normalize();
214
+ const right = up.cross(forward);
215
+ right.normalize(); // X+ is right
216
+ const upVec = forward.cross(right);
217
+ upVec.normalize(); // Y+ is up
159
218
  return new Mat4(new Float32Array([
160
219
  right.x,
161
220
  upVec.x,
package/dist/model.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Quat, Vec3 } from "./math";
1
+ import { Mat4, Quat, Vec3 } from "./math";
2
2
  import { Rigidbody, Joint } from "./physics";
3
3
  export interface Texture {
4
4
  path: string;
@@ -83,9 +83,9 @@ export interface Morphing {
83
83
  }
84
84
  export interface SkeletonRuntime {
85
85
  nameIndex: Record<string, number>;
86
- localRotations: Float32Array;
87
- localTranslations: Float32Array;
88
- worldMatrices: Float32Array;
86
+ localRotations: Quat[];
87
+ localTranslations: Vec3[];
88
+ worldMatrices: Mat4[];
89
89
  ikChainInfo?: IKChainInfo[];
90
90
  ikSolvers?: IKSolver[];
91
91
  }
@@ -110,7 +110,7 @@ export declare class Model {
110
110
  private morphsDirty;
111
111
  private cachedIdentityMat1;
112
112
  private cachedIdentityMat2;
113
- private cachedSkinMatrices?;
113
+ private skinMatricesArray?;
114
114
  private tweenState;
115
115
  private tweenTimeMs;
116
116
  private animationData;