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'")