目录
bpy导入fbx,第一帧加上t-pose,t-pose有了,但是后面的动画丢了
bpy读取fbx获取所有节点名
bpy.ops.import_scene.fbx(filepath=fbx_in,axis_forward='Z', axis_up='Y')
armature = bpy.data.objects.get('Armature') # 获取名为 "Armature" 的对象
if armature and armature.type == 'ARMATURE':
# 使用.pose.bones来访问骨骼数据
for bone in armature.pose.bones:
print(bone.name)
fbx转bvh
c++ visual studio项目
https://github.com/zyndor/fbx2bvh
FBX SDK | Autodesk Platform Services
bpy读取fbx,转bvh
bpy直接转bvh后,丢失了根节点的平移和旋转信息
代码是读取fbx每帧根节点的平移和旋转信息
import bpy
# 假设已经导入了FBX文件
# fbx_filepath = 'path/to/your/fbx/file.fbx'
# bpy.ops.import_scene.fbx(filepath=fbx_filepath)
# 获取根节点
# 这里假设根节点为场景中的第一个对象
root_object = bpy.context.scene.objects[0]
# 确保根对象是选中的并且是活动对象
bpy.context.view_layer.objects.active = root_object
root_object.select_set(True)
# 获取动画数据
animation_data = root_object.animation_data
if animation_data is not None and animation_data.action is not None:
fcurves = animation_data.action.fcurves
for fcurve in fcurves:
# fcurve.data_path 会告诉你这是位置动画还是旋转动画
for keyframe_point in fcurve.keyframe_points:
frame = keyframe_point.co[0] # 关键帧的帧数
value = keyframe_point.co[1] # 关键帧的值
# 根据 fcurve.data_path 和 index 处理位置或旋转值
# 导出BVH
bvh_filepath = 'path/to/export.bvh'
bpy.ops.export_anim.bvh(filepath=bvh_filepath, check_existing=True)
其他fbx转bvh方法
-
Autodesk MotionBuilder:
- Autodesk MotionBuilder是一个专业的3D字符动画软件,提供了强大的动画制作和编辑功能。它支持多种文件格式,包括FBX和BVH,因此可以用来转换文件格式。MotionBuilder更适用于专业用户,需要购买许可证。
-
Mixamo:
- Mixamo是Adobe提供的一个在线服务,它允许用户上传角色,自动绑定骨骼,并应用动作。Mixamo支持上传FBX格式的文件,并可以导出为多种格式,包括BVH。Mixamo适用于快速角色动画,但可能不支持所有FBX到BVH的高级转换需求。
-
iPi Motion Capture:
- iPi Motion Capture是一个基于视觉的动作捕捉软件,支持将录制的视频转换为动画。它可以导入FBX格式,并导出为BVH格式。这个工具适用于需要从真实动作生成动画的用户。
-
AccuRIG (by AccuRIG):
- AccuRIG是一个自动化的角色绑定和设置工具,可以将FBX文件导入,自动设置骨骼和权重,然后导出为多种动画格式,包括BVH。它提供了一个相对简单和自动化的流程来处理角色和动画。
-
在线转换工具:
- 网络上也有一些在线服务提供FBX到BVH的转换,例如“AnyConv”、“CloudConvert”等。这些工具通常用户界面友好,但可能不提供高级的编辑或调整功能。
-
自定义脚本和库:
- 对于具有编程经验的用户,可以使用如
fbx2bvh
这样的开源工具或库来进行转换。此外,使用Python的bpy
模块(在Blender中)或其他语言的相应库,用户可以编写自定义脚本来精细控制转换过程。
- 对于具有编程经验的用户,可以使用如
Mixamo
GitHub - Nor-s/mediapipe-to-mixamo: mediapipe landmark to mixamo skeleton
bpy导入fbx,第一帧加上t-pose,t-pose有了,但是后面的动画丢了
import os.path
import time
import bpy
import numpy as np
from img_read import ImgReader
name_mapping = {
"f_avg_Pelvis": "Pelvis",
"f_avg_L_Hip": "Left_hip",
"f_avg_R_Hip": "Right_hip",
"f_avg_Spine1": "Spine1",
"f_avg_L_Knee": "Left_knee",
"f_avg_R_Knee": "Right_knee",
"f_avg_Spine2": "Spine2",
"f_avg_L_Ankle": "Left_ankle",
"f_avg_R_Ankle": "Right_ankle",
"f_avg_Spine3": "Spine3",
"f_avg_L_Foot": "Left_foot",
"f_avg_R_Foot": "Right_foot",
"f_avg_Neck": "Neck",
"f_avg_L_Collar": "Left_collar",
"f_avg_R_Collar": "Right_collar",
"f_avg_Head": "Head",
"f_avg_L_Shoulder": "Left_shoulder",
"f_avg_R_Shoulder": "Right_shoulder",
"f_avg_L_Elbow": "Left_elbow",
"f_avg_R_Elbow": "Right_elbow",
"f_avg_L_Wrist": "Left_wrist",
"f_avg_R_Wrist": "Right_wrist",
"f_avg_L_Hand": "Left_palm",
"f_avg_R_Hand": "Right_palm",
# Add more mappings as needed
}
def reset_rotation_and_keyframe(obj, frame_number):
# 设置当前帧
bpy.context.scene.frame_set(frame_number)
# 重置对象的旋转
obj.rotation_euler = (0.0, 0.0, 0.0)
# 插入旋转的关键帧
obj.keyframe_insert(data_path="rotation_euler", frame=frame_number)
def reset_transforms_and_insert_keyframes(obj, frame_number):
# 设置当前帧
bpy.context.scene.frame_set(frame_number)
# 重置对象的位置和旋转
obj.location = (0.0, 0.0, 0.0)
obj.rotation_euler = (0.0, 0.0, 0.0)
# 插入位置和旋转的关键帧
obj.keyframe_insert(data_path="location", frame=frame_number)
obj.keyframe_insert(data_path="rotation_euler", frame=frame_number)
# 递归调用此函数以处理所有子对象
for child in obj.children:
reset_transforms_and_insert_keyframes(child, frame_number)
def fbx_to_bvh(fbx_dir,order="ZXY",modify_node=False):
imgReader = ImgReader(fbx_dir, suffix='fbx', if_dir=1)
total_frames = imgReader.total_frames
start = time.time()
print('start convert bvh...', total_frames)
save_bvh = True
y_xishu = 1
data_len=0
for i in range(total_frames):
fbx_in, img_i, img_path = imgReader.get_filename(i)
# if "_m" not in fbx_in:
# continue
data_len+=1
bvh_out = os.path.dirname(fbx_in)+'/'+os.path.basename(fbx_in).split("_T")[0]+".bvh"
print("data_len", data_len, i, fbx_in,bvh_out)
bpy.ops.import_scene.fbx(filepath=fbx_in,axis_forward='Z', axis_up='Y')
armature = bpy.data.objects.get('Armature') # 获取名为 "Armature" 的对象
if armature and armature.type == 'ARMATURE':
bpy.context.view_layer.objects.active = armature
bpy.ops.object.mode_set(mode='POSE')
# 使用.pose.bones来访问骨骼数据
for bone in armature.pose.bones:
print(bone.name)
bone.rotation_mode = 'XYZ'
bone.rotation_euler = (0.0, 0.0, 0.0)
bone.keyframe_insert(data_path="rotation_euler", frame=1)
bpy.context.view_layer.update()
bpy.ops.object.mode_set(mode='OBJECT')
if modify_node:
if armature and armature.type == 'ARMATURE':
bpy.context.view_layer.objects.active = armature
bpy.ops.object.mode_set(mode='EDIT')
# 修改特定骨骼的名字
for bone in armature.data.edit_bones:
if bone.name in name_mapping:
bone.name = name_mapping[bone.name]
bpy.ops.object.mode_set(mode='OBJECT')
bpy.context.view_layer.objects.active = armature
# armature.select_set(True)
#
#
# animation_data = armature.animation_data
#
# if animation_data is not None and animation_data.action is not None:
# fcurves = animation_data.action.fcurves
# for fcurve in fcurves:
# # fcurve.data_path 会告诉你这是位置动画还是旋转动画
# if "rotation" in fcurve.data_path: #or "location" in fcurve.data_path:
# for keyframe_point in fcurve.keyframe_points:
# if keyframe_point.co[0] == 1: # 第一帧
# # 设置第一帧的关键帧值为0
# keyframe_point.co[1] = 0.0
# bpy.context.view_layer.update()
# 遍历所有对象
# for obj in bpy.data.objects:
# # 只对网格和骨架对象进行操作
# if obj.type in ('MESH', 'ARMATURE'):
# reset_rotation_and_keyframe(obj, 1)
# reset_transforms_and_insert_keyframes(armature, 0)
character_object = bpy.context.active_object
character_object.rotation_euler[1] += np.pi / 2 # 绕Y轴旋转90度
# 确定动画范围
action = armature.animation_data.action if armature.animation_data else None
frame_start = action.frame_range[0] if action else 0
frame_end = action.frame_range[1] if action else 1
bpy.ops.export_anim.bvh(filepath=bvh_out, frame_start=int(frame_start), frame_end=int(frame_end), global_scale=100, rotate_mode=order, root_transform_only=True)
if action:
bpy.data.actions.remove(action)
# 清理场景
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
def fbx_len(fbx_path):
bpy.ops.import_scene.fbx(filepath=fbx_path, axis_forward='Z', axis_up='Y')
frame_start = 100
frame_end = -1
action = bpy.data.actions[-1]
frame_end = max(frame_end, action.frame_range[1])
frame_start = min(frame_start, action.frame_range[0])
print(frame_start,int(frame_end))
if __name__ == '__main__':
fbx_dir=r"E:\tmp"
# fbx_len(fbx_file)
fbx_to_bvh(fbx_dir,order='ZXY',modify_node=0)