fixtures.js
3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// fixtures.js —— 测试用假 rig / clip 库(不依赖 @drama/rig 已构建,纯 Node 可跑)。
// 形状严格对齐 @drama/rig §3 契约(RigDefinition / AnimationClip / TRACKS / TRACK_BONES / TRACK_DEFAULTS)。
export const TRACKS = ['base', 'lower_body', 'arm_l', 'arm_r', 'head']
export const TRACK_BONES = {
base: ['root', 'hip', 'torso'],
lower_body: ['thigh_l', 'shin_l', 'thigh_r', 'shin_r'],
arm_l: ['upper_arm_l', 'lower_arm_l'],
arm_r: ['upper_arm_r', 'lower_arm_r'],
head: ['neck', 'head'],
}
export const TRACK_DEFAULTS = {
base: 'base.idle',
lower_body: 'leg.stand_together',
arm_l: 'arm_l.down',
arm_r: 'arm_r.down',
head: 'head.idle',
}
/** @type {Record<string, import('@drama/rig').AnimationClip>} */
export const CLIPS = {
'base.idle': { id: 'base.idle', track: 'base', kind: 'loop', duration: 2000, bones: { torso: [{ t: 0, dy: 0 }, { t: 0.5, dy: -4 }, { t: 1, dy: 0 }] } },
'leg.stand_together': { id: 'leg.stand_together', track: 'lower_body', kind: 'pose', duration: 0, bones: { thigh_l: [{ t: 0, drot: 0 }], thigh_r: [{ t: 0, drot: 0 }] } },
'leg.walk': { id: 'leg.walk', track: 'lower_body', kind: 'loop', duration: 600, bones: { thigh_l: [{ t: 0, drot: -20 }, { t: 0.5, drot: 20 }, { t: 1, drot: -20 }], thigh_r: [{ t: 0, drot: 20 }, { t: 0.5, drot: -20 }, { t: 1, drot: 20 }] } },
'arm_l.down': { id: 'arm_l.down', track: 'arm_l', kind: 'pose', duration: 0, bones: { upper_arm_l: [{ t: 0, drot: 0 }] } },
'arm_r.down': { id: 'arm_r.down', track: 'arm_r', kind: 'pose', duration: 0, bones: { upper_arm_r: [{ t: 0, drot: 0 }] } },
'arm_r.point_right': { id: 'arm_r.point_right', track: 'arm_r', kind: 'pose', duration: 0, bones: { upper_arm_r: [{ t: 0, drot: 90 }] } },
'arm_r.punch': { id: 'arm_r.punch', track: 'arm_r', kind: 'oneshot', duration: 200, bones: { upper_arm_r: [{ t: 0, drot: 0 }, { t: 1, drot: 120 }] } },
'head.idle': { id: 'head.idle', track: 'head', kind: 'loop', duration: 3000, bones: { head: [{ t: 0, drot: 0 }] } },
'head.talk_shake': { id: 'head.talk_shake', track: 'head', kind: 'loop', duration: 300, bones: { head: [{ t: 0, drot: -3 }, { t: 0.5, drot: 3 }, { t: 1, drot: -3 }] } },
'head.nod': { id: 'head.nod', track: 'head', kind: 'oneshot', duration: 400, bones: { head: [{ t: 0, drot: 0 }, { t: 0.5, drot: 15 }, { t: 1, drot: 0 }] } },
}
/** @param {string} id @returns {import('@drama/rig').AnimationClip|null} */
export function getClip(id) {
return CLIPS[id] || null
}
/** @param {Object} extra 覆盖默认字段 @returns {import('./../src/anim-state.js').ClipLib} */
export function makeClipLib(extra = {}) {
return { getClip, TRACKS, TRACK_BONES, TRACK_DEFAULTS, ...extra }
}
/** 一个最小 humanoid 骨架:root → hip → torso → neck → head,及一条腿链。 */
export const RIG = {
id: 'humanoid_v1',
canvas: { width: 1024, height: 1536 },
bones: [
// 故意把子骨放在父骨之前,验证 core 能拓扑排序
{ name: 'head', parent: 'neck', pos: { x: 0, y: -40 }, rotation: 0 },
{ name: 'neck', parent: 'torso', pos: { x: 0, y: -60 }, rotation: 0 },
{ name: 'torso', parent: 'hip', pos: { x: 0, y: -80 }, rotation: 0 },
{ name: 'hip', parent: 'root', pos: { x: 0, y: -100 }, rotation: 0 },
{ name: 'root', parent: null, pos: { x: 0, y: 0 }, rotation: 0 },
{ name: 'thigh_r', parent: 'hip', pos: { x: 20, y: 0 }, rotation: 0 },
{ name: 'shin_r', parent: 'thigh_r', pos: { x: 0, y: 50 }, rotation: 0 },
],
slots: [
{ name: 'head', bone: 'head', zOrder: 40 },
{ name: 'torso', bone: 'torso', zOrder: 20 },
],
}