reze-engine 0.9.5 → 0.10.1

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
@@ -2,6 +2,8 @@
2
2
 
3
3
  A minimal-dependency WebGPU engine for real-time MMD/PMX rendering. Only external dependency is Ammo.js for physics.
4
4
 
5
+ ![screenshot](./screenshot.png)
6
+
5
7
  ## Install
6
8
 
7
9
  ```bash
@@ -14,76 +16,113 @@ npm install reze-engine
14
16
  - VMD animation with IK solver and Bullet physics
15
17
  - Orbit camera with bone-follow mode
16
18
  - GPU picking (double-click/tap)
17
- - Ground plane with PCF shadow mapping, grid lines, and frosted texture
19
+ - Ground plane with PCF shadow mapping
18
20
  - Multi-model support
19
21
 
20
- ## Quick Start
22
+ ## Usage
21
23
 
22
24
  ```javascript
23
- import { Engine, Vec3 } from "reze-engine"
25
+ import { Engine, Vec3 } from "reze-engine";
24
26
 
25
27
  const engine = new Engine(canvas, {
26
28
  ambientColor: new Vec3(0.88, 0.92, 0.99),
27
29
  cameraDistance: 31.5, // MMD units (1 unit = 8 cm)
28
- })
29
- await engine.init()
30
-
31
- const model = await engine.loadModel("hero", "/models/hero/hero.pmx")
32
- await model.loadAnimation("idle", "/animations/idle.vmd")
33
- model.show("idle")
34
- model.play()
35
-
36
- engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0))
37
- engine.addGround({ width: 160, height: 160 })
38
- engine.runRenderLoop()
30
+ cameraTarget: new Vec3(0, 11.5, 0),
31
+ });
32
+ await engine.init();
33
+
34
+ const model = await engine.loadModel("hero", "/models/hero/hero.pmx");
35
+ await model.loadVmd("idle", "/animations/idle.vmd");
36
+ model.show("idle");
37
+ model.play();
38
+
39
+ engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0));
40
+ engine.addGround({ width: 160, height: 160 });
41
+ engine.runRenderLoop();
39
42
  ```
40
43
 
41
44
  ## API
42
45
 
46
+ One WebGPU **Engine** per page (singleton after `init()`). Load models via `engine.loadModel(path)` or `engine.loadModel(name, path)`.
47
+
43
48
  ### Engine
44
49
 
45
- | Method | Description |
46
- |--------|-------------|
47
- | `new Engine(canvas, options?)` | Create engine with optional config |
48
- | `engine.init()` | Initialize WebGPU device and context |
49
- | `engine.loadModel(path)` | Load PMX model (auto-named) |
50
- | `engine.loadModel(name, path)` | Load PMX model with name |
51
- | `engine.getModel(name)` | Get model by name |
52
- | `engine.getModelNames()` | List all model names |
53
- | `engine.removeModel(name)` | Remove model |
54
- | `engine.setMaterialVisible(model, mat, visible)` | Show/hide material |
55
- | `engine.toggleMaterialVisible(model, mat)` | Toggle material visibility |
56
- | `engine.setIKEnabled(enabled)` | Enable/disable IK globally |
57
- | `engine.setPhysicsEnabled(enabled)` | Enable/disable physics globally |
58
- | `engine.setCameraFollow(model, bone?, offset?)` | Orbit center tracks a bone |
59
- | `engine.setCameraTarget(vec3)` | Static camera target |
60
- | `engine.setCameraDistance(d)` | Set orbit radius |
61
- | `engine.setCameraAlpha(a)` | Set horizontal orbit angle |
62
- | `engine.setCameraBeta(b)` | Set vertical orbit angle |
63
- | `engine.addGround(options?)` | Add ground plane with shadows |
64
- | `engine.runRenderLoop(callback?)` | Start render loop |
65
- | `engine.stopRenderLoop()` | Stop render loop |
66
- | `engine.getStats()` | Returns `{ fps, frameTime }` |
67
- | `engine.dispose()` | Clean up all resources |
50
+ ```javascript
51
+ engine.init()
52
+ engine.loadModel(name, path)
53
+ engine.getModel(name)
54
+ engine.getModelNames()
55
+ engine.removeModel(name)
56
+
57
+ engine.setMaterialVisible(name, material, visible)
58
+ engine.toggleMaterialVisible(name, material)
59
+ engine.isMaterialVisible(name, material)
60
+
61
+ engine.setIKEnabled(enabled)
62
+ engine.setPhysicsEnabled(enabled)
63
+
64
+ engine.setCameraFollow(model, bone?, offset?)
65
+ engine.setCameraFollow(null)
66
+ engine.setCameraTarget(vec3)
67
+ engine.setCameraDistance(d)
68
+ engine.setCameraAlpha(a)
69
+ engine.setCameraBeta(b)
70
+
71
+ engine.addGround(options?)
72
+ engine.runRenderLoop(callback?)
73
+ engine.stopRenderLoop()
74
+ engine.getStats()
75
+ engine.dispose()
76
+ ```
68
77
 
69
78
  ### Model
70
79
 
71
- | Method | Description |
72
- |--------|-------------|
73
- | `model.loadAnimation(name, url)` | Load VMD animation |
74
- | `model.show(name)` | Set pose at time 0 |
75
- | `model.play(name?)` | Play animation (queued if busy) |
76
- | `model.pause()` | Pause playback |
77
- | `model.stop()` | Stop playback |
78
- | `model.seek(time)` | Seek to time |
79
- | `model.getAnimationProgress()` | `{ current, duration, percentage, animationName }` |
80
- | `model.getAnimationState()` | Access animation controller |
81
- | `model.rotateBones(rotations, ms?)` | Tween bone rotations |
82
- | `model.moveBones(translations, ms?)` | Tween bone translations |
83
- | `model.setMorphWeight(name, weight, ms?)` | Tween morph weight |
84
- | `model.resetAllBones()` | Reset to bind pose |
85
- | `model.resetAllMorphs()` | Reset all morph weights |
86
- | `model.getBoneWorldPosition(name)` | World position of bone |
80
+ ```javascript
81
+ await model.loadVmd(name, url)
82
+ model.loadClip(name, clip)
83
+ model.show(name)
84
+ model.play(name)
85
+ model.play(name, { priority: 8 }) // higher number = higher priority (0 default/lowest)
86
+ model.play(name, { loop: true }) // repeat until stop/pause or another play
87
+ model.pause()
88
+ model.stop()
89
+ model.seek(time)
90
+ model.getAnimationProgress()
91
+ model.getClip(name)
92
+ model.exportVmd(name) // returns ArrayBuffer
93
+
94
+ model.rotateBones({ 首: quat, 頭: quat }, ms?)
95
+ model.moveBones({ センター: vec3 }, ms?)
96
+ model.setMorphWeight(name, weight, ms?)
97
+ model.resetAllBones()
98
+ model.resetAllMorphs()
99
+ model.getBoneWorldPosition(name)
100
+ ```
101
+
102
+ #### Animation data
103
+
104
+ `AnimationClip` holds keyframes only: bone/morph tracks keyed by `frame`, and `frameCount` (last keyframe index). Time advances at fixed `FPS` (see package export `FPS`, default 30).
105
+
106
+ #### VMD Export
107
+
108
+ `model.exportVmd(name)` serialises a loaded clip back to the VMD binary format and returns an `ArrayBuffer`. Bone and morph names are Shift-JIS encoded for compatibility with standard MMD tools.
109
+
110
+ ```javascript
111
+ const buffer = model.exportVmd("idle")
112
+ const blob = new Blob([buffer], { type: "application/octet-stream" })
113
+ const link = document.createElement("a")
114
+ link.href = URL.createObjectURL(blob)
115
+ link.download = "idle.vmd"
116
+ link.click()
117
+ ```
118
+
119
+ #### Playback
120
+
121
+ Call `model.play(name, options?)` to start or switch motion. `loop: true` makes the playhead wrap at the end of the clip until you stop, pause, or call `play` with something else. `priority` chooses which request wins when several clips compete.
122
+
123
+ #### Progress
124
+
125
+ `getAnimationProgress()` reports `current` and `duration` in seconds, plus `playing`, `paused`, `looping`, and related fields.
87
126
 
88
127
  ### Engine Options
89
128
 
@@ -108,25 +147,6 @@ engine.runRenderLoop()
108
147
 
109
148
  `constraintSolverKeywords` — joints whose name contains any keyword use the Bullet 2.75 constraint solver; all others keep the stable Ammo 2.82+ default. See [babylon-mmd: Fix Constraint Behavior](https://noname0310.github.io/babylon-mmd/docs/reference/runtime/apply-physics-to-mmd-models/#fix-constraint-behavior) for details.
110
149
 
111
- ### Ground Options
112
-
113
- ```javascript
114
- engine.addGround({
115
- width: 100, // ground plane width
116
- height: 100, // ground plane depth
117
- diffuseColor: Vec3, // base color (default: 0.8, 0.1, 1.0)
118
- fadeStart: 5.0, // distance where edge fade begins
119
- fadeEnd: 60.0, // distance where ground fully fades out
120
- shadowMapSize: 4096, // shadow map resolution
121
- shadowStrength: 1.0, // shadow darkness
122
- gridSpacing: 5.0, // world-space distance between grid lines
123
- gridLineWidth: 0.012, // thickness of grid lines
124
- gridLineOpacity: 0.4, // grid line visibility (0–1)
125
- gridLineColor: Vec3, // grid line color (default: 0.8, 0.8, 0.8)
126
- noiseStrength: 0.08, // frosted/matte micro-texture intensity
127
- })
128
- ```
129
-
130
150
  ## Projects Using This Engine
131
151
 
132
152
  - **[MiKaPo](https://mikapo.vercel.app)** — Real-time motion capture for MMD
@@ -15,31 +15,46 @@ export interface BoneKeyframe {
15
15
  rotation: Quat;
16
16
  translation: Vec3;
17
17
  interpolation: BoneInterpolation;
18
- time: number;
19
18
  }
20
19
  export interface MorphKeyframe {
21
20
  morphName: string;
22
21
  frame: number;
23
22
  weight: number;
24
- time: number;
25
23
  }
26
24
  export interface AnimationClip {
27
25
  boneTracks: Map<string, BoneKeyframe[]>;
28
26
  morphTracks: Map<string, MorphKeyframe[]>;
29
- duration: number;
27
+ frameCount: number;
28
+ }
29
+ export interface AnimationPlayOptions {
30
+ priority?: number;
30
31
  loop?: boolean;
31
32
  }
33
+ /** Wall-clock playback progress; `current`/`duration` are seconds (clip span uses `AnimationClip.frameCount`, not `duration`). */
34
+ export interface AnimationProgress {
35
+ animationName: string | null;
36
+ current: number;
37
+ duration: number;
38
+ percentage: number;
39
+ looping: boolean;
40
+ /** True while the timeline is advancing (not idle at end, not paused). */
41
+ playing: boolean;
42
+ paused: boolean;
43
+ }
44
+ export declare const FPS = 30;
32
45
  export declare class AnimationState {
33
46
  private animations;
34
47
  private currentAnimationName;
35
- private currentTime;
48
+ private currentFrame;
49
+ private currentPriority;
50
+ private currentLoop;
36
51
  private isPlaying;
37
52
  private isPaused;
38
- private nextAnimationName;
53
+ private nextAnimation;
39
54
  private onEnd;
40
55
  loadAnimation(name: string, clip: AnimationClip): void;
41
56
  removeAnimation(name: string): void;
42
- play(name: string): boolean;
57
+ play(name: string, options?: AnimationPlayOptions): boolean;
43
58
  play(): void;
44
59
  update(deltaTime: number): {
45
60
  ended: boolean;
@@ -47,17 +62,15 @@ export declare class AnimationState {
47
62
  };
48
63
  pause(): void;
49
64
  stop(): void;
50
- seek(time: number): void;
65
+ seek(seconds: number): void;
51
66
  getCurrentClip(): AnimationClip | null;
67
+ getAnimationClip(name: string): AnimationClip | null;
52
68
  getCurrentAnimation(): string | null;
53
69
  getCurrentTime(): number;
70
+ getCurrentFrame(): number;
71
+ /** Clip length in seconds (`frameCount / FPS`). */
54
72
  getDuration(): number;
55
- getProgress(): {
56
- animationName: string | null;
57
- current: number;
58
- duration: number;
59
- percentage: number;
60
- };
73
+ getProgress(): AnimationProgress;
61
74
  getAnimationNames(): string[];
62
75
  hasAnimation(name: string): boolean;
63
76
  show(name: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"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"}
package/dist/animation.js CHANGED
@@ -1,30 +1,39 @@
1
- // Non-interruptible playback; play(name) while playing queues one next.
1
+ export const FPS = 30;
2
+ // Priority-aware playback: higher priority preempts, otherwise latest request is queued.
2
3
  export class AnimationState {
3
4
  constructor() {
4
5
  this.animations = new Map();
5
6
  this.currentAnimationName = null;
6
- this.currentTime = 0;
7
+ this.currentFrame = 0;
8
+ this.currentPriority = 0;
9
+ this.currentLoop = false;
7
10
  this.isPlaying = false;
8
11
  this.isPaused = false;
9
- this.nextAnimationName = null;
12
+ this.nextAnimation = null;
10
13
  this.onEnd = null;
11
14
  }
12
15
  loadAnimation(name, clip) {
13
- this.animations.set(name, clip);
16
+ this.animations.set(name, {
17
+ boneTracks: clip.boneTracks,
18
+ morphTracks: clip.morphTracks,
19
+ frameCount: clip.frameCount,
20
+ });
14
21
  }
15
22
  removeAnimation(name) {
16
23
  this.animations.delete(name);
17
24
  if (this.currentAnimationName === name) {
18
25
  this.currentAnimationName = null;
19
- this.currentTime = 0;
26
+ this.currentFrame = 0;
27
+ this.currentPriority = 0;
28
+ this.currentLoop = false;
20
29
  this.isPlaying = false;
21
- this.nextAnimationName = this.nextAnimationName === name ? null : this.nextAnimationName;
30
+ this.nextAnimation = this.nextAnimation?.name === name ? null : this.nextAnimation;
22
31
  }
23
- else if (this.nextAnimationName === name) {
24
- this.nextAnimationName = null;
32
+ else if (this.nextAnimation?.name === name) {
33
+ this.nextAnimation = null;
25
34
  }
26
35
  }
27
- play(name) {
36
+ play(name, options) {
28
37
  if (name === undefined) {
29
38
  if (this.currentAnimationName && this.animations.has(this.currentAnimationName)) {
30
39
  this.isPaused = false;
@@ -34,15 +43,37 @@ export class AnimationState {
34
43
  }
35
44
  if (!this.animations.has(name))
36
45
  return false;
46
+ const priority = options?.priority ?? 0;
47
+ const loop = options?.loop ?? false;
48
+ if (this.currentAnimationName === name) {
49
+ this.currentFrame = 0;
50
+ this.currentPriority = priority;
51
+ this.currentLoop = loop;
52
+ this.isPlaying = true;
53
+ this.isPaused = false;
54
+ return true;
55
+ }
37
56
  if (this.isPlaying && !this.isPaused) {
38
- this.nextAnimationName = name;
57
+ if (priority > this.currentPriority) {
58
+ this.currentAnimationName = name;
59
+ this.currentFrame = 0;
60
+ this.currentPriority = priority;
61
+ this.currentLoop = loop;
62
+ this.isPlaying = true;
63
+ this.isPaused = false;
64
+ this.nextAnimation = null;
65
+ return true;
66
+ }
67
+ this.nextAnimation = { name, priority, loop };
39
68
  return true;
40
69
  }
41
70
  this.currentAnimationName = name;
42
- this.currentTime = 0;
71
+ this.currentFrame = 0;
72
+ this.currentPriority = priority;
73
+ this.currentLoop = loop;
43
74
  this.isPlaying = true;
44
75
  this.isPaused = false;
45
- this.nextAnimationName = null;
76
+ this.nextAnimation = null;
46
77
  return true;
47
78
  }
48
79
  update(deltaTime) {
@@ -52,21 +83,28 @@ export class AnimationState {
52
83
  const clip = this.animations.get(this.currentAnimationName);
53
84
  if (!clip)
54
85
  return { ended: false, animationName: this.currentAnimationName };
55
- this.currentTime += deltaTime;
56
- const duration = clip.duration;
57
- if (this.currentTime >= duration) {
58
- this.currentTime = duration;
59
- if (clip.loop) {
60
- this.currentTime = 0;
86
+ const frameCount = clip.frameCount;
87
+ if (frameCount <= 0 || !Number.isFinite(frameCount)) {
88
+ return { ended: false, animationName: this.currentAnimationName };
89
+ }
90
+ this.currentFrame += deltaTime * FPS;
91
+ if (this.currentFrame >= frameCount) {
92
+ if (this.currentLoop) {
93
+ while (this.currentFrame >= frameCount) {
94
+ this.currentFrame -= frameCount;
95
+ }
61
96
  return { ended: false, animationName: this.currentAnimationName };
62
97
  }
98
+ this.currentFrame = frameCount;
63
99
  const finishedName = this.currentAnimationName;
64
100
  this.onEnd?.(finishedName);
65
- if (this.nextAnimationName !== null) {
66
- const next = this.nextAnimationName;
67
- this.nextAnimationName = null;
68
- this.currentAnimationName = next;
69
- this.currentTime = 0;
101
+ if (this.nextAnimation !== null) {
102
+ const next = this.nextAnimation;
103
+ this.nextAnimation = null;
104
+ this.currentAnimationName = next.name;
105
+ this.currentFrame = 0;
106
+ this.currentPriority = next.priority;
107
+ this.currentLoop = next.loop;
70
108
  this.isPlaying = true;
71
109
  this.isPaused = false;
72
110
  return { ended: true, animationName: finishedName };
@@ -82,37 +120,57 @@ export class AnimationState {
82
120
  stop() {
83
121
  this.isPlaying = false;
84
122
  this.isPaused = false;
85
- this.currentTime = 0;
86
- this.nextAnimationName = null;
123
+ this.currentFrame = 0;
124
+ this.currentPriority = 0;
125
+ this.currentLoop = false;
126
+ this.nextAnimation = null;
87
127
  }
88
- seek(time) {
128
+ // Seek by absolute timeline seconds, not frame index.
129
+ seek(seconds) {
89
130
  const clip = this.getCurrentClip();
90
- if (!clip)
131
+ if (!clip || clip.frameCount <= 0 || !Number.isFinite(clip.frameCount))
91
132
  return;
92
- this.currentTime = Math.max(0, Math.min(time, clip.duration));
133
+ const targetFrame = seconds * FPS;
134
+ this.currentFrame = Math.max(0, Math.min(targetFrame, clip.frameCount));
93
135
  }
94
136
  getCurrentClip() {
95
137
  return this.currentAnimationName !== null ? this.animations.get(this.currentAnimationName) ?? null : null;
96
138
  }
139
+ getAnimationClip(name) {
140
+ return this.animations.get(name) ?? null;
141
+ }
97
142
  getCurrentAnimation() {
98
143
  return this.currentAnimationName;
99
144
  }
100
145
  getCurrentTime() {
101
- return this.currentTime;
146
+ const clip = this.getCurrentClip();
147
+ if (!clip)
148
+ return 0;
149
+ return this.currentFrame / FPS;
150
+ }
151
+ getCurrentFrame() {
152
+ return this.currentFrame;
102
153
  }
154
+ /** Clip length in seconds (`frameCount / FPS`). */
103
155
  getDuration() {
104
156
  const clip = this.getCurrentClip();
105
- return clip ? clip.duration : 0;
157
+ if (!clip || clip.frameCount <= 0 || !Number.isFinite(clip.frameCount))
158
+ return 0;
159
+ return clip.frameCount / FPS;
106
160
  }
107
161
  getProgress() {
108
162
  const clip = this.getCurrentClip();
109
- const duration = clip ? clip.duration : 0;
110
- const percentage = duration > 0 ? (this.currentTime / duration) * 100 : 0;
163
+ const duration = clip && clip.frameCount > 0 ? clip.frameCount / FPS : 0;
164
+ const current = clip ? this.currentFrame / FPS : 0;
165
+ const percentage = duration > 0 ? (current / duration) * 100 : 0;
111
166
  return {
112
167
  animationName: this.currentAnimationName,
113
- current: this.currentTime,
168
+ current,
114
169
  duration,
115
170
  percentage,
171
+ looping: this.currentLoop,
172
+ playing: this.isPlaying && !this.isPaused,
173
+ paused: this.isPaused,
116
174
  };
117
175
  }
118
176
  getAnimationNames() {
@@ -125,10 +183,12 @@ export class AnimationState {
125
183
  if (!this.animations.has(name))
126
184
  return;
127
185
  this.currentAnimationName = name;
128
- this.currentTime = 0;
186
+ this.currentFrame = 0;
187
+ this.currentPriority = 0;
188
+ this.currentLoop = false;
129
189
  this.isPlaying = false;
130
190
  this.isPaused = false;
131
- this.nextAnimationName = null;
191
+ this.nextAnimation = null;
132
192
  }
133
193
  setOnEnd(callback) {
134
194
  this.onEnd = callback;
package/dist/engine.d.ts CHANGED
@@ -132,7 +132,6 @@ export declare class Engine {
132
132
  setCameraBeta(b: number): void;
133
133
  private setupLighting;
134
134
  private setAmbientColor;
135
- clearLights(): void;
136
135
  private addLight;
137
136
  addGround(options?: {
138
137
  width?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/engine.js CHANGED
@@ -623,7 +623,7 @@ export class Engine {
623
623
  // Screen-stable edgeline: extrusion ∝ camera distance (same idea as MMD viewers / babylon-mmd-style scaling)
624
624
  let camDist = max(length(camera.viewPos - worldPos), 0.25);
625
625
  let refDist = 30.0;
626
- let edgeScale = 0.032;
626
+ let edgeScale = 0.025;
627
627
  let expandedPos = worldPos + worldNormal * material.edgeSize * edgeScale * (camDist / refDist);
628
628
  output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
629
629
  return output;
@@ -880,15 +880,6 @@ export class Engine {
880
880
  this.lightData[3] = this.minSpecularIntensity; // ambientColor.w = minSpecularIntensity
881
881
  this.updateLightBuffer();
882
882
  }
883
- clearLights() {
884
- this.lightCount = 0;
885
- // Clear all light data by setting intensity to 0
886
- for (let i = 0; i < 4; i++) {
887
- const baseIndex = 4 + i * 8;
888
- this.lightData[baseIndex + 7] = 0; // color.w / intensity
889
- }
890
- this.updateLightBuffer();
891
- }
892
883
  addLight(direction, color, intensity = 1.0) {
893
884
  if (this.lightCount >= 4)
894
885
  return false;
@@ -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;AAoE/E,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;WAE1C,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,EAC1B,iBAAiB,CAAC,EAAE,mBAAmB,GACtC,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IAoFtB,OAAO,CAAC,MAAM,CAAC,UAAU;IAqGzB,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"}
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;AAoE/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;IAoFtB,OAAO,CAAC,MAAM,CAAC,UAAU;IAqGzB,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/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export { Engine, type EngineStats } from "./engine";
2
2
  export { Model } from "./model";
3
3
  export { Vec3, Quat, Mat4 } from "./math";
4
- export { AnimationState, type AnimationClip, type BoneKeyframe, type MorphKeyframe, } from "./animation";
4
+ export type { AnimationClip, AnimationPlayOptions, AnimationProgress, BoneKeyframe, MorphKeyframe, BoneInterpolation, ControlPoint, } from "./animation";
5
+ export { FPS } from "./animation";
5
6
  export { Physics, type PhysicsOptions } from "./physics";
7
+ export { VMDWriter } from "./vmd-writer";
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,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,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { Engine } from "./engine";
2
2
  export { Model } from "./model";
3
3
  export { Vec3, Quat, Mat4 } from "./math";
4
- export { AnimationState, } from "./animation";
4
+ export { FPS } from "./animation";
5
5
  export { Physics } from "./physics";
6
+ export { VMDWriter } from "./vmd-writer";
package/dist/model.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Mat4, Quat, Vec3 } from "./math";
2
2
  import { Rigidbody, Joint } from "./physics";
3
- import { AnimationState } from "./animation";
3
+ import { AnimationClip, AnimationPlayOptions, AnimationProgress } from "./animation";
4
4
  export interface Texture {
5
5
  path: string;
6
6
  name: string;
@@ -145,26 +145,24 @@ export declare class Model {
145
145
  setMorphWeight(name: string, weight: number, durationMs?: number): void;
146
146
  private applyMorphs;
147
147
  private buildClipFromVmdKeyFrames;
148
- loadAnimation(animationName: string, vmdUrl: string): Promise<void>;
148
+ loadVmd(name: string, url: string): Promise<void>;
149
+ loadClip(name: string, clip: AnimationClip): void;
149
150
  resetAllBones(): void;
150
151
  resetAllMorphs(): void;
151
- getAnimationState(): AnimationState;
152
+ getClip(name: string): AnimationClip | null;
153
+ exportVmd(name: string): ArrayBuffer;
152
154
  play(): void;
153
155
  play(name: string): boolean;
156
+ play(name: string, options?: AnimationPlayOptions): boolean;
154
157
  show(name: string): void;
155
158
  playAnimation(): void;
156
159
  pause(): void;
157
160
  pauseAnimation(): void;
158
161
  stop(): void;
159
162
  stopAnimation(): void;
160
- seek(time: number): void;
161
- seekAnimation(time: number): void;
162
- getAnimationProgress(): {
163
- current: number;
164
- duration: number;
165
- percentage: number;
166
- animationName: string | null;
167
- };
163
+ seek(seconds: number): void;
164
+ seekAnimation(seconds: number): void;
165
+ getAnimationProgress(): AnimationProgress;
168
166
  private static upperBound;
169
167
  private findKeyframeIndex;
170
168
  private applyPoseFromClip;