Files
opendance-web/web-server/test/fixtures/generate-sample-bvh.mjs
T

198 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 生成用于测试与验收的 BVH 样例(60 帧挥手 + 深蹲)。
* 用法:node test/fixtures/generate-sample-bvh.mjs [输出路径]
*/
import { writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const outPath = process.argv[2] ?? join(here, 'sample.bvh');
const HIERARCHY = `HIERARCHY
ROOT Hips
{
OFFSET 0.0 0.0 0.0
CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
JOINT Spine
{
OFFSET 0.0 12.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT Neck
{
OFFSET 0.0 14.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT Head
{
OFFSET 0.0 8.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
End Site
{
OFFSET 0.0 8.0 0.0
}
}
}
JOINT LeftShoulder
{
OFFSET 6.0 10.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT LeftArm
{
OFFSET 12.0 0.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT LeftForeArm
{
OFFSET 22.0 0.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT LeftHand
{
OFFSET 20.0 0.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
End Site
{
OFFSET 8.0 0.0 0.0
}
}
}
}
}
JOINT RightShoulder
{
OFFSET -6.0 10.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT RightArm
{
OFFSET -12.0 0.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT RightForeArm
{
OFFSET -22.0 0.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT RightHand
{
OFFSET -20.0 0.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
End Site
{
OFFSET -8.0 0.0 0.0
}
}
}
}
}
}
JOINT LeftUpLeg
{
OFFSET 5.0 -3.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT LeftLeg
{
OFFSET 0.0 -26.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT LeftFoot
{
OFFSET 0.0 -25.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT LeftToeBase
{
OFFSET 0.0 -4.0 10.0
CHANNELS 3 Zrotation Xrotation Yrotation
End Site
{
OFFSET 0.0 -2.0 6.0
}
}
}
}
}
JOINT RightUpLeg
{
OFFSET -5.0 -3.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT RightLeg
{
OFFSET 0.0 -26.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT RightFoot
{
OFFSET 0.0 -25.0 0.0
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT RightToeBase
{
OFFSET 0.0 -4.0 10.0
CHANNELS 3 Zrotation Xrotation Yrotation
End Site
{
OFFSET 0.0 -2.0 6.0
}
}
}
}
}
}
MOTION
`;
/** 通道顺序(与上面的 HIERARCHY 一致):Hips 6 通道 + 19 个关节各 3 通道 */
const JOINT_ORDER = [
'Hips',
'Spine',
'Neck',
'Head',
'LeftShoulder',
'LeftArm',
'LeftForeArm',
'LeftHand',
'RightShoulder',
'RightArm',
'RightForeArm',
'RightHand',
'LeftUpLeg',
'LeftLeg',
'LeftFoot',
'LeftToeBase',
'RightUpLeg',
'RightLeg',
'RightFoot',
'RightToeBase',
];
const CHANNELS = 6 + 3 * (JOINT_ORDER.length - 1);
const FRAMES = 60;
const FRAME_TIME = 1 / 30;
const lines = [];
for (let f = 0; f < FRAMES; f += 1) {
const t = f / FRAMES;
const phase = Math.sin(t * Math.PI * 2);
const squat = (1 - Math.cos(t * Math.PI * 2)) / 2;
const values = [];
for (const joint of JOINT_ORDER) {
if (joint === 'Hips') {
values.push(0, -squat * 12, 0); // Xposition Yposition Zposition
values.push(phase * 6, 0, 0); // Zrotation Xrotation Yrotation
continue;
}
const rot = { z: 0, x: 0, y: 0 };
if (joint === 'LeftArm') rot.z = 20 + phase * 55;
if (joint === 'RightArm') rot.z = -20 - phase * 55;
if (joint === 'LeftForeArm') rot.x = -25 - phase * 30;
if (joint === 'RightForeArm') rot.x = -25 + phase * 30;
if (joint === 'LeftUpLeg') rot.x = -squat * 35;
if (joint === 'RightUpLeg') rot.x = -squat * 35;
if (joint === 'LeftLeg') rot.x = squat * 60;
if (joint === 'RightLeg') rot.x = squat * 60;
if (joint === 'LeftFoot') rot.x = -squat * 25;
if (joint === 'RightFoot') rot.x = -squat * 25;
if (joint === 'Spine') rot.z = phase * 5;
if (joint === 'Neck') rot.z = -phase * 3;
values.push(rot.z, rot.x, rot.y);
}
if (values.length !== CHANNELS) throw new Error(`帧 ${f} 通道数错误:${values.length}`);
lines.push(values.map((v) => v.toFixed(4)).join(' '));
}
const text = `${HIERARCHY}Frames: ${FRAMES}\nFrame Time: ${FRAME_TIME.toFixed(7)}\n${lines.join('\n')}\n`;
writeFileSync(outPath, text, 'utf8');
console.log(`已生成 ${outPath}(${FRAMES} 帧,${CHANNELS} 通道,${text.length} 字节)`);