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/dist/player.d.ts DELETED
@@ -1,64 +0,0 @@
1
- import { Quat, Vec3 } from "./math";
2
- export interface AnimationPose {
3
- boneRotations: Map<string, Quat>;
4
- boneTranslations: Map<string, Vec3>;
5
- morphWeights: Map<string, number>;
6
- }
7
- export interface AnimationProgress {
8
- current: number;
9
- duration: number;
10
- percentage: number;
11
- }
12
- export declare class Player {
13
- private frames;
14
- private boneTracks;
15
- private morphTracks;
16
- private _duration;
17
- private isPlaying;
18
- private isPaused;
19
- private _currentTime;
20
- private startTime;
21
- /**
22
- * Load VMD animation file
23
- */
24
- loadVmd(vmdUrl: string): Promise<void>;
25
- /**
26
- * Process frames into tracks
27
- */
28
- private processFrames;
29
- /**
30
- * Start or resume playback
31
- * Note: For iOS, this should be called synchronously from a user interaction event
32
- */
33
- play(): void;
34
- /**
35
- * Pause playback
36
- */
37
- pause(): void;
38
- /**
39
- * Stop playback and reset to beginning
40
- */
41
- stop(): void;
42
- /**
43
- * Seek to specific time
44
- */
45
- seek(time: number): void;
46
- /**
47
- * Update playback and return current pose
48
- * Returns null if not playing, but returns current pose if paused
49
- */
50
- update(currentRealTime: number): AnimationPose | null;
51
- /**
52
- * Get pose at specific time (pure function)
53
- */
54
- getPoseAtTime(time: number): AnimationPose;
55
- /**
56
- * Get current playback progress
57
- */
58
- getProgress(): AnimationProgress;
59
- get currentTime(): number;
60
- get duration(): number;
61
- get isPlayingState(): boolean;
62
- get isPausedState(): boolean;
63
- }
64
- //# sourceMappingURL=player.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAqB,MAAM,QAAQ,CAAA;AAGtD,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAChC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,qBAAa,MAAM;IAEjB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,UAAU,CAAwE;IAC1F,OAAO,CAAC,WAAW,CAA0E;IAC7F,OAAO,CAAC,SAAS,CAAY;IAG7B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,YAAY,CAAY;IAGhC,OAAO,CAAC,SAAS,CAAY;IAE7B;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5C;;OAEG;IACH,OAAO,CAAC,aAAa;IAsDrB;;;OAGG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASxB;;;OAGG;IACH,MAAM,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAwBrD;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;IA6F1C;;OAEG;IACH,WAAW,IAAI,iBAAiB;IAQhC,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,aAAa,IAAI,OAAO,CAE3B;CACF"}
package/dist/player.js DELETED
@@ -1,220 +0,0 @@
1
- import { Quat, Vec3, bezierInterpolate } from "./math";
2
- import { VMDLoader } from "./vmd-loader";
3
- export class Player {
4
- constructor() {
5
- // Animation data
6
- this.frames = [];
7
- this.boneTracks = new Map();
8
- this.morphTracks = new Map();
9
- this._duration = 0;
10
- // Playback state
11
- this.isPlaying = false;
12
- this.isPaused = false;
13
- this._currentTime = 0;
14
- // Timing
15
- this.startTime = 0; // Real-time when playback started
16
- }
17
- /**
18
- * Load VMD animation file
19
- */
20
- async loadVmd(vmdUrl) {
21
- // Load animation
22
- this.frames = await VMDLoader.load(vmdUrl);
23
- this.processFrames();
24
- }
25
- /**
26
- * Process frames into tracks
27
- */
28
- processFrames() {
29
- // Helper to group frames by name and sort by time
30
- const groupFrames = (items) => {
31
- const tracks = new Map();
32
- for (const { item, name, time } of items) {
33
- if (!tracks.has(name))
34
- tracks.set(name, []);
35
- tracks.get(name).push({ item, time });
36
- }
37
- for (const keyFrames of tracks.values()) {
38
- keyFrames.sort((a, b) => a.time - b.time);
39
- }
40
- return tracks;
41
- };
42
- // Collect all bone and morph frames
43
- const boneItems = [];
44
- const morphItems = [];
45
- for (const keyFrame of this.frames) {
46
- for (const boneFrame of keyFrame.boneFrames) {
47
- boneItems.push({ item: boneFrame, name: boneFrame.boneName, time: keyFrame.time });
48
- }
49
- for (const morphFrame of keyFrame.morphFrames) {
50
- morphItems.push({ item: morphFrame, name: morphFrame.morphName, time: keyFrame.time });
51
- }
52
- }
53
- // Transform to expected format
54
- this.boneTracks = new Map();
55
- for (const [name, frames] of groupFrames(boneItems).entries()) {
56
- this.boneTracks.set(name, frames.map((f) => ({ boneFrame: f.item, time: f.time })));
57
- }
58
- this.morphTracks = new Map();
59
- for (const [name, frames] of groupFrames(morphItems).entries()) {
60
- this.morphTracks.set(name, frames.map((f) => ({ morphFrame: f.item, time: f.time })));
61
- }
62
- // Calculate duration from all tracks
63
- const allTracks = [...this.boneTracks.values(), ...this.morphTracks.values()];
64
- this._duration = allTracks.reduce((max, keyFrames) => {
65
- const lastTime = keyFrames[keyFrames.length - 1]?.time ?? 0;
66
- return Math.max(max, lastTime);
67
- }, 0);
68
- }
69
- /**
70
- * Start or resume playback
71
- * Note: For iOS, this should be called synchronously from a user interaction event
72
- */
73
- play() {
74
- if (this.frames.length === 0)
75
- return;
76
- this.isPaused = false;
77
- this.startTime = performance.now() - this._currentTime * 1000;
78
- this.isPlaying = true;
79
- }
80
- /**
81
- * Pause playback
82
- */
83
- pause() {
84
- if (!this.isPlaying || this.isPaused)
85
- return;
86
- this.isPaused = true;
87
- }
88
- /**
89
- * Stop playback and reset to beginning
90
- */
91
- stop() {
92
- this.isPlaying = false;
93
- this.isPaused = false;
94
- this._currentTime = 0;
95
- this.startTime = 0;
96
- }
97
- /**
98
- * Seek to specific time
99
- */
100
- seek(time) {
101
- const clampedTime = Math.max(0, Math.min(time, this._duration));
102
- this._currentTime = clampedTime;
103
- if (this.isPlaying && !this.isPaused) {
104
- this.startTime = performance.now() - clampedTime * 1000;
105
- }
106
- }
107
- /**
108
- * Update playback and return current pose
109
- * Returns null if not playing, but returns current pose if paused
110
- */
111
- update(currentRealTime) {
112
- if (!this.isPlaying || this.frames.length === 0) {
113
- return null;
114
- }
115
- // If paused, return current pose at paused time (no time update)
116
- if (this.isPaused) {
117
- return this.getPoseAtTime(this._currentTime);
118
- }
119
- // Calculate current animation time
120
- const elapsedSeconds = (currentRealTime - this.startTime) / 1000;
121
- this._currentTime = elapsedSeconds;
122
- // Check if animation ended
123
- if (this._currentTime >= this._duration) {
124
- this._currentTime = this._duration;
125
- this.pause(); // Auto-pause at end
126
- return this.getPoseAtTime(this._currentTime);
127
- }
128
- return this.getPoseAtTime(this._currentTime);
129
- }
130
- /**
131
- * Get pose at specific time (pure function)
132
- */
133
- getPoseAtTime(time) {
134
- const pose = {
135
- boneRotations: new Map(),
136
- boneTranslations: new Map(),
137
- morphWeights: new Map(),
138
- };
139
- // Generic binary search for upper bound
140
- const upperBound = (time, keyFrames) => {
141
- let left = 0, right = keyFrames.length;
142
- while (left < right) {
143
- const mid = Math.floor((left + right) / 2);
144
- if (keyFrames[mid].time <= time)
145
- left = mid + 1;
146
- else
147
- right = mid;
148
- }
149
- return left;
150
- };
151
- // Process bone tracks
152
- for (const [boneName, keyFrames] of this.boneTracks.entries()) {
153
- if (keyFrames.length === 0)
154
- continue;
155
- const clampedTime = Math.max(keyFrames[0].time, Math.min(keyFrames[keyFrames.length - 1].time, time));
156
- const idx = upperBound(clampedTime, keyFrames) - 1;
157
- if (idx < 0)
158
- continue;
159
- const frameA = keyFrames[idx].boneFrame;
160
- const frameB = keyFrames[idx + 1]?.boneFrame;
161
- if (!frameB) {
162
- pose.boneRotations.set(boneName, frameA.rotation);
163
- pose.boneTranslations.set(boneName, frameA.translation);
164
- }
165
- else {
166
- const timeA = keyFrames[idx].time;
167
- const timeB = keyFrames[idx + 1].time;
168
- const gradient = (clampedTime - timeA) / (timeB - timeA);
169
- const interp = frameB.interpolation;
170
- pose.boneRotations.set(boneName, Quat.slerp(frameA.rotation, frameB.rotation, bezierInterpolate(interp[0] / 127, interp[1] / 127, interp[2] / 127, interp[3] / 127, gradient)));
171
- const lerp = (a, b, w) => a + (b - a) * w;
172
- const getWeight = (offset) => bezierInterpolate(interp[offset] / 127, interp[offset + 8] / 127, interp[offset + 4] / 127, interp[offset + 12] / 127, gradient);
173
- pose.boneTranslations.set(boneName, new Vec3(lerp(frameA.translation.x, frameB.translation.x, getWeight(0)), lerp(frameA.translation.y, frameB.translation.y, getWeight(16)), lerp(frameA.translation.z, frameB.translation.z, getWeight(32))));
174
- }
175
- }
176
- // Process morph tracks
177
- for (const [morphName, keyFrames] of this.morphTracks.entries()) {
178
- if (keyFrames.length === 0)
179
- continue;
180
- const clampedTime = Math.max(keyFrames[0].time, Math.min(keyFrames[keyFrames.length - 1].time, time));
181
- const idx = upperBound(clampedTime, keyFrames) - 1;
182
- if (idx < 0)
183
- continue;
184
- const frameA = keyFrames[idx].morphFrame;
185
- const frameB = keyFrames[idx + 1]?.morphFrame;
186
- if (!frameB) {
187
- pose.morphWeights.set(morphName, frameA.weight);
188
- }
189
- else {
190
- const timeA = keyFrames[idx].time;
191
- const timeB = keyFrames[idx + 1].time;
192
- const gradient = (clampedTime - timeA) / (timeB - timeA);
193
- pose.morphWeights.set(morphName, frameA.weight + (frameB.weight - frameA.weight) * gradient);
194
- }
195
- }
196
- return pose;
197
- }
198
- /**
199
- * Get current playback progress
200
- */
201
- getProgress() {
202
- return {
203
- current: this._currentTime,
204
- duration: this._duration,
205
- percentage: this._duration > 0 ? (this._currentTime / this._duration) * 100 : 0,
206
- };
207
- }
208
- get currentTime() {
209
- return this._currentTime;
210
- }
211
- get duration() {
212
- return this._duration;
213
- }
214
- get isPlayingState() {
215
- return this.isPlaying && !this.isPaused;
216
- }
217
- get isPausedState() {
218
- return this.isPaused;
219
- }
220
- }
package/src/player.ts DELETED
@@ -1,290 +0,0 @@
1
- import { Quat, Vec3, bezierInterpolate } from "./math"
2
- import { BoneFrame, MorphFrame, VMDKeyFrame, VMDLoader } from "./vmd-loader"
3
-
4
- export interface AnimationPose {
5
- boneRotations: Map<string, Quat>
6
- boneTranslations: Map<string, Vec3>
7
- morphWeights: Map<string, number>
8
- }
9
-
10
- export interface AnimationProgress {
11
- current: number
12
- duration: number
13
- percentage: number
14
- }
15
-
16
- export class Player {
17
- // Animation data
18
- private frames: VMDKeyFrame[] = []
19
- private boneTracks: Map<string, Array<{ boneFrame: BoneFrame; time: number }>> = new Map()
20
- private morphTracks: Map<string, Array<{ morphFrame: MorphFrame; time: number }>> = new Map()
21
- private _duration: number = 0
22
-
23
- // Playback state
24
- private isPlaying: boolean = false
25
- private isPaused: boolean = false
26
- private _currentTime: number = 0
27
-
28
- // Timing
29
- private startTime: number = 0 // Real-time when playback started
30
-
31
- /**
32
- * Load VMD animation file
33
- */
34
- async loadVmd(vmdUrl: string): Promise<void> {
35
- // Load animation
36
- this.frames = await VMDLoader.load(vmdUrl)
37
- this.processFrames()
38
- }
39
-
40
- /**
41
- * Process frames into tracks
42
- */
43
- private processFrames(): void {
44
- // Helper to group frames by name and sort by time
45
- const groupFrames = <T>(
46
- items: Array<{ item: T; name: string; time: number }>
47
- ): Map<string, Array<{ item: T; time: number }>> => {
48
- const tracks = new Map<string, Array<{ item: T; time: number }>>()
49
- for (const { item, name, time } of items) {
50
- if (!tracks.has(name)) tracks.set(name, [])
51
- tracks.get(name)!.push({ item, time })
52
- }
53
- for (const keyFrames of tracks.values()) {
54
- keyFrames.sort((a, b) => a.time - b.time)
55
- }
56
- return tracks
57
- }
58
-
59
- // Collect all bone and morph frames
60
- const boneItems: Array<{ item: BoneFrame; name: string; time: number }> = []
61
- const morphItems: Array<{ item: MorphFrame; name: string; time: number }> = []
62
-
63
- for (const keyFrame of this.frames) {
64
- for (const boneFrame of keyFrame.boneFrames) {
65
- boneItems.push({ item: boneFrame, name: boneFrame.boneName, time: keyFrame.time })
66
- }
67
- for (const morphFrame of keyFrame.morphFrames) {
68
- morphItems.push({ item: morphFrame, name: morphFrame.morphName, time: keyFrame.time })
69
- }
70
- }
71
-
72
- // Transform to expected format
73
- this.boneTracks = new Map()
74
- for (const [name, frames] of groupFrames(boneItems).entries()) {
75
- this.boneTracks.set(
76
- name,
77
- frames.map((f) => ({ boneFrame: f.item, time: f.time }))
78
- )
79
- }
80
-
81
- this.morphTracks = new Map()
82
- for (const [name, frames] of groupFrames(morphItems).entries()) {
83
- this.morphTracks.set(
84
- name,
85
- frames.map((f) => ({ morphFrame: f.item, time: f.time }))
86
- )
87
- }
88
-
89
- // Calculate duration from all tracks
90
- const allTracks = [...this.boneTracks.values(), ...this.morphTracks.values()]
91
- this._duration = allTracks.reduce((max, keyFrames) => {
92
- const lastTime = keyFrames[keyFrames.length - 1]?.time ?? 0
93
- return Math.max(max, lastTime)
94
- }, 0)
95
- }
96
-
97
- /**
98
- * Start or resume playback
99
- * Note: For iOS, this should be called synchronously from a user interaction event
100
- */
101
- play(): void {
102
- if (this.frames.length === 0) return
103
-
104
- this.isPaused = false
105
- this.startTime = performance.now() - this._currentTime * 1000
106
-
107
- this.isPlaying = true
108
- }
109
-
110
- /**
111
- * Pause playback
112
- */
113
- pause(): void {
114
- if (!this.isPlaying || this.isPaused) return
115
- this.isPaused = true
116
- }
117
-
118
- /**
119
- * Stop playback and reset to beginning
120
- */
121
- stop(): void {
122
- this.isPlaying = false
123
- this.isPaused = false
124
- this._currentTime = 0
125
- this.startTime = 0
126
- }
127
-
128
- /**
129
- * Seek to specific time
130
- */
131
- seek(time: number): void {
132
- const clampedTime = Math.max(0, Math.min(time, this._duration))
133
- this._currentTime = clampedTime
134
-
135
- if (this.isPlaying && !this.isPaused) {
136
- this.startTime = performance.now() - clampedTime * 1000
137
- }
138
- }
139
-
140
- /**
141
- * Update playback and return current pose
142
- * Returns null if not playing, but returns current pose if paused
143
- */
144
- update(currentRealTime: number): AnimationPose | null {
145
- if (!this.isPlaying || this.frames.length === 0) {
146
- return null
147
- }
148
-
149
- // If paused, return current pose at paused time (no time update)
150
- if (this.isPaused) {
151
- return this.getPoseAtTime(this._currentTime)
152
- }
153
-
154
- // Calculate current animation time
155
- const elapsedSeconds = (currentRealTime - this.startTime) / 1000
156
- this._currentTime = elapsedSeconds
157
-
158
- // Check if animation ended
159
- if (this._currentTime >= this._duration) {
160
- this._currentTime = this._duration
161
- this.pause() // Auto-pause at end
162
- return this.getPoseAtTime(this._currentTime)
163
- }
164
-
165
- return this.getPoseAtTime(this._currentTime)
166
- }
167
-
168
- /**
169
- * Get pose at specific time (pure function)
170
- */
171
- getPoseAtTime(time: number): AnimationPose {
172
- const pose: AnimationPose = {
173
- boneRotations: new Map(),
174
- boneTranslations: new Map(),
175
- morphWeights: new Map(),
176
- }
177
-
178
- // Generic binary search for upper bound
179
- const upperBound = <T extends { time: number }>(time: number, keyFrames: T[]): number => {
180
- let left = 0,
181
- right = keyFrames.length
182
- while (left < right) {
183
- const mid = Math.floor((left + right) / 2)
184
- if (keyFrames[mid].time <= time) left = mid + 1
185
- else right = mid
186
- }
187
- return left
188
- }
189
-
190
- // Process bone tracks
191
- for (const [boneName, keyFrames] of this.boneTracks.entries()) {
192
- if (keyFrames.length === 0) continue
193
-
194
- const clampedTime = Math.max(keyFrames[0].time, Math.min(keyFrames[keyFrames.length - 1].time, time))
195
- const idx = upperBound(clampedTime, keyFrames) - 1
196
- if (idx < 0) continue
197
-
198
- const frameA = keyFrames[idx].boneFrame
199
- const frameB = keyFrames[idx + 1]?.boneFrame
200
-
201
- if (!frameB) {
202
- pose.boneRotations.set(boneName, frameA.rotation)
203
- pose.boneTranslations.set(boneName, frameA.translation)
204
- } else {
205
- const timeA = keyFrames[idx].time
206
- const timeB = keyFrames[idx + 1].time
207
- const gradient = (clampedTime - timeA) / (timeB - timeA)
208
- const interp = frameB.interpolation
209
-
210
- pose.boneRotations.set(
211
- boneName,
212
- Quat.slerp(
213
- frameA.rotation,
214
- frameB.rotation,
215
- bezierInterpolate(interp[0] / 127, interp[1] / 127, interp[2] / 127, interp[3] / 127, gradient)
216
- )
217
- )
218
-
219
- const lerp = (a: number, b: number, w: number) => a + (b - a) * w
220
- const getWeight = (offset: number) =>
221
- bezierInterpolate(
222
- interp[offset] / 127,
223
- interp[offset + 8] / 127,
224
- interp[offset + 4] / 127,
225
- interp[offset + 12] / 127,
226
- gradient
227
- )
228
-
229
- pose.boneTranslations.set(
230
- boneName,
231
- new Vec3(
232
- lerp(frameA.translation.x, frameB.translation.x, getWeight(0)),
233
- lerp(frameA.translation.y, frameB.translation.y, getWeight(16)),
234
- lerp(frameA.translation.z, frameB.translation.z, getWeight(32))
235
- )
236
- )
237
- }
238
- }
239
-
240
- // Process morph tracks
241
- for (const [morphName, keyFrames] of this.morphTracks.entries()) {
242
- if (keyFrames.length === 0) continue
243
-
244
- const clampedTime = Math.max(keyFrames[0].time, Math.min(keyFrames[keyFrames.length - 1].time, time))
245
- const idx = upperBound(clampedTime, keyFrames) - 1
246
- if (idx < 0) continue
247
-
248
- const frameA = keyFrames[idx].morphFrame
249
- const frameB = keyFrames[idx + 1]?.morphFrame
250
-
251
- if (!frameB) {
252
- pose.morphWeights.set(morphName, frameA.weight)
253
- } else {
254
- const timeA = keyFrames[idx].time
255
- const timeB = keyFrames[idx + 1].time
256
- const gradient = (clampedTime - timeA) / (timeB - timeA)
257
- pose.morphWeights.set(morphName, frameA.weight + (frameB.weight - frameA.weight) * gradient)
258
- }
259
- }
260
-
261
- return pose
262
- }
263
-
264
- /**
265
- * Get current playback progress
266
- */
267
- getProgress(): AnimationProgress {
268
- return {
269
- current: this._currentTime,
270
- duration: this._duration,
271
- percentage: this._duration > 0 ? (this._currentTime / this._duration) * 100 : 0,
272
- }
273
- }
274
-
275
- get currentTime(): number {
276
- return this._currentTime
277
- }
278
-
279
- get duration(): number {
280
- return this._duration
281
- }
282
-
283
- get isPlayingState(): boolean {
284
- return this.isPlaying && !this.isPaused
285
- }
286
-
287
- get isPausedState(): boolean {
288
- return this.isPaused
289
- }
290
- }