Я пытаюсь преобразовать этот файл CSV в файл glTF. Я устал от попыток... что бы я ни делал, результат выглядит плохо. Может ли кто-нибудь попытаться это сделать или подсказать, как получить действительный результат?
https://drive.google.com/file/d/1TTD50w ... drive_link
вот близкая попытка, но не совсем точная.
import bpy
import csv
import ast
from mathutils import Vector, Matrix
# === CONFIG ===
CSV_PATH = r"C:\Users\path\path\path\path\out_animation.csv"
OUTPUT_PATH =r"C:\Users\path\path\path\path\out_animation.glb"
FPS = 30
SCALE = 1.0
START_FRAME = 1
USE_AXIS_SWAP = False # set True if CSV is Y-up
# Smoothing
BASE_SMOOTH = 5
FINGER_SMOOTH = 9 # only fingers are smoothed more
# === SWAP _l _r ===
def swap_side(name: str) -> str:
if name.endswith("_l"):
return name[:-2] + "_r"
if name.endswith("_r"):
return name[:-2] + "_l"
return name
# === BASE PARENT MAP ===
PARENT_MAP_BASE = {
"pelvis": None,
"stomach": "pelvis",
"chest": "stomach",
"neck": "chest",
"head": "neck",
"hair": "head",
"clavicle_l": "chest",
"arm_l": "clavicle_l",
"forearm_l": "arm_l",
"forearm_twist_l": "forearm_l",
"hand_l": "forearm_l",
"weapon_l": "hand_l",
"clavicle_r": "chest",
"arm_r": "clavicle_r",
"forearm_r": "arm_r",
"forearm_twist_r": "forearm_r",
"hand_r": "forearm_r",
"weapon_r": "hand_r",
"thigh_l": "pelvis",
"calf_l": "thigh_l",
"foot_l": "calf_l",
"toe_l": "foot_l",
"thigh_r": "pelvis",
"calf_r": "thigh_r",
"foot_r": "calf_r",
"toe_r": "foot_r",
"back_l": "pelvis",
"back_r": "pelvis",
"scapular_l": "chest",
"scapular_r": "chest",
"chest_l": "chest",
"chest_r": "chest",
"zero_joint_hand_l": "hand_l",
"zero_joint_hand_r": "hand_r",
"zero_joint_pelvis_l": "pelvis",
"zero_joint_pelvis_r": "pelvis",
"biceps_twist_l": "arm_l",
"biceps_twist_r": "arm_r",
"thigh_twist_l": "thigh_l",
"thigh_twist_r": "thigh_r",
"f_big1_l": "hand_l",
"f_big2_l": "f_big1_l",
"f_big3_l": "f_big2_l",
"f_main1_l": "hand_l",
"f_main2_l": "f_main1_l",
"f_main3_l": "f_main2_l",
"f_pointer1_l": "hand_l",
"f_pointer2_l": "f_pointer1_l",
"f_pointer3_l": "f_pointer2_l",
"f_big1_r": "hand_r",
"f_big2_r": "f_big1_r",
"f_big3_r": "f_big2_r",
"f_main1_r": "hand_r",
"f_main2_r": "f_main1_r",
"f_main3_r": "f_main2_r",
"f_pointer1_r": "hand_r",
"f_pointer2_r": "f_pointer1_r",
"f_pointer3_r": "f_pointer2_r",
}
# Swap map
PARENT_MAP = {
swap_side(k): (swap_side(v) if v else None)
for k, v in PARENT_MAP_BASE.items()
}
# Hand tail should use fingers first (not weapon)
HAND_CHILD_PRIORITY_BASE = {
"hand_l": ["f_big1_l", "f_main1_l", "f_pointer1_l", "zero_joint_hand_l"],
"hand_r": ["f_big1_r", "f_main1_r", "f_pointer1_r", "zero_joint_hand_r"],
}
HAND_CHILD_PRIORITY = {
swap_side(k): [swap_side(x) for x in v]
for k, v in HAND_CHILD_PRIORITY_BASE.items()
}
# === LOAD CSV ===
with open(CSV_PATH, "r", newline="") as f:
reader = csv.reader(f)
bone_names_raw = [b.strip() for b in next(reader)]
bone_names = [swap_side(b) for b in bone_names_raw]
raw_frames = []
for row in reader:
frame = {}
for name_raw, cell in zip(bone_names_raw, row):
try:
x, y, z = ast.literal_eval(cell.strip())
vec = Vector((x, z, -y)) if USE_AXIS_SWAP else Vector((x, y, z))
frame[swap_side(name_raw)] = vec * SCALE
except:
pass
raw_frames.append(frame)
if not raw_frames:
raise RuntimeError("No frames found in CSV.")
finger_bones = [b for b in bone_names if b.startswith("f_")]
def get_window(bone):
return FINGER_SMOOTH if bone in finger_bones else BASE_SMOOTH
# === SMOOTH POSITIONS ===
frames = []
for i in range(len(raw_frames)):
smoothed = {}
for bone in bone_names:
window = get_window(bone)
half = window // 2
acc = Vector((0, 0, 0))
count = 0
for j in range(i - half, i + half + 1):
if 0 0:
smoothed[bone] = acc / count
frames.append(smoothed)
# === REMOVE OLD ARMATURE ===
if "CSV_Armature" in bpy.data.objects:
old = bpy.data.objects["CSV_Armature"]
bpy.data.objects.remove(old, do_unlink=True)
# === CREATE ARMATURE ===
arm_data = bpy.data.armatures.new("CSV_Armature")
arm_obj = bpy.data.objects.new("CSV_Armature", arm_data)
bpy.context.collection.objects.link(arm_obj)
bpy.context.view_layer.objects.active = arm_obj
arm_obj.select_set(True)
# === BUILD REST POSE FROM FRAME 0 ===
frame0 = frames[0]
children = {b: [] for b in bone_names}
for child, parent in PARENT_MAP.items():
if parent:
children.setdefault(parent, []).append(child)
bpy.ops.object.mode_set(mode="EDIT")
for name in bone_names:
if name not in frame0:
continue
eb = arm_data.edit_bones.new(name)
head = frame0[name]
eb.head = head
# Choose child for tail direction
child_list = HAND_CHILD_PRIORITY.get(name, children.get(name, []))
direction = None
for child_name in child_list:
if child_name in frame0:
direction = frame0[child_name] - head
break
if direction is None or direction.length == 0:
direction = Vector((0, 0.1, 0))
eb.tail = head + direction
for name, parent in PARENT_MAP.items():
child_bone = arm_data.edit_bones.get(name)
parent_bone = arm_data.edit_bones.get(parent) if parent else None
if child_bone and parent_bone:
child_bone.parent = parent_bone
bpy.ops.object.mode_set(mode="POSE")
# === CREATE ACTION ===
arm_obj.animation_data_create()
action = bpy.data.actions.new(name="CSV_Action")
arm_obj.animation_data.action = action
# === REST DATA ===
rest_heads = {b.name: b.head_local.copy() for b in arm_data.bones}
rest_tails = {b.name: b.tail_local.copy() for b in arm_data.bones}
rest_dirs = {}
rest_rot = {}
for b in arm_data.bones:
d = rest_tails[b.name] - rest_heads[b.name]
if d.length > 0:
d.normalize()
rest_dirs[b.name] = d
rest_rot[b.name] = b.matrix_local.to_3x3()
# === ANIMATION WITH ROTATION ===
scene = bpy.context.scene
scene.render.fps = FPS
scene.frame_start = START_FRAME
scene.frame_end = START_FRAME + len(frames) - 1
for f_idx, frame in enumerate(frames):
frame_num = START_FRAME + f_idx
scene.frame_set(frame_num)
bpy.context.view_layer.update()
for bone_name, pose_bone in arm_obj.pose.bones.items():
if bone_name not in frame:
continue
head = frame[bone_name]
# direction from first child
direction = None
for child in children.get(bone_name, []):
if child in frame:
direction = frame[child] - head
if direction.length > 0:
direction.normalize()
break
# LOCK rotation for finger bones
if bone_name in finger_bones:
rot_mat = rest_rot[bone_name]
else:
if direction:
rot_delta = rest_dirs[bone_name].rotation_difference(direction)
rot_mat = rot_delta.to_matrix() @ rest_rot[bone_name]
else:
rot_mat = rest_rot[bone_name]
pose_bone.matrix = Matrix.Translation(head) @ rot_mat.to_4x4()
pose_bone.keyframe_insert(data_path="location", frame=frame_num, group=bone_name)
pose_bone.keyframe_insert(data_path="rotation_quaternion", frame=frame_num, group=bone_name)
# === EXPORT GLB ===
bpy.ops.object.mode_set(mode="OBJECT")
for obj in bpy.context.selected_objects:
obj.select_set(False)
arm_obj.select_set(True)
bpy.context.view_layer.objects.active = arm_obj
op_props = bpy.ops.export_scene.gltf.get_rna_type().properties
kwargs = {
"filepath": OUTPUT_PATH,
"export_format": "GLB",
"use_selection": True,
"export_animations": True,
"export_force_sampling": True,
"export_skins": True,
"export_yup": True,
}
kwargs = {k: v for k, v in kwargs.items() if k in op_props}
bpy.ops.export_scene.gltf(**kwargs)
print(f"Exported: {OUTPUT_PATH}")
Подробнее здесь: https://stackoverflow.com/questions/798 ... ting-issue
Проблема с преобразованием данных CSV 3D-анимации в gLTF ⇐ Python
Программы на Python
-
Anonymous
1770934903
Anonymous
Я пытаюсь преобразовать этот файл CSV в файл glTF. Я устал от попыток... что бы я ни делал, результат выглядит плохо. Может ли кто-нибудь попытаться это сделать или подсказать, как получить действительный результат?
https://drive.google.com/file/d/1TTD50w6rSEiliqxgWdTRwZnGSNc5CIJf/view?usp=drive_link
вот близкая попытка, но не совсем точная.
import bpy
import csv
import ast
from mathutils import Vector, Matrix
# === CONFIG ===
CSV_PATH = r"C:\Users\path\path\path\path\out_animation.csv"
OUTPUT_PATH =r"C:\Users\path\path\path\path\out_animation.glb"
FPS = 30
SCALE = 1.0
START_FRAME = 1
USE_AXIS_SWAP = False # set True if CSV is Y-up
# Smoothing
BASE_SMOOTH = 5
FINGER_SMOOTH = 9 # only fingers are smoothed more
# === SWAP _l _r ===
def swap_side(name: str) -> str:
if name.endswith("_l"):
return name[:-2] + "_r"
if name.endswith("_r"):
return name[:-2] + "_l"
return name
# === BASE PARENT MAP ===
PARENT_MAP_BASE = {
"pelvis": None,
"stomach": "pelvis",
"chest": "stomach",
"neck": "chest",
"head": "neck",
"hair": "head",
"clavicle_l": "chest",
"arm_l": "clavicle_l",
"forearm_l": "arm_l",
"forearm_twist_l": "forearm_l",
"hand_l": "forearm_l",
"weapon_l": "hand_l",
"clavicle_r": "chest",
"arm_r": "clavicle_r",
"forearm_r": "arm_r",
"forearm_twist_r": "forearm_r",
"hand_r": "forearm_r",
"weapon_r": "hand_r",
"thigh_l": "pelvis",
"calf_l": "thigh_l",
"foot_l": "calf_l",
"toe_l": "foot_l",
"thigh_r": "pelvis",
"calf_r": "thigh_r",
"foot_r": "calf_r",
"toe_r": "foot_r",
"back_l": "pelvis",
"back_r": "pelvis",
"scapular_l": "chest",
"scapular_r": "chest",
"chest_l": "chest",
"chest_r": "chest",
"zero_joint_hand_l": "hand_l",
"zero_joint_hand_r": "hand_r",
"zero_joint_pelvis_l": "pelvis",
"zero_joint_pelvis_r": "pelvis",
"biceps_twist_l": "arm_l",
"biceps_twist_r": "arm_r",
"thigh_twist_l": "thigh_l",
"thigh_twist_r": "thigh_r",
"f_big1_l": "hand_l",
"f_big2_l": "f_big1_l",
"f_big3_l": "f_big2_l",
"f_main1_l": "hand_l",
"f_main2_l": "f_main1_l",
"f_main3_l": "f_main2_l",
"f_pointer1_l": "hand_l",
"f_pointer2_l": "f_pointer1_l",
"f_pointer3_l": "f_pointer2_l",
"f_big1_r": "hand_r",
"f_big2_r": "f_big1_r",
"f_big3_r": "f_big2_r",
"f_main1_r": "hand_r",
"f_main2_r": "f_main1_r",
"f_main3_r": "f_main2_r",
"f_pointer1_r": "hand_r",
"f_pointer2_r": "f_pointer1_r",
"f_pointer3_r": "f_pointer2_r",
}
# Swap map
PARENT_MAP = {
swap_side(k): (swap_side(v) if v else None)
for k, v in PARENT_MAP_BASE.items()
}
# Hand tail should use fingers first (not weapon)
HAND_CHILD_PRIORITY_BASE = {
"hand_l": ["f_big1_l", "f_main1_l", "f_pointer1_l", "zero_joint_hand_l"],
"hand_r": ["f_big1_r", "f_main1_r", "f_pointer1_r", "zero_joint_hand_r"],
}
HAND_CHILD_PRIORITY = {
swap_side(k): [swap_side(x) for x in v]
for k, v in HAND_CHILD_PRIORITY_BASE.items()
}
# === LOAD CSV ===
with open(CSV_PATH, "r", newline="") as f:
reader = csv.reader(f)
bone_names_raw = [b.strip() for b in next(reader)]
bone_names = [swap_side(b) for b in bone_names_raw]
raw_frames = []
for row in reader:
frame = {}
for name_raw, cell in zip(bone_names_raw, row):
try:
x, y, z = ast.literal_eval(cell.strip())
vec = Vector((x, z, -y)) if USE_AXIS_SWAP else Vector((x, y, z))
frame[swap_side(name_raw)] = vec * SCALE
except:
pass
raw_frames.append(frame)
if not raw_frames:
raise RuntimeError("No frames found in CSV.")
finger_bones = [b for b in bone_names if b.startswith("f_")]
def get_window(bone):
return FINGER_SMOOTH if bone in finger_bones else BASE_SMOOTH
# === SMOOTH POSITIONS ===
frames = []
for i in range(len(raw_frames)):
smoothed = {}
for bone in bone_names:
window = get_window(bone)
half = window // 2
acc = Vector((0, 0, 0))
count = 0
for j in range(i - half, i + half + 1):
if 0 0:
smoothed[bone] = acc / count
frames.append(smoothed)
# === REMOVE OLD ARMATURE ===
if "CSV_Armature" in bpy.data.objects:
old = bpy.data.objects["CSV_Armature"]
bpy.data.objects.remove(old, do_unlink=True)
# === CREATE ARMATURE ===
arm_data = bpy.data.armatures.new("CSV_Armature")
arm_obj = bpy.data.objects.new("CSV_Armature", arm_data)
bpy.context.collection.objects.link(arm_obj)
bpy.context.view_layer.objects.active = arm_obj
arm_obj.select_set(True)
# === BUILD REST POSE FROM FRAME 0 ===
frame0 = frames[0]
children = {b: [] for b in bone_names}
for child, parent in PARENT_MAP.items():
if parent:
children.setdefault(parent, []).append(child)
bpy.ops.object.mode_set(mode="EDIT")
for name in bone_names:
if name not in frame0:
continue
eb = arm_data.edit_bones.new(name)
head = frame0[name]
eb.head = head
# Choose child for tail direction
child_list = HAND_CHILD_PRIORITY.get(name, children.get(name, []))
direction = None
for child_name in child_list:
if child_name in frame0:
direction = frame0[child_name] - head
break
if direction is None or direction.length == 0:
direction = Vector((0, 0.1, 0))
eb.tail = head + direction
for name, parent in PARENT_MAP.items():
child_bone = arm_data.edit_bones.get(name)
parent_bone = arm_data.edit_bones.get(parent) if parent else None
if child_bone and parent_bone:
child_bone.parent = parent_bone
bpy.ops.object.mode_set(mode="POSE")
# === CREATE ACTION ===
arm_obj.animation_data_create()
action = bpy.data.actions.new(name="CSV_Action")
arm_obj.animation_data.action = action
# === REST DATA ===
rest_heads = {b.name: b.head_local.copy() for b in arm_data.bones}
rest_tails = {b.name: b.tail_local.copy() for b in arm_data.bones}
rest_dirs = {}
rest_rot = {}
for b in arm_data.bones:
d = rest_tails[b.name] - rest_heads[b.name]
if d.length > 0:
d.normalize()
rest_dirs[b.name] = d
rest_rot[b.name] = b.matrix_local.to_3x3()
# === ANIMATION WITH ROTATION ===
scene = bpy.context.scene
scene.render.fps = FPS
scene.frame_start = START_FRAME
scene.frame_end = START_FRAME + len(frames) - 1
for f_idx, frame in enumerate(frames):
frame_num = START_FRAME + f_idx
scene.frame_set(frame_num)
bpy.context.view_layer.update()
for bone_name, pose_bone in arm_obj.pose.bones.items():
if bone_name not in frame:
continue
head = frame[bone_name]
# direction from first child
direction = None
for child in children.get(bone_name, []):
if child in frame:
direction = frame[child] - head
if direction.length > 0:
direction.normalize()
break
# LOCK rotation for finger bones
if bone_name in finger_bones:
rot_mat = rest_rot[bone_name]
else:
if direction:
rot_delta = rest_dirs[bone_name].rotation_difference(direction)
rot_mat = rot_delta.to_matrix() @ rest_rot[bone_name]
else:
rot_mat = rest_rot[bone_name]
pose_bone.matrix = Matrix.Translation(head) @ rot_mat.to_4x4()
pose_bone.keyframe_insert(data_path="location", frame=frame_num, group=bone_name)
pose_bone.keyframe_insert(data_path="rotation_quaternion", frame=frame_num, group=bone_name)
# === EXPORT GLB ===
bpy.ops.object.mode_set(mode="OBJECT")
for obj in bpy.context.selected_objects:
obj.select_set(False)
arm_obj.select_set(True)
bpy.context.view_layer.objects.active = arm_obj
op_props = bpy.ops.export_scene.gltf.get_rna_type().properties
kwargs = {
"filepath": OUTPUT_PATH,
"export_format": "GLB",
"use_selection": True,
"export_animations": True,
"export_force_sampling": True,
"export_skins": True,
"export_yup": True,
}
kwargs = {k: v for k, v in kwargs.items() if k in op_props}
bpy.ops.export_scene.gltf(**kwargs)
print(f"Exported: {OUTPUT_PATH}")
Подробнее здесь: [url]https://stackoverflow.com/questions/79888443/csv-3d-animation-data-to-gltf-converting-issue[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия