192 lines
9.5 KiB
Python
192 lines
9.5 KiB
Python
import bpy
|
||
import bmesh
|
||
|
||
# Имена mesh и арматуры
|
||
mesh_name = "arm"
|
||
armature_name = "Reference"
|
||
|
||
# Находим объект mesh по имени
|
||
mesh_obj = bpy.data.objects.get(mesh_name)
|
||
|
||
# Находим объект арматуры по имени
|
||
armature_obj = bpy.data.objects.get(armature_name)
|
||
|
||
# Устанавливаем текущий кадр на 0
|
||
bpy.context.scene.frame_set(0)
|
||
|
||
# Принудительно обновляем сцену, чтобы применить анимацию
|
||
bpy.context.view_layer.update()
|
||
|
||
# Открываем файл для записи
|
||
with open("C:\\Work\\Projects\\witcher001\\resources\\w\\zombie002.txt", "w") as file:
|
||
# Обработка арматуры и анимации
|
||
if armature_obj and armature_obj.type == 'ARMATURE':
|
||
file.write("=== Armature Matrix ===\n")
|
||
for row in armature_obj.matrix_world:
|
||
file.write(f"{row}\n")
|
||
file.write(f"=== Armature Bones: {len(armature_obj.data.bones)}\n")
|
||
for bone in armature_obj.data.bones:
|
||
# Записываем имя кости, длину и связи
|
||
file.write(f"Bone: {bone.name}\n")
|
||
file.write(f" HEAD_LOCAL: {bone.head_local}\n")
|
||
file.write(f" TAIL_LOCAL: {bone.tail_local}\n")
|
||
file.write(f" Length: {(bone.tail_local - bone.head_local).length}\n")
|
||
for row in bone.matrix:
|
||
file.write(f" {row}\n")
|
||
file.write(f" Parent: {bone.parent.name if bone.parent else 'None'}\n")
|
||
file.write(f" Children: {[child.name for child in bone.children]}\n")
|
||
|
||
# Обработка mesh
|
||
if mesh_obj and mesh_obj.type == 'MESH':
|
||
# Создаем копию mesh, чтобы не изменять оригинал
|
||
mesh_copy = mesh_obj.copy()
|
||
mesh_copy.data = mesh_obj.data.copy()
|
||
bpy.context.collection.objects.link(mesh_copy)
|
||
|
||
# Убедимся, что объект активен
|
||
bpy.context.view_layer.objects.active = mesh_copy
|
||
mesh_copy.select_set(True)
|
||
|
||
# Применяем модификатор Armature (если он есть)
|
||
for modifier in mesh_copy.modifiers:
|
||
if modifier.type == 'ARMATURE':
|
||
# Включаем модификатор, если он отключен
|
||
if not modifier.show_viewport:
|
||
modifier.show_viewport = True
|
||
if not modifier.show_render:
|
||
modifier.show_render = True
|
||
|
||
# Проверяем, что модификатор связан с арматурой
|
||
if modifier.object is None:
|
||
print(f"Модификатор Armature на объекте {mesh_copy.name} не связан с арматурой. Пропускаем.")
|
||
continue
|
||
|
||
# Временно применяем модификатор, чтобы получить правильные координаты вершин
|
||
try:
|
||
bpy.ops.object.modifier_apply(modifier=modifier.name)
|
||
except RuntimeError as e:
|
||
print(f"Ошибка при применении модификатора Armature: {e}")
|
||
continue
|
||
|
||
# Переходим в режим редактирования
|
||
bpy.ops.object.mode_set(mode='EDIT')
|
||
|
||
# Получаем BMesh представление mesh
|
||
bm = bmesh.from_edit_mesh(mesh_copy.data)
|
||
|
||
# Записываем список вершин
|
||
file.write(f"===Vertices: {len(bm.verts)}\n")
|
||
for vertex in bm.verts:
|
||
file.write(f"Vertex {vertex.index}: {vertex.co}\n")
|
||
|
||
# Убедимся, что у меша есть UV слой
|
||
uv_layer = bm.loops.layers.uv.active
|
||
if not uv_layer:
|
||
file.write("UV слой не найден.\n")
|
||
|
||
if uv_layer:
|
||
file.write(f"===UV Coordinates:\n")
|
||
file.write(f"Face count: {len(bm.faces)}\n")
|
||
|
||
for face in bm.faces:
|
||
file.write(f"Face {face.index}\n")
|
||
file.write(f"UV Count: {len(face.loops)}\n")
|
||
for loop in face.loops:
|
||
uv_coords = loop[uv_layer].uv
|
||
file.write(f" UV {uv_coords}\n")
|
||
|
||
# Записываем нормали
|
||
file.write(f"===Normals:\n")
|
||
for vertex in bm.verts:
|
||
file.write(f"Vertex {vertex.index}: Normal {vertex.normal}\n")
|
||
|
||
# Записываем треугольники (индексы вершин)
|
||
file.write(f"===Triangles: {len(bm.faces)}\n")
|
||
for face in bm.faces:
|
||
if len(face.verts) == 3: # Проверяем, что это треугольник
|
||
verts_indices = [vert.index for vert in face.verts]
|
||
file.write(f"Triangle: {verts_indices}\n")
|
||
|
||
# Возвращаемся в объектный режим
|
||
bpy.ops.object.mode_set(mode='OBJECT')
|
||
|
||
# Записываем веса вершин
|
||
file.write("=== Vertex Weights ===\n")
|
||
for vertex in mesh_copy.data.vertices:
|
||
file.write(f"Vertex {vertex.index}:\n")
|
||
file.write(f"Vertex groups: {len(vertex.groups)}\n")
|
||
for group in vertex.groups:
|
||
group_name = mesh_copy.vertex_groups[group.group].name
|
||
file.write(f" Group: '{group_name}', Weight: {group.weight}\n")
|
||
|
||
# Удаляем временную копию mesh
|
||
bpy.data.objects.remove(mesh_copy)
|
||
else:
|
||
file.write(f"Объект с именем '{mesh_name}' не найден или не является mesh.\n")
|
||
|
||
# Обработка арматуры и анимации
|
||
if armature_obj and armature_obj.type == 'ARMATURE':
|
||
|
||
# Получаем все ключевые кадры для арматуры
|
||
file.write("=== Animation Keyframes ===\n")
|
||
if armature_obj.animation_data and armature_obj.animation_data.action:
|
||
action = armature_obj.animation_data.action
|
||
|
||
# Собираем все уникальные ключевые кадры
|
||
keyframes = set()
|
||
|
||
# Логика для Blender 5.0 (Strip-based / ChannelBag structure)
|
||
if hasattr(action, "layers"):
|
||
for layer in action.layers:
|
||
if hasattr(layer, "strips"):
|
||
for strip in layer.strips:
|
||
# Проверяем наличие channelbags (согласно вашему dir(strip))
|
||
if hasattr(strip, "channelbags"):
|
||
for bag in strip.channelbags:
|
||
for fcurve in bag.fcurves:
|
||
for keyframe in fcurve.keyframe_points:
|
||
keyframes.add(int(keyframe.co[0]))
|
||
|
||
# На случай, если в этой версии используется единственное число
|
||
elif hasattr(strip, "channelbag") and strip.channelbag:
|
||
for fcurve in strip.channelbag.fcurves:
|
||
for keyframe in fcurve.keyframe_points:
|
||
keyframes.add(int(keyframe.co[0]))
|
||
|
||
# Фоллбек для Legacy экшенов
|
||
if not keyframes and hasattr(action, "fcurves"):
|
||
for fcurve in action.fcurves:
|
||
for keyframe in fcurve.keyframe_points:
|
||
keyframes.add(int(keyframe.co[0]))
|
||
|
||
keyframes = sorted(keyframes)
|
||
|
||
# Сортируем ключевые кадры
|
||
keyframes = sorted(keyframes)
|
||
|
||
# Сохраняем координаты и матрицы поворота для каждой кости на каждом ключевом кадре
|
||
file.write("=== Bone Transforms per Keyframe ===\n")
|
||
file.write(f"Keyframes: {len(keyframes)}\n")
|
||
for frame in keyframes:
|
||
# Устанавливаем текущий кадр
|
||
bpy.context.scene.frame_set(frame)
|
||
bpy.context.view_layer.update() # Обновляем сцену
|
||
|
||
file.write(f"Frame: {frame}\n")
|
||
for bone in armature_obj.pose.bones:
|
||
# Получаем координаты и матрицу поворота кости в мировом пространстве
|
||
matrix = bone.matrix
|
||
location = matrix.translation
|
||
rotation = matrix.to_euler()
|
||
|
||
# Записываем данные
|
||
file.write(f" Bone: {bone.name}\n")
|
||
file.write(f" Location: {location}\n")
|
||
file.write(f" Rotation: {rotation}\n")
|
||
file.write(f" Matrix:\n")
|
||
for row in matrix:
|
||
file.write(f" {row}\n")
|
||
else:
|
||
file.write(f"Объект с именем '{armature_name}' не найден или не является арматурой.\n")
|
||
|
||
print("Данные сохранены в файл 'mesh_armature_and_animation_data.txt'") |