如何从GLTF文件中提取动画并生成独立动画文件?
从GLTF/GLB中提取动画生成独立文件的方案
推荐工具库
有两个成熟的库可以帮你高效完成动画提取:
- glTF Transform:专门针对glTF格式的编辑工具,API简洁直观,支持Node.js和浏览器环境,适合批量处理或精细编辑glTF结构。
- Three.js:通用3D库,自带GLTFLoader可以解析glTF,提取动画后再构建新的glTF文件,适合已经在使用Three.js的项目。
具体实现示例(glTF Transform)
以你的现有代码为基础,用glTF Transform修改动画提取部分:
首先安装依赖:
npm install @gltf-transform/core @gltf-transform/functions
然后修改代码中的动画处理逻辑:
import { Document, NodeIO } from '@gltf-transform/core'; import { prune } from '@gltf-transform/functions'; // ... 你的现有代码 ... // 创建仅包含动画的glb文件 const io = new NodeIO(); const document = await io.read(`temp/${assetData.file?.filename}`); // 定位目标动画 const targetAnimation = document.getAnimation(animation.name); if (!targetAnimation) { return reject(new Error(`Animation ${animation.name} not found`)); } // 移除其他无关动画 document.listAnimations().forEach(anim => { if (anim !== targetAnimation) anim.dispose(); }); // 修剪掉动画未引用的冗余资源(网格、材质等) await document.transform(prune({ keepMeshes: false, keepMaterials: false })); // 导出为GLB文件 const glbBuffer = await io.writeBinary(document); fsExtra.writeFileSync(`${animation.name}.glb`, glbBuffer); // 更新文件元数据 const stats = fsExtra.statSync(`${animation.name}.glb`); assetData.file = { filename: `${animation.name}.glb`, size: stats.size, hash: assetData.file?.hash, }; // ... 后续的数据库操作 ...
关键说明
- 动画依赖节点层级结构,
prune工具会自动保留动画引用的节点(即使没有网格),确保导出的动画文件能正确绑定到目标模型(目标模型需要有匹配的节点名称/层级)。 - 如果需要提取所有动画并分别导出,只需循环处理每个动画即可。
备选方案(Three.js)
如果你的项目已经依赖Three.js,可以用以下方式实现:
import * as THREE from 'three'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js'; // ... 你的现有代码 ... const loader = new GLTFLoader(); const gltf = await loader.loadAsync(`temp/${assetData.file?.filename}`); // 过滤出目标动画 const targetClip = gltf.animations.find(clip => clip.name === animation.name); if (!targetClip) { return reject(new Error(`Animation ${animation.name} not found`)); } // 创建仅包含动画关联节点的场景 const scene = new THREE.Scene(); const cloneAnimatedNodes = (node: THREE.Object3D) => { const clone = node.clone(false); // 不复制网格、材质 scene.add(clone); node.children.forEach(child => cloneAnimatedNodes(child)); }; // 遍历原场景,复制被动画引用的节点 gltf.scene.traverse(node => { const isAnimated = targetClip.tracks.some(track => { const targetName = track.name.split('.')[0]; return node.name === targetName; }); if (isAnimated) cloneAnimatedNodes(node); }); // 导出为GLB const exporter = new GLTFExporter(); const glbBuffer = await exporter.parseAsync(scene, { animations: [targetClip], binary: true }); fsExtra.writeFileSync(`${animation.name}.glb`, glbBuffer); // ... 更新文件元数据 ...
内容的提问来源于stack exchange,提问作者jomaup
相关产品推荐
相关产品推荐

