* feat(3d): FBX/GLTF/OBJ 加载器与骨骼动画支持 * chore: 更新 pnpm-lock.yaml * fix: 移除未使用的变量和方法 * fix: 修复 mesh-3d-editor tsconfig 引用路径 * fix: 修复正则表达式 ReDoS 漏洞
144 lines
4.4 KiB
JavaScript
144 lines
4.4 KiB
JavaScript
/**
|
|
* Test Animation at t=0
|
|
* 测试 t=0 时的动画值
|
|
*
|
|
* Compare animation values at t=0 with node.transform
|
|
*/
|
|
|
|
import { readFileSync } from 'fs';
|
|
|
|
const filePath = process.argv[2] || 'F:\\MyProject4\\assets\\octopus.fbx';
|
|
console.log(`Testing animation at t=0: ${filePath}\n`);
|
|
|
|
async function main() {
|
|
const { FBXLoader } = await import('../packages/asset-system/dist/index.js');
|
|
|
|
const binaryData = readFileSync(filePath);
|
|
const loader = new FBXLoader();
|
|
|
|
const context = {
|
|
metadata: {
|
|
path: filePath,
|
|
name: filePath.split(/[\\/]/).pop(),
|
|
type: 'model/fbx',
|
|
guid: '',
|
|
size: binaryData.length,
|
|
hash: '',
|
|
dependencies: [],
|
|
lastModified: Date.now(),
|
|
importerVersion: '1.0.0',
|
|
labels: [],
|
|
tags: [],
|
|
version: 1
|
|
},
|
|
loadDependency: async () => null
|
|
};
|
|
|
|
const content = {
|
|
type: 'binary',
|
|
binary: binaryData.buffer
|
|
};
|
|
|
|
const asset = await loader.parse(content, context);
|
|
|
|
if (!asset.animations || asset.animations.length === 0) {
|
|
console.log('No animation data!');
|
|
return;
|
|
}
|
|
|
|
const clip = asset.animations[0];
|
|
const nodes = asset.nodes;
|
|
const skeleton = asset.skeleton;
|
|
|
|
console.log(`Animation: "${clip.name}", duration: ${clip.duration}s`);
|
|
console.log(`Channels: ${clip.channels.length}, Samplers: ${clip.samplers.length}`);
|
|
|
|
// Sample animation at t=0
|
|
function sampleAtT0(sampler, componentCount) {
|
|
if (!sampler.output || sampler.output.length === 0) return null;
|
|
const result = [];
|
|
for (let i = 0; i < componentCount; i++) {
|
|
result.push(sampler.output[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Get animated transforms at t=0
|
|
const animTransforms = new Map();
|
|
for (const channel of clip.channels) {
|
|
const sampler = clip.samplers[channel.samplerIndex];
|
|
if (!sampler) continue;
|
|
|
|
const nodeIndex = channel.target.nodeIndex;
|
|
const path = channel.target.path;
|
|
|
|
let value;
|
|
if (path === 'rotation') {
|
|
value = sampleAtT0(sampler, 4);
|
|
} else {
|
|
value = sampleAtT0(sampler, 3);
|
|
}
|
|
if (!value) continue;
|
|
|
|
if (!animTransforms.has(nodeIndex)) {
|
|
animTransforms.set(nodeIndex, {});
|
|
}
|
|
const t = animTransforms.get(nodeIndex);
|
|
if (path === 'translation') t.position = value;
|
|
else if (path === 'rotation') t.rotation = value;
|
|
else if (path === 'scale') t.scale = value;
|
|
}
|
|
|
|
console.log(`\nAnimated node count at t=0: ${animTransforms.size}`);
|
|
|
|
// Compare with node.transform for first few skeleton joints
|
|
if (skeleton) {
|
|
console.log(`\n=== COMPARING ANIMATION vs NODE.TRANSFORM ===\n`);
|
|
|
|
let matchCount = 0;
|
|
let mismatchCount = 0;
|
|
const mismatches = [];
|
|
|
|
for (let i = 0; i < skeleton.joints.length; i++) {
|
|
const joint = skeleton.joints[i];
|
|
const node = nodes[joint.nodeIndex];
|
|
const animT = animTransforms.get(joint.nodeIndex);
|
|
|
|
if (!node || !animT) continue;
|
|
|
|
// Compare rotation (most important)
|
|
const nodeRot = node.transform.rotation;
|
|
const animRot = animT.rotation;
|
|
|
|
if (animRot) {
|
|
const rotMatch = nodeRot.every((v, idx) => Math.abs(v - animRot[idx]) < 0.001);
|
|
if (rotMatch) {
|
|
matchCount++;
|
|
} else {
|
|
mismatchCount++;
|
|
mismatches.push({ jointIndex: i, name: joint.name, nodeRot, animRot });
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`Rotation matches: ${matchCount}/${matchCount + mismatchCount}`);
|
|
|
|
if (mismatches.length > 0) {
|
|
console.log(`\n❌ MISMATCHES found!`);
|
|
console.log(`First 5 mismatches:`);
|
|
for (let i = 0; i < 5 && i < mismatches.length; i++) {
|
|
const m = mismatches[i];
|
|
console.log(`\n Joint[${m.jointIndex}] "${m.name}":`);
|
|
console.log(` node.rotation: [${m.nodeRot.map(v => v.toFixed(4)).join(', ')}]`);
|
|
console.log(` anim.rotation: [${m.animRot.map(v => v.toFixed(4)).join(', ')}]`);
|
|
}
|
|
} else {
|
|
console.log(`\n✅ All rotations match at t=0!`);
|
|
}
|
|
}
|
|
|
|
console.log('\nDone!');
|
|
}
|
|
|
|
main().catch(console.error);
|