@myned-ai/gsplat-flame-avatar-renderer 1.0.6 → 1.0.7
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 +30 -0
- package/dist/gsplat-flame-avatar-renderer.cjs.js +38 -33
- package/dist/gsplat-flame-avatar-renderer.cjs.min.js +1 -1
- package/dist/gsplat-flame-avatar-renderer.cjs.min.js.map +1 -1
- package/dist/gsplat-flame-avatar-renderer.esm.js +38 -33
- package/dist/gsplat-flame-avatar-renderer.esm.min.js +1 -1
- package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +1 -1
- package/package.json +5 -2
- package/src/api/index.js +0 -7
- package/src/buffers/SplatBuffer.js +0 -1394
- package/src/buffers/SplatBufferGenerator.js +0 -41
- package/src/buffers/SplatPartitioner.js +0 -110
- package/src/buffers/UncompressedSplatArray.js +0 -106
- package/src/buffers/index.js +0 -11
- package/src/core/SplatGeometry.js +0 -48
- package/src/core/SplatMesh.js +0 -2627
- package/src/core/SplatScene.js +0 -43
- package/src/core/SplatTree.js +0 -200
- package/src/core/Viewer.js +0 -2746
- package/src/core/index.js +0 -13
- package/src/enums/EngineConstants.js +0 -58
- package/src/enums/LogLevel.js +0 -13
- package/src/enums/RenderMode.js +0 -11
- package/src/enums/SceneFormat.js +0 -21
- package/src/enums/SceneRevealMode.js +0 -11
- package/src/enums/SplatRenderMode.js +0 -10
- package/src/enums/index.js +0 -13
- package/src/errors/ApplicationError.js +0 -185
- package/src/errors/index.js +0 -17
- package/src/flame/FlameAnimator.js +0 -496
- package/src/flame/FlameConstants.js +0 -21
- package/src/flame/FlameTextureManager.js +0 -293
- package/src/flame/index.js +0 -22
- package/src/flame/utils.js +0 -50
- package/src/index.js +0 -39
- package/src/loaders/DirectLoadError.js +0 -14
- package/src/loaders/INRIAV1PlyParser.js +0 -223
- package/src/loaders/PlyLoader.js +0 -519
- package/src/loaders/PlyParser.js +0 -19
- package/src/loaders/PlyParserUtils.js +0 -311
- package/src/loaders/index.js +0 -13
- package/src/materials/SplatMaterial.js +0 -1068
- package/src/materials/SplatMaterial2D.js +0 -358
- package/src/materials/SplatMaterial3D.js +0 -323
- package/src/materials/index.js +0 -11
- package/src/raycaster/Hit.js +0 -37
- package/src/raycaster/Ray.js +0 -123
- package/src/raycaster/Raycaster.js +0 -175
- package/src/raycaster/index.js +0 -10
- package/src/renderer/AnimationManager.js +0 -577
- package/src/renderer/AppConstants.js +0 -101
- package/src/renderer/GaussianSplatRenderer.js +0 -1146
- package/src/renderer/index.js +0 -24
- package/src/utils/BlobUrlManager.js +0 -294
- package/src/utils/EventEmitter.js +0 -349
- package/src/utils/LoaderUtils.js +0 -66
- package/src/utils/Logger.js +0 -171
- package/src/utils/ObjectPool.js +0 -248
- package/src/utils/RenderLoop.js +0 -306
- package/src/utils/Util.js +0 -416
- package/src/utils/ValidationUtils.js +0 -331
- package/src/utils/index.js +0 -18
- package/src/worker/SortWorker.js +0 -284
- package/src/worker/index.js +0 -8
|
@@ -1,577 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AnimationManager
|
|
3
|
-
*
|
|
4
|
-
* Derived from gaussian-splat-renderer-for-lam
|
|
5
|
-
* Manages animation state machine with Three.js AnimationMixer.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { LoopOnce, LoopRepeat } from 'three';
|
|
9
|
-
import { TYVoiceChatState } from './AppConstants.js';
|
|
10
|
-
import { getLogger } from '../utils/Logger.js';
|
|
11
|
-
|
|
12
|
-
const logger = getLogger('AnimationManager');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Base State class for animation states
|
|
16
|
-
*/
|
|
17
|
-
class State {
|
|
18
|
-
constructor(actions, isGroup) {
|
|
19
|
-
this.isPlaying = false;
|
|
20
|
-
this.stage = 0;
|
|
21
|
-
this.actions = actions || [];
|
|
22
|
-
this.blendingTime = 0.5;
|
|
23
|
-
this.isGroup = isGroup || false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
dispose() {
|
|
27
|
-
this.actions = [];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
update(state) {
|
|
31
|
-
// Override in subclasses
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Hello state - initial greeting animation
|
|
37
|
-
*/
|
|
38
|
-
class Hello extends State {
|
|
39
|
-
constructor(actions, isGroup) {
|
|
40
|
-
super(actions, isGroup);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
update(state) {
|
|
44
|
-
// Safety check: return early if no actions available
|
|
45
|
-
if (!this.actions || this.actions.length === 0) return;
|
|
46
|
-
|
|
47
|
-
if (AnimationManager.CurPlaying === undefined &&
|
|
48
|
-
state === TYVoiceChatState.Idle &&
|
|
49
|
-
this.isPlaying === false) {
|
|
50
|
-
this.stage = 0;
|
|
51
|
-
this.actions[this.stage].time = 0;
|
|
52
|
-
AnimationManager.SetWeight(this.actions[this.stage], 1.0);
|
|
53
|
-
this.actions[this.stage].loop = LoopRepeat;
|
|
54
|
-
this.actions[this.stage].clampWhenFinished = false;
|
|
55
|
-
this.actions[this.stage].paused = false;
|
|
56
|
-
this.actions[this.stage].play();
|
|
57
|
-
if (AnimationManager.LastAction !== undefined) {
|
|
58
|
-
AnimationManager.PrepareCrossFade(AnimationManager.LastAction, this.actions[this.stage], this.blendingTime);
|
|
59
|
-
}
|
|
60
|
-
this.isPlaying = true;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Idle &&
|
|
64
|
-
state === TYVoiceChatState.Idle &&
|
|
65
|
-
this.isPlaying === true) {
|
|
66
|
-
if (this.actions[this.stage].time >
|
|
67
|
-
this.actions[this.stage].getClip().duration - this.blendingTime) {
|
|
68
|
-
let nextStage = this.stage + 1;
|
|
69
|
-
if (nextStage >= this.actions.length) nextStage = 0;
|
|
70
|
-
this.actions[nextStage].time = 0;
|
|
71
|
-
AnimationManager.SetWeight(this.actions[nextStage], 1.0);
|
|
72
|
-
this.actions[nextStage].loop = LoopRepeat;
|
|
73
|
-
this.actions[nextStage].play();
|
|
74
|
-
AnimationManager.PrepareCrossFade(this.actions[this.stage], this.actions[nextStage], this.blendingTime);
|
|
75
|
-
this.stage = nextStage;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Idle state - resting animation
|
|
83
|
-
*/
|
|
84
|
-
class Idle extends State {
|
|
85
|
-
constructor(actions, isGroup) {
|
|
86
|
-
super(actions, isGroup);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
update(state) {
|
|
90
|
-
// Safety check: return early if no actions available
|
|
91
|
-
if (!this.actions || this.actions.length === 0) return;
|
|
92
|
-
|
|
93
|
-
if (AnimationManager.CurPlaying === undefined &&
|
|
94
|
-
state === TYVoiceChatState.Idle &&
|
|
95
|
-
this.isPlaying === false) {
|
|
96
|
-
this.stage = 0;
|
|
97
|
-
this.actions[this.stage].time = 0;
|
|
98
|
-
AnimationManager.SetWeight(this.actions[this.stage], 1.0);
|
|
99
|
-
this.actions[this.stage].loop = LoopRepeat;
|
|
100
|
-
this.actions[this.stage].clampWhenFinished = false;
|
|
101
|
-
this.actions[this.stage].paused = false;
|
|
102
|
-
this.actions[this.stage].play();
|
|
103
|
-
if (AnimationManager.LastAction !== undefined) {
|
|
104
|
-
AnimationManager.PrepareCrossFade(AnimationManager.LastAction, this.actions[this.stage], this.blendingTime);
|
|
105
|
-
}
|
|
106
|
-
this.isPlaying = true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Idle &&
|
|
110
|
-
state !== TYVoiceChatState.Idle &&
|
|
111
|
-
this.isPlaying === true &&
|
|
112
|
-
this.stage === 0) {
|
|
113
|
-
this.actions[this.stage].loop = LoopOnce;
|
|
114
|
-
this.actions[this.stage].clampWhenFinished = true;
|
|
115
|
-
this.isPlaying = false;
|
|
116
|
-
AnimationManager.LastAction = this.actions[this.stage];
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Listen state - listening animation
|
|
123
|
-
*/
|
|
124
|
-
class Listen extends State {
|
|
125
|
-
constructor(actions, isGroup) {
|
|
126
|
-
super(actions, isGroup);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
update(state) {
|
|
130
|
-
// Safety check: return early if no actions available
|
|
131
|
-
if (!this.actions || this.actions.length === 0) return;
|
|
132
|
-
|
|
133
|
-
if (AnimationManager.CurPlaying === undefined &&
|
|
134
|
-
state === TYVoiceChatState.Listening &&
|
|
135
|
-
this.isPlaying === false) {
|
|
136
|
-
this.stage = 0;
|
|
137
|
-
this.actions[this.stage].time = 0;
|
|
138
|
-
this.actions[this.stage].play();
|
|
139
|
-
AnimationManager.SetWeight(this.actions[this.stage], 1.0);
|
|
140
|
-
this.actions[this.stage].loop = this.isGroup ? LoopOnce : LoopRepeat;
|
|
141
|
-
this.actions[this.stage].clampWhenFinished = this.isGroup ? true : false;
|
|
142
|
-
if (AnimationManager.LastAction !== undefined) {
|
|
143
|
-
AnimationManager.PrepareCrossFade(AnimationManager.LastAction, this.actions[this.stage], this.blendingTime);
|
|
144
|
-
}
|
|
145
|
-
this.isPlaying = true;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (this.isGroup) {
|
|
149
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Listening &&
|
|
150
|
-
state === TYVoiceChatState.Listening &&
|
|
151
|
-
this.isPlaying === true &&
|
|
152
|
-
this.stage === 0) {
|
|
153
|
-
if (this.actions[this.stage].time >
|
|
154
|
-
this.actions[this.stage].getClip().duration - this.blendingTime) {
|
|
155
|
-
this.actions[this.stage + 1].time = 0;
|
|
156
|
-
AnimationManager.SetWeight(this.actions[this.stage + 1], 1.0);
|
|
157
|
-
this.actions[this.stage + 1].loop = LoopRepeat;
|
|
158
|
-
this.actions[this.stage + 1].play();
|
|
159
|
-
AnimationManager.PrepareCrossFade(this.actions[this.stage], this.actions[this.stage + 1], this.blendingTime);
|
|
160
|
-
this.stage = 1;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Listening &&
|
|
165
|
-
state !== TYVoiceChatState.Listening &&
|
|
166
|
-
this.isPlaying === true &&
|
|
167
|
-
(this.stage === 0 || this.stage === 1)) {
|
|
168
|
-
this.actions[2].time = 0;
|
|
169
|
-
this.actions[2].play();
|
|
170
|
-
AnimationManager.SetWeight(this.actions[2], 1.0);
|
|
171
|
-
this.actions[2].loop = LoopOnce;
|
|
172
|
-
AnimationManager.PrepareCrossFade(this.actions[this.stage], this.actions[2], this.blendingTime);
|
|
173
|
-
this.stage = 2;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Listening &&
|
|
178
|
-
state !== TYVoiceChatState.Listening &&
|
|
179
|
-
this.isPlaying === true &&
|
|
180
|
-
this.stage === (this.isGroup ? this.actions.length - 1 : 0)) {
|
|
181
|
-
this.actions[this.stage].loop = LoopOnce;
|
|
182
|
-
this.actions[this.stage].clampWhenFinished = true;
|
|
183
|
-
this.isPlaying = false;
|
|
184
|
-
AnimationManager.LastAction = this.actions[this.stage];
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Think state - thinking animation
|
|
191
|
-
*/
|
|
192
|
-
class Think extends State {
|
|
193
|
-
constructor(actions, isGroup) {
|
|
194
|
-
super(actions, isGroup);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
update(state) {
|
|
198
|
-
// Safety check: return early if no actions available
|
|
199
|
-
if (!this.actions || this.actions.length === 0) return;
|
|
200
|
-
|
|
201
|
-
if (AnimationManager.CurPlaying === undefined &&
|
|
202
|
-
state === TYVoiceChatState.Thinking &&
|
|
203
|
-
this.isPlaying === false) {
|
|
204
|
-
this.stage = 0;
|
|
205
|
-
this.actions[this.stage].time = 0;
|
|
206
|
-
this.actions[this.stage].play();
|
|
207
|
-
AnimationManager.SetWeight(this.actions[this.stage], 1.0);
|
|
208
|
-
this.actions[this.stage].loop = LoopOnce;
|
|
209
|
-
if (AnimationManager.LastAction !== undefined) {
|
|
210
|
-
AnimationManager.PrepareCrossFade(AnimationManager.LastAction, this.actions[this.stage], this.blendingTime);
|
|
211
|
-
}
|
|
212
|
-
this.isPlaying = true;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (this.isGroup) {
|
|
216
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Thinking &&
|
|
217
|
-
state === TYVoiceChatState.Thinking &&
|
|
218
|
-
this.isPlaying === true &&
|
|
219
|
-
this.stage === 0) {
|
|
220
|
-
if (this.actions[this.stage].time >
|
|
221
|
-
this.actions[this.stage].getClip().duration - this.blendingTime) {
|
|
222
|
-
this.actions[this.stage + 1].time = 0;
|
|
223
|
-
AnimationManager.SetWeight(this.actions[this.stage + 1], 1.0);
|
|
224
|
-
this.actions[this.stage + 1].loop = LoopRepeat;
|
|
225
|
-
this.actions[this.stage + 1].play();
|
|
226
|
-
AnimationManager.PrepareCrossFade(this.actions[this.stage], this.actions[this.stage + 1], this.blendingTime);
|
|
227
|
-
this.stage = 1;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Thinking &&
|
|
232
|
-
state !== TYVoiceChatState.Thinking &&
|
|
233
|
-
this.isPlaying === true &&
|
|
234
|
-
(this.stage === 0 || this.stage === 1)) {
|
|
235
|
-
this.actions[2].time = 0;
|
|
236
|
-
this.actions[2].play();
|
|
237
|
-
AnimationManager.SetWeight(this.actions[2], 1.0);
|
|
238
|
-
this.actions[2].loop = LoopOnce;
|
|
239
|
-
AnimationManager.PrepareCrossFade(this.actions[this.stage], this.actions[2], this.blendingTime);
|
|
240
|
-
this.stage = 2;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Thinking &&
|
|
245
|
-
state !== TYVoiceChatState.Thinking &&
|
|
246
|
-
this.isPlaying === true &&
|
|
247
|
-
this.stage === (this.isGroup ? this.actions.length - 1 : 0)) {
|
|
248
|
-
this.actions[this.stage].loop = LoopOnce;
|
|
249
|
-
this.actions[this.stage].clampWhenFinished = true;
|
|
250
|
-
this.isPlaying = false;
|
|
251
|
-
AnimationManager.LastAction = this.actions[this.stage];
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Speak state - speaking animation with random movement selection
|
|
258
|
-
*/
|
|
259
|
-
class Speak extends State {
|
|
260
|
-
constructor(actions, isGroup) {
|
|
261
|
-
super(actions, isGroup);
|
|
262
|
-
logger.debug('[SPEAK] Initialized with', actions?.length || 0, 'actions, isGroup:', isGroup);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Get a random number in range [min, max]
|
|
267
|
-
*/
|
|
268
|
-
getRandomNumber(max, min) {
|
|
269
|
-
const range = max - min;
|
|
270
|
-
return min + Math.round(Math.random() * range);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
update(state) {
|
|
274
|
-
// Safety check: return early if no actions available
|
|
275
|
-
if (!this.actions || this.actions.length === 0) {
|
|
276
|
-
if (!this._warnedNoActions) {
|
|
277
|
-
logger.warn('[SPEAK] No actions available!');
|
|
278
|
-
this._warnedNoActions = true;
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Start speaking - pick a random animation
|
|
284
|
-
if (AnimationManager.CurPlaying === undefined &&
|
|
285
|
-
state === TYVoiceChatState.Responding &&
|
|
286
|
-
this.isPlaying === false) {
|
|
287
|
-
// Randomly select initial animation
|
|
288
|
-
this.stage = Math.ceil(this.getRandomNumber(0, this.actions.length - 1));
|
|
289
|
-
logger.debug('[SPEAK] Starting animation, stage:', this.stage, 'of', this.actions.length);
|
|
290
|
-
this.actions[this.stage].time = 0;
|
|
291
|
-
this.actions[this.stage].play();
|
|
292
|
-
AnimationManager.SetWeight(this.actions[this.stage], 1.0);
|
|
293
|
-
this.actions[this.stage].loop = LoopOnce;
|
|
294
|
-
this.actions[this.stage].clampWhenFinished = true;
|
|
295
|
-
if (AnimationManager.LastAction !== undefined) {
|
|
296
|
-
AnimationManager.PrepareCrossFade(AnimationManager.LastAction, this.actions[this.stage], this.blendingTime);
|
|
297
|
-
}
|
|
298
|
-
this.isPlaying = true;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Continue speaking - cycle through random animations
|
|
302
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Responding &&
|
|
303
|
-
state === TYVoiceChatState.Responding &&
|
|
304
|
-
this.isPlaying === true) {
|
|
305
|
-
if (this.actions[this.stage].time >=
|
|
306
|
-
this.actions[this.stage].getClip().duration - this.blendingTime) {
|
|
307
|
-
const lastAction = this.actions[this.stage];
|
|
308
|
-
// Pick a different random animation
|
|
309
|
-
this.stage = (this.stage + Math.ceil(this.getRandomNumber(1, this.actions.length - 1))) % this.actions.length;
|
|
310
|
-
logger.debug('[SPEAK] Cycling to next animation, stage:', this.stage);
|
|
311
|
-
this.actions[this.stage].time = 0;
|
|
312
|
-
this.actions[this.stage].play();
|
|
313
|
-
AnimationManager.SetWeight(this.actions[this.stage], 1.0);
|
|
314
|
-
this.actions[this.stage].loop = LoopOnce;
|
|
315
|
-
this.actions[this.stage].clampWhenFinished = true;
|
|
316
|
-
AnimationManager.PrepareCrossFade(lastAction, this.actions[this.stage], this.blendingTime);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Stop speaking - finish current animation
|
|
321
|
-
if (AnimationManager.CurPlaying === TYVoiceChatState.Responding &&
|
|
322
|
-
state !== TYVoiceChatState.Responding &&
|
|
323
|
-
this.isPlaying === true) {
|
|
324
|
-
this.actions[this.stage].loop = LoopOnce;
|
|
325
|
-
this.actions[this.stage].clampWhenFinished = true;
|
|
326
|
-
this.isPlaying = false;
|
|
327
|
-
AnimationManager.LastAction = this.actions[this.stage];
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* AnimationManager - Main animation controller
|
|
334
|
-
* Manages state machine with crossfade transitions between animation states
|
|
335
|
-
*/
|
|
336
|
-
export class AnimationManager {
|
|
337
|
-
// Static properties
|
|
338
|
-
static IsBlending = false;
|
|
339
|
-
static actions = [];
|
|
340
|
-
static NeedReset = false;
|
|
341
|
-
static NeedFullReset = false;
|
|
342
|
-
static LastAction = undefined;
|
|
343
|
-
static CurPlaying = undefined;
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Set animation action weight
|
|
347
|
-
*/
|
|
348
|
-
static SetWeight(action, weight) {
|
|
349
|
-
action.enabled = true;
|
|
350
|
-
action.setEffectiveTimeScale(1);
|
|
351
|
-
action.setEffectiveWeight(weight);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Prepare crossfade between two actions
|
|
356
|
-
*/
|
|
357
|
-
static PrepareCrossFade(startAction, endAction, defaultDuration) {
|
|
358
|
-
const duration = defaultDuration;
|
|
359
|
-
AnimationManager.UnPauseAllActions();
|
|
360
|
-
AnimationManager.ExecuteCrossFade(startAction, endAction, duration);
|
|
361
|
-
AnimationManager.IsBlending = true;
|
|
362
|
-
setTimeout(() => {
|
|
363
|
-
AnimationManager.IsBlending = false;
|
|
364
|
-
}, defaultDuration + 0.1);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Pause all animation actions
|
|
369
|
-
*/
|
|
370
|
-
static PauseAllActions() {
|
|
371
|
-
AnimationManager.actions.forEach(function(action) {
|
|
372
|
-
action.paused = true;
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Unpause all animation actions
|
|
378
|
-
*/
|
|
379
|
-
static UnPauseAllActions() {
|
|
380
|
-
AnimationManager.actions.forEach(function(action) {
|
|
381
|
-
action.paused = false;
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Execute crossfade between two actions
|
|
387
|
-
*/
|
|
388
|
-
static ExecuteCrossFade(startAction, endAction, duration) {
|
|
389
|
-
AnimationManager.SetWeight(endAction, 1);
|
|
390
|
-
endAction.time = 0;
|
|
391
|
-
startAction.crossFadeTo(endAction, duration, true);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Constructor
|
|
396
|
-
* @param {THREE.AnimationMixer} mixer - Three.js animation mixer
|
|
397
|
-
* @param {THREE.AnimationClip[]} animations - Animation clips
|
|
398
|
-
* @param {object} animationcfg - Animation configuration
|
|
399
|
-
*/
|
|
400
|
-
constructor(mixer, animations, animationcfg) {
|
|
401
|
-
const helloActions = [];
|
|
402
|
-
const idleActions = [];
|
|
403
|
-
const listenActions = [];
|
|
404
|
-
const speakActions = [];
|
|
405
|
-
const thinkActions = [];
|
|
406
|
-
|
|
407
|
-
this.mixer = mixer;
|
|
408
|
-
|
|
409
|
-
// Calculate action indices based on config
|
|
410
|
-
const helloIdx = animationcfg?.hello?.size || 0;
|
|
411
|
-
const idleIdx = (animationcfg?.idle?.size || 0) + helloIdx;
|
|
412
|
-
const listenIdx = (animationcfg?.listen?.size || 0) + idleIdx;
|
|
413
|
-
const speakIdx = (animationcfg?.speak?.size || 0) + listenIdx;
|
|
414
|
-
const thinkIdx = (animationcfg?.think?.size || 0) + speakIdx;
|
|
415
|
-
|
|
416
|
-
// Distribute animation clips to state action arrays
|
|
417
|
-
if (animations && animations.length > 0) {
|
|
418
|
-
for (let i = 0; i < animations.length; i++) {
|
|
419
|
-
const clip = animations[i];
|
|
420
|
-
const action = mixer.clipAction(clip);
|
|
421
|
-
|
|
422
|
-
if (i < helloIdx) {
|
|
423
|
-
helloActions.push(action);
|
|
424
|
-
} else if (i < idleIdx) {
|
|
425
|
-
idleActions.push(action);
|
|
426
|
-
// Duplicate for states that share idle
|
|
427
|
-
if (listenIdx === idleIdx) {
|
|
428
|
-
listenActions.push(mixer.clipAction(clip.clone()));
|
|
429
|
-
}
|
|
430
|
-
if (speakIdx === listenIdx) {
|
|
431
|
-
speakActions.push(mixer.clipAction(clip.clone()));
|
|
432
|
-
}
|
|
433
|
-
if (thinkIdx === speakIdx) {
|
|
434
|
-
thinkActions.push(mixer.clipAction(clip.clone()));
|
|
435
|
-
}
|
|
436
|
-
} else if (i < listenIdx) {
|
|
437
|
-
listenActions.push(action);
|
|
438
|
-
} else if (i < speakIdx) {
|
|
439
|
-
speakActions.push(action);
|
|
440
|
-
} else if (i < thinkIdx) {
|
|
441
|
-
thinkActions.push(action);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
AnimationManager.actions.push(action);
|
|
445
|
-
AnimationManager.SetWeight(action, 0);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Create state instances
|
|
450
|
-
this.hello = new Hello(helloActions, animationcfg?.hello?.isGroup || false);
|
|
451
|
-
this.idle = new Idle(idleActions, animationcfg?.idle?.isGroup || false);
|
|
452
|
-
this.listen = new Listen(listenActions, animationcfg?.listen?.isGroup || false);
|
|
453
|
-
this.think = new Think(thinkActions, animationcfg?.think?.isGroup || false);
|
|
454
|
-
this.speak = new Speak(speakActions, animationcfg?.speak?.isGroup || false);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Get currently playing state
|
|
459
|
-
*/
|
|
460
|
-
curPlaying() {
|
|
461
|
-
if (this.hello.isPlaying) return TYVoiceChatState.Idle;
|
|
462
|
-
if (this.idle.isPlaying) return TYVoiceChatState.Idle;
|
|
463
|
-
if (this.listen.isPlaying) return TYVoiceChatState.Listening;
|
|
464
|
-
if (this.think.isPlaying) return TYVoiceChatState.Thinking;
|
|
465
|
-
if (this.speak.isPlaying) return TYVoiceChatState.Responding;
|
|
466
|
-
return undefined;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Dispose animation manager
|
|
471
|
-
*/
|
|
472
|
-
dispose() {
|
|
473
|
-
this.hello.dispose();
|
|
474
|
-
this.idle.dispose();
|
|
475
|
-
this.listen.dispose();
|
|
476
|
-
this.think.dispose();
|
|
477
|
-
this.speak.dispose();
|
|
478
|
-
AnimationManager.actions = [];
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Reset all animation actions
|
|
483
|
-
*/
|
|
484
|
-
resetAllActions(ignoreBlending = false) {
|
|
485
|
-
const curPlaying = this.curPlaying();
|
|
486
|
-
|
|
487
|
-
switch (curPlaying) {
|
|
488
|
-
case TYVoiceChatState.Idle:
|
|
489
|
-
AnimationManager.LastAction = this.hello.actions[this.hello.stage];
|
|
490
|
-
break;
|
|
491
|
-
case TYVoiceChatState.Listening:
|
|
492
|
-
AnimationManager.LastAction = this.listen.actions[this.listen.stage];
|
|
493
|
-
break;
|
|
494
|
-
case TYVoiceChatState.Thinking:
|
|
495
|
-
AnimationManager.LastAction = this.think.actions[this.think.stage];
|
|
496
|
-
break;
|
|
497
|
-
case TYVoiceChatState.Responding:
|
|
498
|
-
AnimationManager.LastAction = this.speak.actions[this.speak.stage];
|
|
499
|
-
break;
|
|
500
|
-
default:
|
|
501
|
-
AnimationManager.LastAction = undefined;
|
|
502
|
-
break;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
if (AnimationManager.LastAction) {
|
|
506
|
-
AnimationManager.LastAction.loop = LoopOnce;
|
|
507
|
-
AnimationManager.LastAction.clampWhenFinished = true;
|
|
508
|
-
AnimationManager.SetWeight(AnimationManager.LastAction, 1.0);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
if (ignoreBlending) {
|
|
512
|
-
AnimationManager.PauseAllActions();
|
|
513
|
-
AnimationManager.actions.forEach(function(action) {
|
|
514
|
-
action.time = 0;
|
|
515
|
-
AnimationManager.SetWeight(action, 0.0);
|
|
516
|
-
});
|
|
517
|
-
AnimationManager.LastAction = undefined;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
this.hello.isPlaying = false;
|
|
521
|
-
this.idle.isPlaying = false;
|
|
522
|
-
this.listen.isPlaying = false;
|
|
523
|
-
this.think.isPlaying = false;
|
|
524
|
-
this.speak.isPlaying = false;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
/**
|
|
528
|
-
* Update animation state
|
|
529
|
-
* @param {string} state - Target state (TYVoiceChatState)
|
|
530
|
-
*/
|
|
531
|
-
update(state) {
|
|
532
|
-
if (AnimationManager.IsBlending) return;
|
|
533
|
-
|
|
534
|
-
AnimationManager.CurPlaying = this.curPlaying();
|
|
535
|
-
|
|
536
|
-
if (AnimationManager.CurPlaying === undefined) {
|
|
537
|
-
switch (state) {
|
|
538
|
-
case TYVoiceChatState.Idle:
|
|
539
|
-
this.idle.update(state);
|
|
540
|
-
break;
|
|
541
|
-
case TYVoiceChatState.Listening:
|
|
542
|
-
this.listen.update(state);
|
|
543
|
-
break;
|
|
544
|
-
case TYVoiceChatState.Thinking:
|
|
545
|
-
this.think.update(state);
|
|
546
|
-
break;
|
|
547
|
-
case TYVoiceChatState.Responding:
|
|
548
|
-
this.speak.update(state);
|
|
549
|
-
break;
|
|
550
|
-
default:
|
|
551
|
-
this.idle.update(state);
|
|
552
|
-
break;
|
|
553
|
-
}
|
|
554
|
-
} else {
|
|
555
|
-
switch (AnimationManager.CurPlaying) {
|
|
556
|
-
case TYVoiceChatState.Idle:
|
|
557
|
-
this.idle.update(state);
|
|
558
|
-
break;
|
|
559
|
-
case TYVoiceChatState.Listening:
|
|
560
|
-
this.listen.update(state);
|
|
561
|
-
break;
|
|
562
|
-
case TYVoiceChatState.Thinking:
|
|
563
|
-
this.think.update(state);
|
|
564
|
-
break;
|
|
565
|
-
case TYVoiceChatState.Responding:
|
|
566
|
-
this.speak.update(state);
|
|
567
|
-
break;
|
|
568
|
-
default:
|
|
569
|
-
this.idle.update(state);
|
|
570
|
-
break;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
export { State, Hello, Idle, Listen, Think, Speak };
|
|
577
|
-
export default AnimationManager;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AppConstants
|
|
3
|
-
*
|
|
4
|
-
* Derived from gaussian-splat-renderer-for-lam
|
|
5
|
-
* Animation state constants and ARKit blendshape mappings.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Voice chat state enumeration
|
|
10
|
-
* Controls the rendering and behavior modes of the avatar
|
|
11
|
-
*/
|
|
12
|
-
export const TYVoiceChatState = {
|
|
13
|
-
Idle: 'Idle', // Idle/waiting state
|
|
14
|
-
Listening: 'Listening', // Listening to user input
|
|
15
|
-
Responding: 'Responding', // Speaking/responding animation
|
|
16
|
-
Thinking: 'Thinking' // Processing/thinking animation
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* ARKit blendshape names (52 expressions)
|
|
21
|
-
* Used for facial expression mapping from ARKit face tracking
|
|
22
|
-
*/
|
|
23
|
-
export const ARKitBlendshapes = [
|
|
24
|
-
'browDownLeft',
|
|
25
|
-
'browDownRight',
|
|
26
|
-
'browInnerUp',
|
|
27
|
-
'browOuterUpLeft',
|
|
28
|
-
'browOuterUpRight',
|
|
29
|
-
'cheekPuff',
|
|
30
|
-
'cheekSquintLeft',
|
|
31
|
-
'cheekSquintRight',
|
|
32
|
-
'eyeBlinkLeft',
|
|
33
|
-
'eyeBlinkRight',
|
|
34
|
-
'eyeLookDownLeft',
|
|
35
|
-
'eyeLookDownRight',
|
|
36
|
-
'eyeLookInLeft',
|
|
37
|
-
'eyeLookInRight',
|
|
38
|
-
'eyeLookOutLeft',
|
|
39
|
-
'eyeLookOutRight',
|
|
40
|
-
'eyeLookUpLeft',
|
|
41
|
-
'eyeLookUpRight',
|
|
42
|
-
'eyeSquintLeft',
|
|
43
|
-
'eyeSquintRight',
|
|
44
|
-
'eyeWideLeft',
|
|
45
|
-
'eyeWideRight',
|
|
46
|
-
'jawForward',
|
|
47
|
-
'jawLeft',
|
|
48
|
-
'jawOpen',
|
|
49
|
-
'jawRight',
|
|
50
|
-
'mouthClose',
|
|
51
|
-
'mouthDimpleLeft',
|
|
52
|
-
'mouthDimpleRight',
|
|
53
|
-
'mouthFrownLeft',
|
|
54
|
-
'mouthFrownRight',
|
|
55
|
-
'mouthFunnel',
|
|
56
|
-
'mouthLeft',
|
|
57
|
-
'mouthLowerDownLeft',
|
|
58
|
-
'mouthLowerDownRight',
|
|
59
|
-
'mouthPressLeft',
|
|
60
|
-
'mouthPressRight',
|
|
61
|
-
'mouthPucker',
|
|
62
|
-
'mouthRight',
|
|
63
|
-
'mouthRollLower',
|
|
64
|
-
'mouthRollUpper',
|
|
65
|
-
'mouthShrugLower',
|
|
66
|
-
'mouthShrugUpper',
|
|
67
|
-
'mouthSmileLeft',
|
|
68
|
-
'mouthSmileRight',
|
|
69
|
-
'mouthStretchLeft',
|
|
70
|
-
'mouthStretchRight',
|
|
71
|
-
'mouthUpperUpLeft',
|
|
72
|
-
'mouthUpperUpRight',
|
|
73
|
-
'noseSneerLeft',
|
|
74
|
-
'noseSneerRight',
|
|
75
|
-
'tongueOut'
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* FLAME model bone names
|
|
80
|
-
*/
|
|
81
|
-
export const FlameBoneNames = [
|
|
82
|
-
'root',
|
|
83
|
-
'neck',
|
|
84
|
-
'jaw',
|
|
85
|
-
'leftEye',
|
|
86
|
-
'rightEye'
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Constants derived from the arrays
|
|
91
|
-
*/
|
|
92
|
-
export const ARKIT_BLENDSHAPES_COUNT = ARKitBlendshapes.length;
|
|
93
|
-
export const FLAME_BONES_COUNT = FlameBoneNames.length;
|
|
94
|
-
|
|
95
|
-
export default {
|
|
96
|
-
TYVoiceChatState,
|
|
97
|
-
ARKitBlendshapes,
|
|
98
|
-
FlameBoneNames,
|
|
99
|
-
ARKIT_BLENDSHAPES_COUNT,
|
|
100
|
-
FLAME_BONES_COUNT
|
|
101
|
-
};
|