@aura3d/animation 1.4.0 → 1.4.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/package.json +36 -3
- package/src/AnimationAction.ts +0 -166
- package/src/AnimationClip.ts +0 -63
- package/src/AnimationClipEvents.ts +0 -501
- package/src/AnimationClipRegistry.ts +0 -404
- package/src/AnimationController.ts +0 -1017
- package/src/AnimationEvents.ts +0 -34
- package/src/AnimationLayer.ts +0 -73
- package/src/AnimationMixer.ts +0 -402
- package/src/AnimationStateGraph.ts +0 -105
- package/src/AnimationStateMachine.ts +0 -265
- package/src/AnimationTrack.ts +0 -176
- package/src/BlendTree.ts +0 -89
- package/src/Bone.ts +0 -40
- package/src/CrowdAnimation.ts +0 -25
- package/src/ECSAnimationBridge.ts +0 -30
- package/src/FighterAnimationAdapter.ts +0 -124
- package/src/FootIk.ts +0 -251
- package/src/HumanoidBoneInference.ts +0 -323
- package/src/HumanoidRetargeting.ts +0 -700
- package/src/IK.ts +0 -117
- package/src/Inertialization.ts +0 -269
- package/src/Keyframe.ts +0 -159
- package/src/LocomotionController.ts +0 -155
- package/src/LocomotionKit.ts +0 -89
- package/src/MotionMatchingFixtures.ts +0 -254
- package/src/MotionQuality.ts +0 -182
- package/src/RigQuality.ts +0 -101
- package/src/RootMotion.ts +0 -124
- package/src/SceneAnimationBridge.ts +0 -80
- package/src/SecondaryAnimationFixtures.ts +0 -210
- package/src/Skeleton.ts +0 -78
- package/src/Skinning.ts +0 -18
- package/src/SpringBones.ts +0 -243
- package/src/browser-index.ts +0 -66
- package/src/index.ts +0 -66
- package/src/library/performanceStateGraph.ts +0 -173
- package/src/library/standardHumanoidClips.ts +0 -659
- package/src/threejs-compatibility/AnimationAction.ts +0 -27
- package/src/threejs-compatibility/AnimationClip.ts +0 -16
- package/src/threejs-compatibility/AnimationDiagnostics.ts +0 -42
- package/src/threejs-compatibility/AnimationMixer.ts +0 -24
- package/src/threejs-compatibility/MorphTargetMixer.ts +0 -16
- package/src/threejs-compatibility/Skeleton.ts +0 -9
- package/src/threejs-compatibility/SkinnedMesh.ts +0 -12
- package/src/threejs-compatibility/index.ts +0 -11
|
@@ -1,659 +0,0 @@
|
|
|
1
|
-
import { AnimationTrack } from "../AnimationTrack.js";
|
|
2
|
-
import {
|
|
3
|
-
AnimationClipRegistry,
|
|
4
|
-
createAnimationClipRegistry,
|
|
5
|
-
type AnimationClipDefinition
|
|
6
|
-
} from "../AnimationClipRegistry.js";
|
|
7
|
-
import { HUMANOID_BONES, type HumanoidBoneName, type HumanoidRigDefinition } from "../HumanoidRetargeting.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The STANDARD performance vocabulary (Phase 2.4). These 14 ids are the contract the Animation
|
|
11
|
-
* Studio drives — every shared library, retarget target, and state graph speaks exactly these.
|
|
12
|
-
*/
|
|
13
|
-
export const STANDARD_CLIP_IDS = [
|
|
14
|
-
"idle", "talk", "gesture", "point", "nod", "wave",
|
|
15
|
-
"walk", "run", "react",
|
|
16
|
-
"sit", "shrug", "cross_arms", "salute", "shake_head"
|
|
17
|
-
] as const;
|
|
18
|
-
export type StandardClipId = typeof STANDARD_CLIP_IDS[number];
|
|
19
|
-
|
|
20
|
-
/** Locomotion subset of the vocabulary (used by the locomotion blend / studio clip-map check). */
|
|
21
|
-
export const STANDARD_LOCOMOTION_CLIP_IDS = ["idle", "walk", "run"] as const;
|
|
22
|
-
|
|
23
|
-
type Quat = readonly [number, number, number, number];
|
|
24
|
-
type Vec3 = readonly [number, number, number];
|
|
25
|
-
|
|
26
|
-
const FRAME_RATE = 30;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* The rig-neutral humanoid these clips are authored against. Bone names are the canonical
|
|
30
|
-
* {@link HUMANOID_BONES} (identity bindings), so an identity retarget map is a no-op and the clips
|
|
31
|
-
* can be retargeted onto any inferred character rig via {@link createHumanoidRetargetingMap}.
|
|
32
|
-
*/
|
|
33
|
-
export const STANDARD_LIBRARY_RIG: HumanoidRigDefinition = {
|
|
34
|
-
id: "aura3d.standard-humanoid",
|
|
35
|
-
name: "Aura3D Standard Humanoid (shared clip library rig)",
|
|
36
|
-
units: "meters",
|
|
37
|
-
facingAxis: "z",
|
|
38
|
-
scale: 1,
|
|
39
|
-
bones: Object.fromEntries(
|
|
40
|
-
HUMANOID_BONES.map((bone) => [bone, { name: bone }])
|
|
41
|
-
) as HumanoidRigDefinition["bones"],
|
|
42
|
-
metadata: {
|
|
43
|
-
library: "aura3d.standard-humanoid-clips",
|
|
44
|
-
clipVocabulary: [...STANDARD_CLIP_IDS]
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/** Euler (radians, XYZ) -> quaternion. Compact authoring helper for the procedural clips. */
|
|
49
|
-
function euler(x: number, y: number, z: number): Quat {
|
|
50
|
-
const cx = Math.cos(x / 2);
|
|
51
|
-
const sx = Math.sin(x / 2);
|
|
52
|
-
const cy = Math.cos(y / 2);
|
|
53
|
-
const sy = Math.sin(y / 2);
|
|
54
|
-
const cz = Math.cos(z / 2);
|
|
55
|
-
const sz = Math.sin(z / 2);
|
|
56
|
-
return [
|
|
57
|
-
sx * cy * cz - cx * sy * sz,
|
|
58
|
-
cx * sy * cz + sx * cy * sz,
|
|
59
|
-
cx * cy * sz - sx * sy * cz,
|
|
60
|
-
cx * cy * cz + sx * sy * sz
|
|
61
|
-
];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const IDENTITY: Quat = [0, 0, 0, 1];
|
|
65
|
-
|
|
66
|
-
/** Build a looping quaternion (rotation) track for a bone from per-frame Euler samples. */
|
|
67
|
-
function rotationTrack(
|
|
68
|
-
bone: HumanoidBoneName,
|
|
69
|
-
duration: number,
|
|
70
|
-
samples: readonly { readonly t: number; readonly e: Vec3 }[]
|
|
71
|
-
): AnimationTrack {
|
|
72
|
-
const keyframes = samples.map((sample) => ({
|
|
73
|
-
time: sample.t,
|
|
74
|
-
value: euler(sample.e[0], sample.e[1], sample.e[2]) as [number, number, number, number],
|
|
75
|
-
interpolation: "linear" as const
|
|
76
|
-
}));
|
|
77
|
-
// Pad to duration so the clip loops cleanly back to its first pose.
|
|
78
|
-
if (keyframes[keyframes.length - 1]!.time < duration) {
|
|
79
|
-
keyframes.push({ time: duration, value: [...keyframes[0]!.value], interpolation: "linear" });
|
|
80
|
-
}
|
|
81
|
-
return new AnimationTrack({ target: `${bone}.rotation`, valueType: "quaternion", keyframes });
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Build a looping vector3 (translation) track for a bone, used for hip bob / sway. */
|
|
85
|
-
function translationTrack(
|
|
86
|
-
bone: HumanoidBoneName,
|
|
87
|
-
duration: number,
|
|
88
|
-
samples: readonly { readonly t: number; readonly p: Vec3 }[]
|
|
89
|
-
): AnimationTrack {
|
|
90
|
-
const keyframes = samples.map((sample) => ({
|
|
91
|
-
time: sample.t,
|
|
92
|
-
value: [...sample.p] as [number, number, number],
|
|
93
|
-
interpolation: "linear" as const
|
|
94
|
-
}));
|
|
95
|
-
if (keyframes[keyframes.length - 1]!.time < duration) {
|
|
96
|
-
keyframes.push({ time: duration, value: [...keyframes[0]!.value], interpolation: "linear" });
|
|
97
|
-
}
|
|
98
|
-
return new AnimationTrack({ target: `${bone}.translation`, valueType: "vector3", keyframes });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function clip(
|
|
102
|
-
id: StandardClipId,
|
|
103
|
-
duration: number,
|
|
104
|
-
tracks: readonly AnimationTrack[],
|
|
105
|
-
loop: boolean,
|
|
106
|
-
tags: readonly string[]
|
|
107
|
-
): AnimationClipDefinition<StandardClipId> {
|
|
108
|
-
return {
|
|
109
|
-
id,
|
|
110
|
-
name: id,
|
|
111
|
-
duration,
|
|
112
|
-
frameRate: FRAME_RATE,
|
|
113
|
-
loop,
|
|
114
|
-
tags,
|
|
115
|
-
tracks,
|
|
116
|
-
source: "aura3d.standard-humanoid-clips",
|
|
117
|
-
metadata: { rig: STANDARD_LIBRARY_RIG.id, procedural: true }
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// --- idle: visible breathing + head turns + weight-shift sway -----------------------------------
|
|
122
|
-
// Amplitudes are deliberately readable (not the old ±2° "alive but invisible" idle): spine breathes
|
|
123
|
-
// ~0.16 rad, head turns ~0.34 rad peak-to-peak, hips shift ~6 cm laterally so a viewer can tell the
|
|
124
|
-
// character is standing/living rather than frozen.
|
|
125
|
-
function idleClip(): AnimationClipDefinition<StandardClipId> {
|
|
126
|
-
const d = 4;
|
|
127
|
-
return clip(
|
|
128
|
-
"idle",
|
|
129
|
-
d,
|
|
130
|
-
[
|
|
131
|
-
// Weight shift left -> right plus a clear breathing bob.
|
|
132
|
-
translationTrack("hips", d, [
|
|
133
|
-
{ t: 0, p: [0.03, 0, 0] },
|
|
134
|
-
{ t: 1, p: [0.02, 0.03, 0] },
|
|
135
|
-
{ t: 2, p: [-0.03, 0.01, 0] },
|
|
136
|
-
{ t: 3, p: [-0.02, 0.03, 0] }
|
|
137
|
-
]),
|
|
138
|
-
// Breathing: spine flexes/extends well past the visibility floor (~0.16 rad swing).
|
|
139
|
-
rotationTrack("spine", d, [
|
|
140
|
-
{ t: 0, e: [0.04, 0, 0.04] },
|
|
141
|
-
{ t: 1, e: [0.18, 0, 0.03] },
|
|
142
|
-
{ t: 2, e: [0.05, 0, -0.05] },
|
|
143
|
-
{ t: 3, e: [0.16, 0, -0.03] }
|
|
144
|
-
]),
|
|
145
|
-
rotationTrack("chest", d, [
|
|
146
|
-
{ t: 0, e: [0, 0, 0] },
|
|
147
|
-
{ t: 1, e: [0.12, 0, 0.02] },
|
|
148
|
-
{ t: 2, e: [0.02, 0, 0] },
|
|
149
|
-
{ t: 3, e: [0.1, 0, -0.02] }
|
|
150
|
-
]),
|
|
151
|
-
// Idle head turns: a slow look-around, ~0.34 rad peak-to-peak yaw + a little pitch.
|
|
152
|
-
rotationTrack("head", d, [
|
|
153
|
-
{ t: 0, e: [0.02, 0.18, 0] },
|
|
154
|
-
{ t: 1, e: [0.06, 0.1, 0.04] },
|
|
155
|
-
{ t: 2, e: [0.04, -0.16, 0] },
|
|
156
|
-
{ t: 3, e: [-0.04, -0.06, -0.04] }
|
|
157
|
-
]),
|
|
158
|
-
rotationTrack("neck", d, [
|
|
159
|
-
{ t: 0, e: [0, 0.08, 0] },
|
|
160
|
-
{ t: 2, e: [0, -0.08, 0] }
|
|
161
|
-
])
|
|
162
|
-
],
|
|
163
|
-
true,
|
|
164
|
-
["idle", "performance", "loop"]
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// --- talk: real conversational hand gestures + torso + head -------------------------------------
|
|
169
|
-
// A speaking beat must read as "talking with the hands": both arms swing through ~0.5+ rad, the
|
|
170
|
-
// forearms beat with the cadence, the chest counter-rotates, and the head punctuates.
|
|
171
|
-
function talkClip(): AnimationClipDefinition<StandardClipId> {
|
|
172
|
-
const d = 2;
|
|
173
|
-
return clip(
|
|
174
|
-
"talk",
|
|
175
|
-
d,
|
|
176
|
-
[
|
|
177
|
-
rotationTrack("head", d, [
|
|
178
|
-
{ t: 0, e: [0, 0.04, 0] },
|
|
179
|
-
{ t: 0.5, e: [0.16, 0.12, 0] },
|
|
180
|
-
{ t: 1, e: [-0.1, -0.1, 0] },
|
|
181
|
-
{ t: 1.5, e: [0.14, 0.06, 0] }
|
|
182
|
-
]),
|
|
183
|
-
rotationTrack("neck", d, [
|
|
184
|
-
{ t: 0, e: [0, 0, 0] },
|
|
185
|
-
{ t: 0.5, e: [0.09, 0.05, 0] },
|
|
186
|
-
{ t: 1.5, e: [-0.07, -0.05, 0] }
|
|
187
|
-
]),
|
|
188
|
-
rotationTrack("chest", d, [
|
|
189
|
-
{ t: 0, e: [0, -0.06, 0] },
|
|
190
|
-
{ t: 0.6, e: [0.04, 0.12, 0] },
|
|
191
|
-
{ t: 1.3, e: [0.02, -0.1, 0] }
|
|
192
|
-
]),
|
|
193
|
-
// Right hand does the lead gesture: up-and-out beats (~0.55 rad swing in Z plus pitch).
|
|
194
|
-
rotationTrack("rightUpperArm", d, [
|
|
195
|
-
{ t: 0, e: [0, 0, -0.2] },
|
|
196
|
-
{ t: 0.6, e: [0.45, -0.1, -0.62] },
|
|
197
|
-
{ t: 1.2, e: [0.1, 0, -0.25] },
|
|
198
|
-
{ t: 1.7, e: [0.35, -0.05, -0.5] }
|
|
199
|
-
]),
|
|
200
|
-
rotationTrack("rightLowerArm", d, [
|
|
201
|
-
{ t: 0, e: [0, 0, -0.35] },
|
|
202
|
-
{ t: 0.6, e: [0, -0.2, -0.95] },
|
|
203
|
-
{ t: 1.2, e: [0, 0, -0.4] },
|
|
204
|
-
{ t: 1.7, e: [0, -0.15, -0.8] }
|
|
205
|
-
]),
|
|
206
|
-
// Left hand supports with its own beat (~0.5 rad swing).
|
|
207
|
-
rotationTrack("leftUpperArm", d, [
|
|
208
|
-
{ t: 0, e: [0, 0, 0.2] },
|
|
209
|
-
{ t: 0.8, e: [0.35, 0.1, 0.6] },
|
|
210
|
-
{ t: 1.5, e: [0.1, 0, 0.28] }
|
|
211
|
-
]),
|
|
212
|
-
rotationTrack("leftLowerArm", d, [
|
|
213
|
-
{ t: 0, e: [0, 0, 0.3] },
|
|
214
|
-
{ t: 0.8, e: [0, 0.15, 0.75] },
|
|
215
|
-
{ t: 1.5, e: [0, 0, 0.35] }
|
|
216
|
-
])
|
|
217
|
-
],
|
|
218
|
-
true,
|
|
219
|
-
["talk", "performance", "loop", "upper-body"]
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// --- gesture: one-shot BIG expressive both-arm sweep --------------------------------------------
|
|
224
|
-
// A full presentational sweep: the lead arm raises ~1.1 rad and opens out, the off arm mirrors, the
|
|
225
|
-
// torso opens with it. Unmistakably a "big gesture", not a twitch.
|
|
226
|
-
function gestureClip(): AnimationClipDefinition<StandardClipId> {
|
|
227
|
-
const d = 1.1;
|
|
228
|
-
return clip(
|
|
229
|
-
"gesture",
|
|
230
|
-
d,
|
|
231
|
-
[
|
|
232
|
-
rotationTrack("rightUpperArm", d, [
|
|
233
|
-
{ t: 0, e: [0, 0, -0.2] },
|
|
234
|
-
{ t: 0.45, e: [1.1, -0.35, -1.15] },
|
|
235
|
-
{ t: 0.8, e: [0.7, -0.2, -0.85] }
|
|
236
|
-
]),
|
|
237
|
-
rotationTrack("rightLowerArm", d, [
|
|
238
|
-
{ t: 0, e: [0, 0, -0.3] },
|
|
239
|
-
{ t: 0.45, e: [0, -0.6, -1.2] },
|
|
240
|
-
{ t: 0.8, e: [0, -0.3, -0.8] }
|
|
241
|
-
]),
|
|
242
|
-
rotationTrack("leftUpperArm", d, [
|
|
243
|
-
{ t: 0, e: [0, 0, 0.2] },
|
|
244
|
-
{ t: 0.5, e: [0.95, 0.35, 1.1] },
|
|
245
|
-
{ t: 0.85, e: [0.6, 0.2, 0.8] }
|
|
246
|
-
]),
|
|
247
|
-
rotationTrack("leftLowerArm", d, [
|
|
248
|
-
{ t: 0, e: [0, 0, 0.3] },
|
|
249
|
-
{ t: 0.5, e: [0, 0.5, 1.1] }
|
|
250
|
-
]),
|
|
251
|
-
rotationTrack("chest", d, [
|
|
252
|
-
{ t: 0, e: [0, 0, 0] },
|
|
253
|
-
{ t: 0.45, e: [-0.08, 0.28, 0] }
|
|
254
|
-
]),
|
|
255
|
-
rotationTrack("head", d, [
|
|
256
|
-
{ t: 0, e: [0, 0, 0] },
|
|
257
|
-
{ t: 0.45, e: [-0.12, 0.1, 0] }
|
|
258
|
-
])
|
|
259
|
-
],
|
|
260
|
-
false,
|
|
261
|
-
["gesture", "performance", "one-shot", "upper-body"]
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// --- point: one-shot directional arm extension --------------------------------------------------
|
|
266
|
-
function pointClip(): AnimationClipDefinition<StandardClipId> {
|
|
267
|
-
const d = 0.9;
|
|
268
|
-
return clip(
|
|
269
|
-
"point",
|
|
270
|
-
d,
|
|
271
|
-
[
|
|
272
|
-
rotationTrack("rightShoulder", d, [
|
|
273
|
-
{ t: 0, e: [0, 0, 0] },
|
|
274
|
-
{ t: 0.35, e: [0, -0.15, 0] }
|
|
275
|
-
]),
|
|
276
|
-
rotationTrack("rightUpperArm", d, [
|
|
277
|
-
{ t: 0, e: [0, 0, -0.2] },
|
|
278
|
-
{ t: 0.35, e: [0, -0.25, -1.55] },
|
|
279
|
-
{ t: 0.7, e: [0, -0.25, -1.55] }
|
|
280
|
-
]),
|
|
281
|
-
rotationTrack("rightLowerArm", d, [
|
|
282
|
-
{ t: 0, e: [0, 0, -0.35] },
|
|
283
|
-
{ t: 0.35, e: [0, 0, 0] },
|
|
284
|
-
{ t: 0.7, e: [0, 0, 0] }
|
|
285
|
-
]),
|
|
286
|
-
rotationTrack("head", d, [
|
|
287
|
-
{ t: 0, e: [0, 0, 0] },
|
|
288
|
-
{ t: 0.35, e: [0, -0.35, 0] },
|
|
289
|
-
{ t: 0.7, e: [0, -0.3, 0] }
|
|
290
|
-
])
|
|
291
|
-
],
|
|
292
|
-
false,
|
|
293
|
-
["point", "performance", "one-shot", "upper-body"]
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// --- nod: one-shot affirmative head nod ---------------------------------------------------------
|
|
298
|
-
function nodClip(): AnimationClipDefinition<StandardClipId> {
|
|
299
|
-
const d = 0.8;
|
|
300
|
-
return clip(
|
|
301
|
-
"nod",
|
|
302
|
-
d,
|
|
303
|
-
[
|
|
304
|
-
rotationTrack("head", d, [
|
|
305
|
-
{ t: 0, e: [-0.06, 0, 0] },
|
|
306
|
-
{ t: 0.2, e: [0.42, 0, 0] },
|
|
307
|
-
{ t: 0.4, e: [-0.1, 0, 0] },
|
|
308
|
-
{ t: 0.6, e: [0.34, 0, 0] }
|
|
309
|
-
]),
|
|
310
|
-
rotationTrack("neck", d, [
|
|
311
|
-
{ t: 0, e: [-0.02, 0, 0] },
|
|
312
|
-
{ t: 0.2, e: [0.18, 0, 0] },
|
|
313
|
-
{ t: 0.4, e: [-0.02, 0, 0] },
|
|
314
|
-
{ t: 0.6, e: [0.14, 0, 0] }
|
|
315
|
-
])
|
|
316
|
-
],
|
|
317
|
-
false,
|
|
318
|
-
["nod", "performance", "one-shot"]
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// --- react: one-shot surprised recoil (torso + arms + head) -------------------------------------
|
|
323
|
-
function reactClip(): AnimationClipDefinition<StandardClipId> {
|
|
324
|
-
const d = 1;
|
|
325
|
-
return clip(
|
|
326
|
-
"react",
|
|
327
|
-
d,
|
|
328
|
-
[
|
|
329
|
-
rotationTrack("spine", d, [
|
|
330
|
-
{ t: 0, e: [0, 0, 0] },
|
|
331
|
-
{ t: 0.18, e: [-0.5, 0, 0] },
|
|
332
|
-
{ t: 0.6, e: [-0.18, 0, 0] }
|
|
333
|
-
]),
|
|
334
|
-
rotationTrack("head", d, [
|
|
335
|
-
{ t: 0, e: [0, 0, 0] },
|
|
336
|
-
{ t: 0.18, e: [-0.4, 0, 0] },
|
|
337
|
-
{ t: 0.6, e: [-0.12, 0, 0] }
|
|
338
|
-
]),
|
|
339
|
-
rotationTrack("leftUpperArm", d, [
|
|
340
|
-
{ t: 0, e: [0, 0, 0.2] },
|
|
341
|
-
{ t: 0.2, e: [-0.7, 0, 1.1] },
|
|
342
|
-
{ t: 0.6, e: [-0.3, 0, 0.6] }
|
|
343
|
-
]),
|
|
344
|
-
rotationTrack("rightUpperArm", d, [
|
|
345
|
-
{ t: 0, e: [0, 0, -0.2] },
|
|
346
|
-
{ t: 0.2, e: [-0.7, 0, -1.1] },
|
|
347
|
-
{ t: 0.6, e: [-0.3, 0, -0.6] }
|
|
348
|
-
]),
|
|
349
|
-
translationTrack("hips", d, [
|
|
350
|
-
{ t: 0, p: [0, 0, 0] },
|
|
351
|
-
{ t: 0.18, p: [0, 0, -0.12] },
|
|
352
|
-
{ t: 0.6, p: [0, 0, -0.04] }
|
|
353
|
-
])
|
|
354
|
-
],
|
|
355
|
-
false,
|
|
356
|
-
["react", "performance", "one-shot"]
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/** Build a symmetric two-step leg/arm cycle for walk or run. */
|
|
361
|
-
function legCycle(id: "walk" | "run", duration: number, amplitude: number, lift: number, bob: number): AnimationClipDefinition<StandardClipId> {
|
|
362
|
-
const half = duration / 2;
|
|
363
|
-
return clip(
|
|
364
|
-
id,
|
|
365
|
-
duration,
|
|
366
|
-
[
|
|
367
|
-
// Hip vertical bob (two bobs per stride).
|
|
368
|
-
translationTrack("hips", duration, [
|
|
369
|
-
{ t: 0, p: [0, 0, 0] },
|
|
370
|
-
{ t: half / 2, p: [0, bob, 0] },
|
|
371
|
-
{ t: half, p: [0, 0, 0] },
|
|
372
|
-
{ t: half + half / 2, p: [0, bob, 0] }
|
|
373
|
-
]),
|
|
374
|
-
// Legs swing in opposition.
|
|
375
|
-
rotationTrack("leftUpperLeg", duration, [
|
|
376
|
-
{ t: 0, e: [amplitude, 0, 0] },
|
|
377
|
-
{ t: half, e: [-amplitude, 0, 0] }
|
|
378
|
-
]),
|
|
379
|
-
rotationTrack("rightUpperLeg", duration, [
|
|
380
|
-
{ t: 0, e: [-amplitude, 0, 0] },
|
|
381
|
-
{ t: half, e: [amplitude, 0, 0] }
|
|
382
|
-
]),
|
|
383
|
-
rotationTrack("leftLowerLeg", duration, [
|
|
384
|
-
{ t: 0, e: [0, 0, 0] },
|
|
385
|
-
{ t: half / 2, e: [lift, 0, 0] },
|
|
386
|
-
{ t: half, e: [0, 0, 0] }
|
|
387
|
-
]),
|
|
388
|
-
rotationTrack("rightLowerLeg", duration, [
|
|
389
|
-
{ t: 0, e: [lift, 0, 0] },
|
|
390
|
-
{ t: half / 2, e: [0, 0, 0] },
|
|
391
|
-
{ t: half, e: [lift, 0, 0] }
|
|
392
|
-
]),
|
|
393
|
-
// Arms counter-swing to the legs.
|
|
394
|
-
rotationTrack("leftUpperArm", duration, [
|
|
395
|
-
{ t: 0, e: [-amplitude * 0.8, 0, 0.15] },
|
|
396
|
-
{ t: half, e: [amplitude * 0.8, 0, 0.15] }
|
|
397
|
-
]),
|
|
398
|
-
rotationTrack("rightUpperArm", duration, [
|
|
399
|
-
{ t: 0, e: [amplitude * 0.8, 0, -0.15] },
|
|
400
|
-
{ t: half, e: [-amplitude * 0.8, 0, -0.15] }
|
|
401
|
-
]),
|
|
402
|
-
rotationTrack("spine", duration, [
|
|
403
|
-
{ t: 0, e: [0.05, amplitude * 0.15, 0] },
|
|
404
|
-
{ t: half, e: [0.05, -amplitude * 0.15, 0] }
|
|
405
|
-
])
|
|
406
|
-
],
|
|
407
|
-
true,
|
|
408
|
-
[id, "locomotion", "loop", "lower-body"]
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Walk: strong staged stride. Upper-leg swing of ±0.55 rad gives a 1.1 rad peak-to-peak step that
|
|
413
|
-
// reads clearly even on a previz rig; knees lift, arms counter-swing, torso twists.
|
|
414
|
-
function walkClip(): AnimationClipDefinition<StandardClipId> {
|
|
415
|
-
return legCycle("walk", 1, 0.55, 0.7, 0.04);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Run: bigger and faster (rig permitting) — ±0.9 rad upper-leg swing, deep knee lift, strong bob.
|
|
419
|
-
function runClip(): AnimationClipDefinition<StandardClipId> {
|
|
420
|
-
return legCycle("run", 0.6, 0.9, 1.1, 0.07);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// --- wave: one-shot greeting wave (right arm) ---------------------------------------------------
|
|
424
|
-
function waveClip(): AnimationClipDefinition<StandardClipId> {
|
|
425
|
-
const d = 1.2;
|
|
426
|
-
return clip(
|
|
427
|
-
"wave",
|
|
428
|
-
d,
|
|
429
|
-
[
|
|
430
|
-
rotationTrack("rightUpperArm", d, [
|
|
431
|
-
{ t: 0, e: [0, 0, -0.2] },
|
|
432
|
-
{ t: 0.3, e: [0.5, -0.2, -1.4] },
|
|
433
|
-
{ t: 0.5, e: [0.6, -0.1, -1.55] },
|
|
434
|
-
{ t: 0.7, e: [0.5, -0.2, -1.4] },
|
|
435
|
-
{ t: 0.9, e: [0.6, -0.1, -1.55] },
|
|
436
|
-
{ t: 1.1, e: [0.4, -0.15, -1.3] }
|
|
437
|
-
]),
|
|
438
|
-
rotationTrack("rightLowerArm", d, [
|
|
439
|
-
{ t: 0, e: [0, 0, -0.3] },
|
|
440
|
-
{ t: 0.3, e: [0, -0.3, -0.8] },
|
|
441
|
-
{ t: 0.5, e: [0, -0.2, -0.9] },
|
|
442
|
-
{ t: 0.7, e: [0, -0.3, -0.8] },
|
|
443
|
-
{ t: 0.9, e: [0, -0.2, -0.9] },
|
|
444
|
-
{ t: 1.1, e: [0, -0.25, -0.7] }
|
|
445
|
-
]),
|
|
446
|
-
rotationTrack("head", d, [
|
|
447
|
-
{ t: 0, e: [0, 0, 0] },
|
|
448
|
-
{ t: 0.3, e: [0.05, 0.12, 0] },
|
|
449
|
-
{ t: 1.1, e: [0, 0.08, 0] }
|
|
450
|
-
])
|
|
451
|
-
],
|
|
452
|
-
false,
|
|
453
|
-
["wave", "performance", "one-shot", "upper-body"]
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// --- sit: seated resting pose loop ---------------------------------------------------------------
|
|
458
|
-
function sitClip(): AnimationClipDefinition<StandardClipId> {
|
|
459
|
-
const d = 3;
|
|
460
|
-
return clip(
|
|
461
|
-
"sit",
|
|
462
|
-
d,
|
|
463
|
-
[
|
|
464
|
-
translationTrack("hips", d, [
|
|
465
|
-
{ t: 0, p: [0, -0.45, 0] },
|
|
466
|
-
{ t: 1.5, p: [0, -0.42, 0] },
|
|
467
|
-
{ t: 3, p: [0, -0.45, 0] }
|
|
468
|
-
]),
|
|
469
|
-
rotationTrack("hips", d, [
|
|
470
|
-
{ t: 0, e: [0.35, 0, 0] },
|
|
471
|
-
{ t: 1.5, e: [0.38, 0, 0] },
|
|
472
|
-
{ t: 3, e: [0.35, 0, 0] }
|
|
473
|
-
]),
|
|
474
|
-
rotationTrack("leftUpperLeg", d, [
|
|
475
|
-
{ t: 0, e: [-1.1, 0, 0] },
|
|
476
|
-
{ t: 1.5, e: [-1.05, 0, 0] },
|
|
477
|
-
{ t: 3, e: [-1.1, 0, 0] }
|
|
478
|
-
]),
|
|
479
|
-
rotationTrack("rightUpperLeg", d, [
|
|
480
|
-
{ t: 0, e: [-1.1, 0, 0] },
|
|
481
|
-
{ t: 1.5, e: [-1.15, 0, 0] },
|
|
482
|
-
{ t: 3, e: [-1.1, 0, 0] }
|
|
483
|
-
]),
|
|
484
|
-
rotationTrack("leftLowerLeg", d, [
|
|
485
|
-
{ t: 0, e: [1.6, 0, 0] },
|
|
486
|
-
{ t: 1.5, e: [1.55, 0, 0] },
|
|
487
|
-
{ t: 3, e: [1.6, 0, 0] }
|
|
488
|
-
]),
|
|
489
|
-
rotationTrack("rightLowerLeg", d, [
|
|
490
|
-
{ t: 0, e: [1.6, 0, 0] },
|
|
491
|
-
{ t: 1.5, e: [1.65, 0, 0] },
|
|
492
|
-
{ t: 3, e: [1.6, 0, 0] }
|
|
493
|
-
]),
|
|
494
|
-
rotationTrack("spine", d, [
|
|
495
|
-
{ t: 0, e: [0.18, 0, 0] },
|
|
496
|
-
{ t: 1.5, e: [0.0, 0, 0] },
|
|
497
|
-
{ t: 3, e: [0.18, 0, 0] }
|
|
498
|
-
]),
|
|
499
|
-
rotationTrack("leftUpperArm", d, [
|
|
500
|
-
{ t: 0, e: [0.15, 0, 0.1] },
|
|
501
|
-
{ t: 1.5, e: [0.12, 0, 0.08] },
|
|
502
|
-
{ t: 3, e: [0.15, 0, 0.1] }
|
|
503
|
-
])
|
|
504
|
-
],
|
|
505
|
-
true,
|
|
506
|
-
["sit", "performance", "loop", "full-body"]
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// --- shrug: one-shot shoulder shrug -------------------------------------------------------------
|
|
511
|
-
function shrugClip(): AnimationClipDefinition<StandardClipId> {
|
|
512
|
-
const d = 0.9;
|
|
513
|
-
return clip(
|
|
514
|
-
"shrug",
|
|
515
|
-
d,
|
|
516
|
-
[
|
|
517
|
-
rotationTrack("leftUpperArm", d, [
|
|
518
|
-
{ t: 0, e: [0, 0, 0.2] },
|
|
519
|
-
{ t: 0.3, e: [0, 0, 0.9] },
|
|
520
|
-
{ t: 0.6, e: [0, 0, 0.3] }
|
|
521
|
-
]),
|
|
522
|
-
rotationTrack("rightUpperArm", d, [
|
|
523
|
-
{ t: 0, e: [0, 0, -0.2] },
|
|
524
|
-
{ t: 0.3, e: [0, 0, -0.9] },
|
|
525
|
-
{ t: 0.6, e: [0, 0, -0.3] }
|
|
526
|
-
]),
|
|
527
|
-
rotationTrack("spine", d, [
|
|
528
|
-
{ t: 0, e: [0, 0, 0] },
|
|
529
|
-
{ t: 0.3, e: [0.06, 0, 0] },
|
|
530
|
-
{ t: 0.6, e: [0, 0, 0] }
|
|
531
|
-
])
|
|
532
|
-
],
|
|
533
|
-
false,
|
|
534
|
-
["shrug", "performance", "one-shot", "upper-body"]
|
|
535
|
-
);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// --- cross_arms: standing arms-crossed pose loop ------------------------------------------------
|
|
539
|
-
function crossArmsClip(): AnimationClipDefinition<StandardClipId> {
|
|
540
|
-
const d = 3;
|
|
541
|
-
return clip(
|
|
542
|
-
"cross_arms",
|
|
543
|
-
d,
|
|
544
|
-
[
|
|
545
|
-
rotationTrack("leftUpperArm", d, [
|
|
546
|
-
{ t: 0, e: [0, 0, 0.5] },
|
|
547
|
-
{ t: 1.5, e: [0, 0, 0.7] },
|
|
548
|
-
{ t: 3, e: [0, 0, 0.5] }
|
|
549
|
-
]),
|
|
550
|
-
rotationTrack("leftLowerArm", d, [
|
|
551
|
-
{ t: 0, e: [0, 0, 1.35] },
|
|
552
|
-
{ t: 1.5, e: [0, 0, 1.6] },
|
|
553
|
-
{ t: 3, e: [0, 0, 1.35] }
|
|
554
|
-
]),
|
|
555
|
-
rotationTrack("rightUpperArm", d, [
|
|
556
|
-
{ t: 0, e: [0, 0, -0.5] },
|
|
557
|
-
{ t: 1.5, e: [0, 0, -0.7] },
|
|
558
|
-
{ t: 3, e: [0, 0, -0.5] }
|
|
559
|
-
]),
|
|
560
|
-
rotationTrack("rightLowerArm", d, [
|
|
561
|
-
{ t: 0, e: [0, 0, -1.35] },
|
|
562
|
-
{ t: 1.5, e: [0, 0, -1.6] },
|
|
563
|
-
{ t: 3, e: [0, 0, -1.35] }
|
|
564
|
-
])
|
|
565
|
-
],
|
|
566
|
-
true,
|
|
567
|
-
["cross_arms", "performance", "loop", "upper-body"]
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// --- salute: one-shot hand-to-forehead ----------------------------------------------------------
|
|
572
|
-
function saluteClip(): AnimationClipDefinition<StandardClipId> {
|
|
573
|
-
const d = 1;
|
|
574
|
-
return clip(
|
|
575
|
-
"salute",
|
|
576
|
-
d,
|
|
577
|
-
[
|
|
578
|
-
rotationTrack("rightUpperArm", d, [
|
|
579
|
-
{ t: 0, e: [0, 0, -0.2] },
|
|
580
|
-
{ t: 0.35, e: [1.3, -0.5, -1.2] },
|
|
581
|
-
{ t: 0.7, e: [1.3, -0.5, -1.2] },
|
|
582
|
-
{ t: 1, e: [0.4, -0.2, -0.8] }
|
|
583
|
-
]),
|
|
584
|
-
rotationTrack("rightLowerArm", d, [
|
|
585
|
-
{ t: 0, e: [0, 0, -0.3] },
|
|
586
|
-
{ t: 0.35, e: [0, -0.4, -1.3] },
|
|
587
|
-
{ t: 0.7, e: [0, -0.4, -1.3] },
|
|
588
|
-
{ t: 1, e: [0, -0.2, -0.7] }
|
|
589
|
-
]),
|
|
590
|
-
rotationTrack("head", d, [
|
|
591
|
-
{ t: 0, e: [0, 0, 0] },
|
|
592
|
-
{ t: 0.35, e: [0.08, -0.1, 0] },
|
|
593
|
-
{ t: 1, e: [0, 0, 0] }
|
|
594
|
-
])
|
|
595
|
-
],
|
|
596
|
-
false,
|
|
597
|
-
["salute", "performance", "one-shot", "upper-body"]
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// --- shake_head: one-shot side-to-side "no" -----------------------------------------------------
|
|
602
|
-
function shakeHeadClip(): AnimationClipDefinition<StandardClipId> {
|
|
603
|
-
const d = 0.8;
|
|
604
|
-
return clip(
|
|
605
|
-
"shake_head",
|
|
606
|
-
d,
|
|
607
|
-
[
|
|
608
|
-
rotationTrack("head", d, [
|
|
609
|
-
{ t: 0, e: [0, 0, 0] },
|
|
610
|
-
{ t: 0.15, e: [0, -0.45, 0] },
|
|
611
|
-
{ t: 0.3, e: [0, 0.45, 0] },
|
|
612
|
-
{ t: 0.45, e: [0, -0.45, 0] },
|
|
613
|
-
{ t: 0.6, e: [0, 0.45, 0] },
|
|
614
|
-
{ t: 0.75, e: [0, 0, 0] }
|
|
615
|
-
]),
|
|
616
|
-
rotationTrack("neck", d, [
|
|
617
|
-
{ t: 0, e: [0, 0, 0] },
|
|
618
|
-
{ t: 0.15, e: [0, -0.25, 0] },
|
|
619
|
-
{ t: 0.3, e: [0, 0.25, 0] },
|
|
620
|
-
{ t: 0.45, e: [0, -0.25, 0] },
|
|
621
|
-
{ t: 0.6, e: [0, 0.25, 0] },
|
|
622
|
-
{ t: 0.75, e: [0, 0, 0] }
|
|
623
|
-
])
|
|
624
|
-
],
|
|
625
|
-
false,
|
|
626
|
-
["shake_head", "performance", "one-shot"]
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/** Ordered list of all standard library clip definitions (idle first). */
|
|
631
|
-
export function createStandardHumanoidClipDefinitions(): readonly AnimationClipDefinition<StandardClipId>[] {
|
|
632
|
-
return [
|
|
633
|
-
idleClip(),
|
|
634
|
-
talkClip(),
|
|
635
|
-
gestureClip(),
|
|
636
|
-
pointClip(),
|
|
637
|
-
nodClip(),
|
|
638
|
-
waveClip(),
|
|
639
|
-
walkClip(),
|
|
640
|
-
runClip(),
|
|
641
|
-
reactClip(),
|
|
642
|
-
sitClip(),
|
|
643
|
-
shrugClip(),
|
|
644
|
-
crossArmsClip(),
|
|
645
|
-
saluteClip(),
|
|
646
|
-
shakeHeadClip()
|
|
647
|
-
];
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Shared, rig-neutral {@link AnimationClipRegistry} populated with the 14 standard performance clips
|
|
652
|
-
* authored on {@link STANDARD_LIBRARY_RIG} (canonical {@link HUMANOID_BONES}). The clips are COMPACT
|
|
653
|
-
* procedural keyframe loops/one-shots — real, samplable {@link AnimationTrack} data, not mocap and
|
|
654
|
-
* not placeholders. They give the studio a guaranteed, retargetable performance vocabulary even when
|
|
655
|
-
* a source GLB ships only one (or zero) usable clips.
|
|
656
|
-
*/
|
|
657
|
-
export function createStandardHumanoidClipRegistry(): AnimationClipRegistry<StandardClipId> {
|
|
658
|
-
return createAnimationClipRegistry<StandardClipId>(createStandardHumanoidClipDefinitions());
|
|
659
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { AnimationClipThreeCompat, ThreeCompatLoopMode } from "./AnimationClip";
|
|
2
|
-
|
|
3
|
-
export class AnimationActionThreeCompat {
|
|
4
|
-
time = 0;
|
|
5
|
-
weight = 1;
|
|
6
|
-
paused = false;
|
|
7
|
-
playing = false;
|
|
8
|
-
loop: ThreeCompatLoopMode = "repeat";
|
|
9
|
-
|
|
10
|
-
constructor(public readonly clip: AnimationClipThreeCompat) {}
|
|
11
|
-
|
|
12
|
-
play(): this { this.playing = true; this.paused = false; return this; }
|
|
13
|
-
pause(): this { this.paused = true; return this; }
|
|
14
|
-
stop(): this { this.playing = false; this.time = 0; return this; }
|
|
15
|
-
|
|
16
|
-
scrub(time: number): this {
|
|
17
|
-
this.time = Math.max(0, Math.min(this.clip.duration, time));
|
|
18
|
-
return this;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
crossFadeTo(next: AnimationActionThreeCompat, alpha: number): void {
|
|
22
|
-
const clamped = Math.max(0, Math.min(1, alpha));
|
|
23
|
-
this.weight = 1 - clamped;
|
|
24
|
-
next.weight = clamped;
|
|
25
|
-
next.play();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export type ThreeCompatLoopMode = "once" | "repeat" | "pingpong";
|
|
2
|
-
|
|
3
|
-
export interface ThreeCompatKeyframeTrack {
|
|
4
|
-
readonly target: string;
|
|
5
|
-
readonly property: string;
|
|
6
|
-
readonly times: readonly number[];
|
|
7
|
-
readonly values: readonly number[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class AnimationClipThreeCompat {
|
|
11
|
-
constructor(
|
|
12
|
-
public readonly name: string,
|
|
13
|
-
public readonly duration: number,
|
|
14
|
-
public readonly tracks: readonly ThreeCompatKeyframeTrack[]
|
|
15
|
-
) {}
|
|
16
|
-
}
|