Skip to content

Latest commit

 

History

History
812 lines (626 loc) · 24.6 KB

File metadata and controls

812 lines (626 loc) · 24.6 KB
Error in user YAML: (<unknown>): could not find expected ':' while scanning a simple key at line 3 column 1
---
- oeasy Python 0744
- 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。
本教程同步发布在: 

     个人网站: `https://oeasy.org` 
     蓝桥云课: `https://www.lanqiao.cn/courses/3584` 
     GitHub: `https://github.com/overmind1980/oeasy-python-tutorial` 
     Gitee: `https://gitee.com/overmind1980/oeasypython` 
---

实验标题

回忆

  • 上次我们研究了
    • 骨架的基础
    • 并且完成了渲染!

图片描述

  • 能做个人形的骨架吗?
    • 我们下次再说!

建立基础模型

  • 朝向
    • 头朝z轴正向
    • 面朝y轴正向
    • 手臂沿x轴展开

图片描述

import bpy

# 清空当前场景(先删旧对象)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

#===== 创建Mesh对象 =====
# 创建head立方体 - 头朝Z轴正向
# 位置: (0, 0, 2.0) - 位于身体上方
# 缩放: (1, 1, 1)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.0, 0.0, 2.0))
obj_head = bpy.context.active_object
obj_head.name = 'head'
obj_head.scale = (1.0, 1.0, 1.0)

# 创建body立方体 - 面朝Y轴正向,左右在X轴
# 位置: (0, 0, 0.5)
# 缩放: (1.0, 1.6, 0.6) - X=左右宽度, Y=前后深度(朝向), Z=上下高度
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.0, 0.0, 0.7))
obj_body = bpy.context.active_object
obj_body.name = 'body'
obj_body.scale = (0.8, 0.6, 1.2)

# 创建arm_l立方体 - 左胳膊(X正方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(1.5, 0.0, 1))
obj_arm_l = bpy.context.active_object
obj_arm_l.name = 'arm_l'
obj_arm_l.scale = (2, 0.3, 0.3)

# 创建arm_r立方体 - 右胳膊(X负方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(-1.5, 0.0, 1))
obj_arm_r = bpy.context.active_object
obj_arm_r.name = 'arm_r'
obj_arm_r.scale = (2, 0.3, 0.3)

# 创建leg_l立方体 - 左腿(X正方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.3, 0.0, -1.2))
obj_leg_l = bpy.context.active_object
obj_leg_l.name = 'leg_l'
obj_leg_l.scale = (0.3, 0.3, 2)

# 创建leg_r立方体 - 右腿(X负方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(-0.3, 0.0, -1.2))
obj_leg_r = bpy.context.active_object
obj_leg_r.name = 'leg_r'
obj_leg_r.scale = (0.3, 0.3, 2)


# 选择所有要合并的对象
objects_to_merge = [obj_head, obj_body, obj_arm_l, obj_arm_r, obj_leg_l, obj_leg_r]
for obj in objects_to_merge:
    obj.select_set(True)

# 设置合并操作的活动对象
bpy.context.view_layer.objects.active = obj_body

# 执行合并操作
bpy.ops.object.join()

# 重命名合并后的对象
bpy.context.active_object.name = "man_model"
bpy.data.objects["man_model"].location = (0,0,2)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

环切四肢

图片描述

import bpy
import bmesh

def loopcut_vertical_edges(obj, cuts=4):
    """
    对指定对象进行垂直边环切操作
    :param obj: 需要进行环切的对象
    :param cuts: 切割次数
    """
    if not obj or obj.type != 'MESH':
        print(f"警告:{obj} 不是有效的网格对象")
        return False
    
    try:
        # 进入编辑模式
        bpy.context.view_layer.objects.active = obj
        obj.select_set(True)
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='SELECT')
        
        # 使用bmesh库获取网格数据
        bm = bmesh.from_edit_mesh(obj.data)
        
        # 找到垂直边(沿着Z轴方向的边)
        vertical_edges = []
        for edge in bm.edges:
            v1 = edge.verts[0].co
            v2 = edge.verts[1].co
            if abs(v1.x - v2.x) < 0.001 and abs(v1.y - v2.y) < 0.001 and abs(v1.z - v2.z) > 0.001:
                vertical_edges.append(edge)
        
        print(f"找到垂直边数量: {len(vertical_edges)}")
        
        if vertical_edges:
            # 对垂直边进行细分
            bmesh.ops.subdivide_edges(bm, edges=vertical_edges, cuts=cuts, use_grid_fill=True)
            print(f"  ✔️ 成功在 {obj.name} 上执行 {cuts} 次垂直边切割")
            
            # 更新网格
            bmesh.update_edit_mesh(obj.data)
        else:
            print(f"  ⚠️ 在 {obj.name} 上未找到垂直边,无法执行切割")
            return False
        
        # 切换回对象模式
        bpy.ops.object.mode_set(mode='OBJECT')
        return True
    
    except Exception as e:
        print(f"  ❌ 对 {obj.name} 执行切割操作失败: {e}")
        bpy.ops.object.mode_set(mode='OBJECT')
        return False

def loopcut_edges_by_axis(obj, axis='Z', cuts=4):
    """
    对指定对象进行特定轴方向的边环切操作
    :param obj: 需要进行环切的对象
    :param axis: 切割方向,可选 'X', 'Y', 'Z'。切割将在垂直于该轴的平面上进行
    :param cuts: 切割次数
    """
    if not obj or obj.type != 'MESH':
        print(f"警告:{obj} 不是有效的网格对象")
        return False
    
    try:
        # 进入编辑模式
        bpy.context.view_layer.objects.active = obj
        obj.select_set(True)
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='SELECT')
        
        # 使用bmesh库获取网格数据
        bm = bmesh.from_edit_mesh(obj.data)
        
        # 根据指定轴找到对应的边(需要进行环切的边)
        target_edges = []
        for edge in bm.edges:
            v1 = edge.verts[0].co
            v2 = edge.verts[1].co
            
            if axis == 'X':
                # 沿着X轴方向的边(用于创建垂直于X轴的切面)
                if (abs(v1.y - v2.y) < 0.001 and abs(v1.z - v2.z) < 0.001) and abs(v1.x - v2.x) > 0.001:
                    target_edges.append(edge)
            elif axis == 'Y':
                # 沿着Y轴方向的边(用于创建垂直于Y轴的切面)
                if (abs(v1.x - v2.x) < 0.001 and abs(v1.z - v2.z) < 0.001) and abs(v1.y - v2.y) > 0.001:
                    target_edges.append(edge)
            else:  # Z轴
                # 沿着Z轴方向的边(用于创建垂直于Z轴的切面)
                if (abs(v1.x - v2.x) < 0.001 and abs(v1.y - v2.y) < 0.001) and abs(v1.z - v2.z) > 0.001:
                    target_edges.append(edge)
        
        print(f"找到沿{axis}轴方向的边数量: {len(target_edges)}")
        
        if target_edges:
            # 对目标边进行细分
            bmesh.ops.subdivide_edges(bm, edges=target_edges, cuts=cuts, use_grid_fill=True)
            print(f"  ✔️ 成功在 {obj.name} 上执行 {cuts} 次垂直于{axis}轴方向的切割")
            
            # 更新网格
            bmesh.update_edit_mesh(obj.data)
        else:
            print(f"  ⚠️ 在 {obj.name} 上未找到沿{axis}轴方向的边,无法执行切割")
            return False
        
        # 切换回对象模式
        bpy.ops.object.mode_set(mode='OBJECT')
        return True
    
    except Exception as e:
        print(f"  ❌ 对 {obj.name} 执行切割操作失败: {e}")
        bpy.ops.object.mode_set(mode='OBJECT')
        return False

# 清空当前场景
print("正在清空场景...")
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

#===== 创建Mesh对象 =====
print("正在创建人物模型...")

# 创建head立方体 - 头朝Z轴正向,位于身体上方
# 位置: (0, 0, 3.0) - 位于身体上方
# 缩放: (1.0, 1.0, 1.0)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.0, 0.0, 5.0))
obj_head = bpy.context.active_object
obj_head.name = 'head'
obj_head.scale = (1.0, 1.0, 1.0)

# 创建body立方体 - 面朝Y轴正向,左右在X轴
# 位置: (0, 0, 1.7) - 身体位置调整,让脚站在Z=0
# 缩放: (0.8, 0.6, 1.2) - X=左右宽度, Y=前后深度(朝向), Z=上下高度
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.0, 0.0, 3.7))
obj_body = bpy.context.active_object
obj_body.name = 'body'
obj_body.scale = (0.8, 0.6, 1.2)

# 创建arm_l立方体 - 左胳膊(X正方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(1.5, 0.0, 4.0))
obj_arm_l = bpy.context.active_object
obj_arm_l.name = 'arm_l'
obj_arm_l.scale = (2, 0.3, 0.3)

# 创建arm_r立方体 - 右胳膊(X负方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(-1.5, 0.0, 4.0))
obj_arm_r = bpy.context.active_object
obj_arm_r.name = 'arm_r'
obj_arm_r.scale = (2, 0.3, 0.3)

# 创建leg_l立方体 - 左腿(X正方向)- 脚站在Z=0平面
# 缩放为(0.3, 0.3, 2),所以底部应在Z=0,中心位置在Z=1
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.3, 0.0, 2.3))
obj_leg_l = bpy.context.active_object
obj_leg_l.name = 'leg_l'
obj_leg_l.scale = (0.3, 0.3, 1.3)

# 创建leg_r立方体 - 右腿(X负方向)- 脚站在Z=0平面
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(-0.3, 0.0, 2.3))
obj_leg_r = bpy.context.active_object
obj_leg_r.name = 'leg_r'
obj_leg_r.scale = (0.3, 0.3, 1.3)

print("\n正在为胳膊和腿添加垂直边环切效果...")

# 对胳膊进行X轴方向的切割
loopcut_edges_by_axis(obj_arm_l, axis='X', cuts=5)
loopcut_edges_by_axis(obj_arm_r, axis='X', cuts=5)

# 对腿进行Z轴方向的切割
loopcut_edges_by_axis(obj_leg_l, axis='Z', cuts=5)
loopcut_edges_by_axis(obj_leg_r, axis='Z', cuts=5)

print("\n人物模型创建完成!")

# 显示所有对象的信息
print("\n对象信息:")
for obj in bpy.context.scene.objects:
    if obj.type == 'MESH':
        print(f"- {obj.name}: {len(obj.data.vertices)} 个顶点, {len(obj.data.edges)} 条边, {len(obj.data.polygons)} 个面")

print("\n正在合并所有立方体对象...")

# 选择所有要合并的对象
objects_to_merge = [obj_head, obj_body, obj_arm_l, obj_arm_r, obj_leg_l, obj_leg_r]
for obj in objects_to_merge:
    obj.select_set(True)

# 设置合并操作的活动对象
bpy.context.view_layer.objects.active = obj_body

# 执行合并操作
bpy.ops.object.join()

# 重命名合并后的对象
bpy.context.active_object.name = "man_model"
bpy.data.objects["man_model"].location = (0,0,2)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

手动添加骨骼

  • 骨架和骨骼
    1. 新建骨架
    2. 插入第一根骨骼
    3. 骨骼重命名

图片描述

设置骨骼显示

  • 骨骼显示
    1. 名称
    2. 最前

图片描述

继续延伸

  • 选中root的结尾部分
    • e挤出
    • 躯干部分
    • spine

图片描述

  • 继续挤出头部head

肩肘手

  • 选中spine结尾部分
    • 挤出锁骨

图片描述

  • 从锁骨结尾部分
    • 挤出手臂

图片描述

锁骨删除

  • 选中锁骨删除

图片描述

  • 删除之后 脊椎结尾 和 手臂起点
    • 有 黑色虚线
    • 保持了 父子关系

图片描述

手臂细化

  • 选中手臂
    • 右键 细分

图片描述

  • 分别命名为上臂、下臂

图片描述

左腿制作

  • 从root的结尾部分挤出骨骼

图片描述

  • 制作 大腿、小腿

命名左右

  • 选中四根骨骼
    • 右键
    • 自动命名左右

图片描述

  • 继续右键
    • symmetry
    • 复制手臂

图片描述

  • 切换到姿态模式进行测试

骨架创建代码

import bpy

for amature in bpy.data.armatures:
    bpy.data.armatures.remove(amature)

# 创建骨架
bpy.ops.object.armature_add(enter_editmode=True, location=(0, 0, 0))
armature = bpy.context.active_object
armature.name = "Armature"
bpy.context.object.show_in_front = True
edit_bones = armature.data.edit_bones

# 清空默认骨骼
while edit_bones:
    edit_bones.remove(edit_bones[0])

# 创建根骨骼
root = edit_bones.new("root")
root.head = (0, 0, 0)
root.tail = (0, 0, 1.2)

# 创建脊椎骨骼
spine = edit_bones.new("spine")
spine.head = (0, 0, 1.2)
spine.tail = (0, 0, 2.7)
spine.parent = root

# 创建头部骨骼
head = edit_bones.new("head")
head.head = (0, 0, 2.7)
head.tail = (0, 0, 3.5)
head.parent = spine

# 创建左上肢骨骼
upper_arm_l = edit_bones.new("upperArm.L")
upper_arm_l.head = (-0.2, 0, 2.3)
upper_arm_l.tail = (-1.6, 0, 2.3)
upper_arm_l.parent = spine

lower_arm_l = edit_bones.new("lowerArm.L")
lower_arm_l.head = (-1.6, 0, 2.3)
lower_arm_l.tail = (-2.6, 0, 2.3)
lower_arm_l.parent = upper_arm_l

# 创建右上肢骨骼
upper_arm_r = edit_bones.new("upperArm.R")
upper_arm_r.head = (0.2, 0, 2.3)
upper_arm_r.tail = (1.6, 0, 2.3)
upper_arm_r.parent = spine

lower_arm_r = edit_bones.new("lowerArm.R")
lower_arm_r.head = (1.6, 0, 2.3)
lower_arm_r.tail = (2.6, 0, 2.3)
lower_arm_r.parent = upper_arm_r

# 创建左下肢骨骼
upper_leg_l = edit_bones.new("upperLeg.L")
upper_leg_l.head = (0.3, 0, 1.2)
upper_leg_l.tail = (0.3, 0, 0.6)
upper_leg_l.parent = root

lower_leg_l = edit_bones.new("lowerLeg.L")
lower_leg_l.head = (0.3, 0, 0.6)
lower_leg_l.tail = (0.3, 0, 0)
lower_leg_l.parent = upper_leg_l

# 创建右下肢骨骼
upper_leg_r = edit_bones.new("upperLeg.R")
upper_leg_r.head = (-0.3, 0, 1.2)
upper_leg_r.tail = (-0.3, 0, 0.6)
upper_leg_r.parent = root

lower_leg_r = edit_bones.new("lowerLeg.R")
lower_leg_r.head = (-0.3, 0, 0.6)
lower_leg_r.tail = (-0.3, 0, 0)
lower_leg_r.parent = upper_leg_r

# 退出编辑模式
bpy.ops.object.mode_set(mode='OBJECT')

print("骨架创建完成!")

自动权重

  • 先选模型
    • 再选骨架
    • 对象 - 父级 - 自动权重

图片描述

  • 现在自动为每个顶点分配了权重
    • 具体有啥用吗?

调整姿势

  • 选中骨架
    • 设置 姿势模式
    • 选中骨骼 进行 旋转缩放

图片描述

  • 骨骼确实有用处
    • 如何回到初始状态

还原骨骼

  • 选中骨架
    • 选中姿势模式
    • a 全选骨骼
      • alt + r 复位旋转
      • alt + g 复位位移
      • alt + s 复位缩放

图片描述

  • 可以把绑定 也总结进入代码吗?

最终完整代码

import bpy
import bmesh

def loopcut_vertical_edges(obj, cuts=4):
    """
    对指定对象进行垂直边环切操作
    :param obj: 需要进行环切的对象
    :param cuts: 切割次数
    """
    if not obj or obj.type != 'MESH':
        print(f"警告:{obj} 不是有效的网格对象")
        return False
    
    try:
        # 进入编辑模式
        bpy.context.view_layer.objects.active = obj
        obj.select_set(True)
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='SELECT')
        
        # 使用bmesh库获取网格数据
        bm = bmesh.from_edit_mesh(obj.data)
        
        # 找到垂直边(沿着Z轴方向的边)
        vertical_edges = []
        for edge in bm.edges:
            v1 = edge.verts[0].co
            v2 = edge.verts[1].co
            if abs(v1.x - v2.x) < 0.001 and abs(v1.y - v2.y) < 0.001 and abs(v1.z - v2.z) > 0.001:
                vertical_edges.append(edge)
        
        print(f"找到垂直边数量: {len(vertical_edges)}")
        
        if vertical_edges:
            # 对垂直边进行细分
            bmesh.ops.subdivide_edges(bm, edges=vertical_edges, cuts=cuts, use_grid_fill=True)
            print(f"  ✔️ 成功在 {obj.name} 上执行 {cuts} 次垂直边切割")
            
            # 更新网格
            bmesh.update_edit_mesh(obj.data)
        else:
            print(f"  ⚠️ 在 {obj.name} 上未找到垂直边,无法执行切割")
            return False
        
        # 切换回对象模式
        bpy.ops.object.mode_set(mode='OBJECT')
        return True
    
    except Exception as e:
        print(f"  ❌ 对 {obj.name} 执行切割操作失败: {e}")
        bpy.ops.object.mode_set(mode='OBJECT')
        return False

def loopcut_edges_by_axis(obj, axis='Z', cuts=4):
    """
    对指定对象进行特定轴方向的边环切操作
    :param obj: 需要进行环切的对象
    :param axis: 切割方向,可选 'X', 'Y', 'Z'。切割将在垂直于该轴的平面上进行
    :param cuts: 切割次数
    """
    if not obj or obj.type != 'MESH':
        print(f"警告:{obj} 不是有效的网格对象")
        return False
    
    try:
        # 进入编辑模式
        bpy.context.view_layer.objects.active = obj
        obj.select_set(True)
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='SELECT')
        
        # 使用bmesh库获取网格数据
        bm = bmesh.from_edit_mesh(obj.data)
        
        # 根据指定轴找到对应的边(需要进行环切的边)
        target_edges = []
        for edge in bm.edges:
            v1 = edge.verts[0].co
            v2 = edge.verts[1].co
            
            if axis == 'X':
                # 沿着X轴方向的边(用于创建垂直于X轴的切面)
                if (abs(v1.y - v2.y) < 0.001 and abs(v1.z - v2.z) < 0.001) and abs(v1.x - v2.x) > 0.001:
                    target_edges.append(edge)
            elif axis == 'Y':
                # 沿着Y轴方向的边(用于创建垂直于Y轴的切面)
                if (abs(v1.x - v2.x) < 0.001 and abs(v1.z - v2.z) < 0.001) and abs(v1.y - v2.y) > 0.001:
                    target_edges.append(edge)
            else:  # Z轴
                # 沿着Z轴方向的边(用于创建垂直于Z轴的切面)
                if (abs(v1.x - v2.x) < 0.001 and abs(v1.y - v2.y) < 0.001) and abs(v1.z - v2.z) > 0.001:
                    target_edges.append(edge)
        
        print(f"找到沿{axis}轴方向的边数量: {len(target_edges)}")
        
        if target_edges:
            # 对目标边进行细分
            bmesh.ops.subdivide_edges(bm, edges=target_edges, cuts=cuts, use_grid_fill=True)
            print(f"  ✔️ 成功在 {obj.name} 上执行 {cuts} 次垂直于{axis}轴方向的切割")
            
            # 更新网格
            bmesh.update_edit_mesh(obj.data)
        else:
            print(f"  ⚠️ 在 {obj.name} 上未找到沿{axis}轴方向的边,无法执行切割")
            return False
        
        # 切换回对象模式
        bpy.ops.object.mode_set(mode='OBJECT')
        return True
    
    except Exception as e:
        print(f"  ❌ 对 {obj.name} 执行切割操作失败: {e}")
        bpy.ops.object.mode_set(mode='OBJECT')
        return False

# 清空当前场景
print("正在清空场景...")
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

#===== 创建Mesh对象 =====
print("正在创建人物模型...")

# 创建head立方体 - 头朝Z轴正向,位于身体上方
# 位置: (0, 0, 3.0) - 位于身体上方
# 缩放: (1.0, 1.0, 1.0)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.0, 0.0, 5.0))
obj_head = bpy.context.active_object
obj_head.name = 'head'
obj_head.scale = (1.0, 1.0, 1.0)

# 创建body立方体 - 面朝Y轴正向,左右在X轴
# 位置: (0, 0, 1.7) - 身体位置调整,让脚站在Z=0
# 缩放: (0.8, 0.6, 1.2) - X=左右宽度, Y=前后深度(朝向), Z=上下高度
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.0, 0.0, 3.7))
obj_body = bpy.context.active_object
obj_body.name = 'body'
obj_body.scale = (0.8, 0.6, 1.2)

# 创建arm_l立方体 - 左胳膊(X正方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(1.5, 0.0, 4.0))
obj_arm_l = bpy.context.active_object
obj_arm_l.name = 'arm_l'
obj_arm_l.scale = (2, 0.3, 0.3)

# 创建arm_r立方体 - 右胳膊(X负方向)
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(-1.5, 0.0, 4.0))
obj_arm_r = bpy.context.active_object
obj_arm_r.name = 'arm_r'
obj_arm_r.scale = (2, 0.3, 0.3)

# 创建leg_l立方体 - 左腿(X正方向)- 脚站在Z=0平面
# 缩放为(0.3, 0.3, 2),所以底部应在Z=0,中心位置在Z=1
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(0.3, 0.0, 2.3))
obj_leg_l = bpy.context.active_object
obj_leg_l.name = 'leg_l'
obj_leg_l.scale = (0.3, 0.3, 1.3)

# 创建leg_r立方体 - 右腿(X负方向)- 脚站在Z=0平面
bpy.ops.mesh.primitive_cube_add(size=1.0, enter_editmode=False, align='WORLD', location=(-0.3, 0.0, 2.3))
obj_leg_r = bpy.context.active_object
obj_leg_r.name = 'leg_r'
obj_leg_r.scale = (0.3, 0.3, 1.3)

print("\n正在为胳膊和腿添加垂直边环切效果...")

# 对胳膊进行X轴方向的切割
loopcut_edges_by_axis(obj_arm_l, axis='X', cuts=5)
loopcut_edges_by_axis(obj_arm_r, axis='X', cuts=5)

# 对腿进行Z轴方向的切割
loopcut_edges_by_axis(obj_leg_l, axis='Z', cuts=5)
loopcut_edges_by_axis(obj_leg_r, axis='Z', cuts=5)

print("\n人物模型创建完成!")

# 显示所有对象的信息
print("\n对象信息:")
for obj in bpy.context.scene.objects:
    if obj.type == 'MESH':
        print(f"- {obj.name}: {len(obj.data.vertices)} 个顶点, {len(obj.data.edges)} 条边, {len(obj.data.polygons)} 个面")

print("\n正在合并所有立方体对象...")

# 选择所有要合并的对象
objects_to_merge = [obj_head, obj_body, obj_arm_l, obj_arm_r, obj_leg_l, obj_leg_r]
for obj in objects_to_merge:
    obj.select_set(True)

# 设置合并操作的活动对象
bpy.context.view_layer.objects.active = obj_body

# 执行合并操作
bpy.ops.object.join()

# 重命名合并后的对象
bpy.context.active_object.name = "man_model"
bpy.data.objects["man_model"].location = (0,0,2)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

for amature in bpy.data.armatures:
    bpy.data.armatures.remove(amature)

# 创建骨架
bpy.ops.object.armature_add(enter_editmode=True, location=(0, 0, 0))
armature = bpy.context.active_object
armature.name = "Armature"
bpy.context.object.show_in_front = True
edit_bones = armature.data.edit_bones

# 清空默认骨骼
while edit_bones:
    edit_bones.remove(edit_bones[0])

# 创建根骨骼
root = edit_bones.new("root")
root.head = (0, 0, 0)
root.tail = (0, 0, 1.2)

# 创建脊椎骨骼
spine = edit_bones.new("spine")
spine.head = (0, 0, 1.2)
spine.tail = (0, 0, 2.7)
spine.parent = root

# 创建头部骨骼
head = edit_bones.new("head")
head.head = (0, 0, 2.7)
head.tail = (0, 0, 3.5)
head.parent = spine

# 创建左上肢骨骼
upper_arm_l = edit_bones.new("upperArm.L")
upper_arm_l.head = (-0.2, 0, 2.3)
upper_arm_l.tail = (-1.6, 0, 2.3)
upper_arm_l.parent = spine

lower_arm_l = edit_bones.new("lowerArm.L")
lower_arm_l.head = (-1.6, 0, 2.3)
lower_arm_l.tail = (-2.6, 0, 2.3)
lower_arm_l.parent = upper_arm_l

# 创建右上肢骨骼
upper_arm_r = edit_bones.new("upperArm.R")
upper_arm_r.head = (0.2, 0, 2.3)
upper_arm_r.tail = (1.6, 0, 2.3)
upper_arm_r.parent = spine

lower_arm_r = edit_bones.new("lowerArm.R")
lower_arm_r.head = (1.6, 0, 2.3)
lower_arm_r.tail = (2.6, 0, 2.3)
lower_arm_r.parent = upper_arm_r

# 创建左下肢骨骼
upper_leg_l = edit_bones.new("upperLeg.L")
upper_leg_l.head = (0.3, 0, 1.2)
upper_leg_l.tail = (0.3, 0, 0.6)
upper_leg_l.parent = root

lower_leg_l = edit_bones.new("lowerLeg.L")
lower_leg_l.head = (0.3, 0, 0.6)
lower_leg_l.tail = (0.3, 0, 0)
lower_leg_l.parent = upper_leg_l

# 创建右下肢骨骼
upper_leg_r = edit_bones.new("upperLeg.R")
upper_leg_r.head = (-0.3, 0, 1.2)
upper_leg_r.tail = (-0.3, 0, 0.6)
upper_leg_r.parent = root

lower_leg_r = edit_bones.new("lowerLeg.R")
lower_leg_r.head = (-0.3, 0, 0.6)
lower_leg_r.tail = (-0.3, 0, 0)
lower_leg_r.parent = upper_leg_r

# 退出编辑模式
bpy.ops.object.mode_set(mode='OBJECT')

print("骨架创建完成!")

bpy.context.view_layer.objects.active = armature
obj_body.select_set(True)
armature.select_set(True)
bpy.ops.object.parent_set(type="ARMATURE_AUTO")

总结

  • 现在这个代码已经全部完成了
    • 还有什么细节可以调整吗?
    • 我们下次再说!

  • 本文来自 oeasy Python 系统教程。
  • 想完整、扎实学 Python,
  • 搜索 oeasy 即可。