// 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} */ 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 }, ], }