import bpy import bmesh def active_object(obj): bpy.context.view_layer.objects.active = obj obj.select_set(True) def get_obj_max_foot(workdir,filename,filename_tex): # 1.模型导入和初始化 # 删除当前场景中的所有对象: # use_global=False表示只删除当前场景中的对象,而不会影响到其他场景中的对象;confirm=False表示删除时不需要确认。 bpy.ops.object.delete(use_global=False, confirm=False) bpy.ops.import_scene.obj(filepath=filename) # 导入指定路径的 OBJ 格式模型文件 bpy.context.scene.unit_settings.scale_length = 0.001 # 将场景的长度单位缩放为0.001,相当于将长度单位从默认的米缩小为毫米 bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' # 将场景的长度单位设置为厘米 bpy.context.scene.unit_settings.mass_unit = 'GRAMS' # 将场景的质量单位设置为克 obj = bpy.context.selected_objects[0] # 获取了当前选中的对象列表,然后通过 [0] 取得列表中的第一个对象 bpy.context.view_layer.objects.active = obj # 将变量 obj 设置为当前活动对象 obj.select_set(True) # 将变量 obj 的选择状态设置为 True,表示选中该对象 pid = obj.name # 获取该对象的名字 # 对选定的对象进行对齐操作 bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'}) # 设置选中对象的原点,参数1:将原点设置为对象的质心,参数2:使用对象的几何中心作为参考 bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') # 将选中对象的位置坐标分别设置为 (0, 0),即将对象移动到世界坐标系的原点位置 obj.location[0] = 0 obj.location[1] = 0 bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) # 将选中对象的位置、旋转和缩放应用到对象的数据中 # 2.选择要复制的对象 obj_duplicate = obj.copy() obj_duplicate.data = obj.data.copy() bpy.context.collection.objects.link(obj_duplicate) # obj_duplicate.location.x += 5.0 bpy.ops.object.select_all(action='DESELECT') # 取消选中全部对象 # 3.处理复制的对象的脚底缝合边 # 选中复制对象 bpy.context.view_layer.objects.active = obj_duplicate obj_duplicate.select_set(True) selected_obj = bpy.context.active_object # 获取当前选中的对象 bpy.context.view_layer.objects.active = selected_obj # 将对象转换到编辑模式 # 切换到3D视图编辑模式 bpy.context.area.type = 'VIEW_3D' bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') # 选择所有的边 # 切换到UV编辑器 bpy.context.area.type = 'IMAGE_EDITOR' pid_img = f"{pid}Tex1.jpg" bpy.ops.image.open(filepath=filename_tex, directory=workdir, files=[{"name": pid_img, "name": pid_img}], relative_path=True, show_multiview=False) bpy.context.area.ui_type = 'UV' bpy.ops.uv.select_all(action='SELECT') # 选择所有UV贴图顶点 # 标记所有沿孤岛的边为缝合边 bpy.ops.uv.seams_from_islands() bpy.context.area.type = 'VIEW_3D' bpy.ops.object.mode_set(mode='OBJECT') # 获取世界坐标系下z轴接近0的顶点的索引 z_zero_vertex_indices = [] for i, vertex in enumerate(selected_obj.data.vertices): world_vertex = selected_obj.matrix_world @ vertex.co if abs(world_vertex.z) < 0.2: z_zero_vertex_indices.append(i) # 将对象转换回对象模式 bpy.ops.object.mode_set(mode='OBJECT') # 创建一个新的顶点组,并将z_zero_vertices中的顶点添加到该顶点组中 vg = selected_obj.vertex_groups.new(name="z_zero_vertices") for index in z_zero_vertex_indices: vg.add([index], 1.0, 'REPLACE') # 将选中的顶点设置为活动顶点 bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.vertex_group_select() # 选中待处理的顶点 bpy.ops.mesh.mark_seam(clear=True) # 取消所选区域内的缝合边 bpy.ops.mesh.region_to_loop() # 选择选定面周围的边界边!!! bpy.ops.mesh.select_mode(type="EDGE") # 转换为线模式 bpy.ops.mesh.mark_seam(clear=False) # 标记所选的线为缝合边 # 选中脚底顶点组 bpy.ops.uv.select_all(action='DESELECT') bpy.ops.object.vertex_group_set_active(group='z_zero_vertices') # 设置活动顶点组 bpy.ops.object.vertex_group_select() # 选择分配给活动顶点组的所有顶点 bpy.ops.uv.unwrap() # 处理贴图脚底部分孤岛,其他孤岛保持不变 # (1)反选模型顶点,方便贴图固定不需要处理的区域 bpy.ops.mesh.select_all(action='INVERT') bpy.context.area.type = 'IMAGE_EDITOR' # 切换到贴图模式 bpy.context.area.ui_type = 'UV' bpy.ops.uv.pin(clear=False) bpy.ops.object.vertex_group_set_active(group='z_zero_vertices') # 设置活动顶点组 bpy.ops.object.vertex_group_select() # 选择分配给活动顶点组的所有顶点 # (2)脚底部位UV展开,平均孤岛比例,重新排列孤岛 bpy.ops.uv.select_all(action='SELECT') bpy.ops.uv.average_islands_scale() bpy.ops.uv.pack_islands(margin=0.001) bpy.context.area.type = 'VIEW_3D' bpy.ops.object.mode_set(mode='OBJECT') # 4. 烘焙模式,参数设置 bpy.ops.object.select_all(action='DESELECT') # 取消选中全部对象 # # 选中原始对象 bpy.context.view_layer.objects.active = obj obj.select_set(True) # 选中复制对象 bpy.context.view_layer.objects.active = obj_duplicate obj_duplicate.select_set(True) bpy.context.scene.render.engine = 'CYCLES' bpy.context.scene.cycles.device = 'GPU' bpy.context.scene.cycles.preview_samples = 1 bpy.context.scene.cycles.samples = 1 bpy.context.scene.cycles.bake_type = 'DIFFUSE' bpy.context.scene.render.bake.use_pass_direct = False bpy.context.scene.render.bake.use_pass_indirect = False bpy.context.scene.render.bake.use_selected_to_active = True bpy.context.scene.render.bake.cage_extrusion = 0.01 bpy.ops.object.bake(type='DIFFUSE') # 开始 Bake # 5. 导出模型和贴图 bpy.ops.object.select_all(action='DESELECT') # 取消选中全部对象 # 选中复制对象 bpy.context.view_layer.objects.active = obj_duplicate obj_duplicate.select_set(True) bpy.ops.wm.obj_export(filepath=filename, export_selected_objects=True) bpy.context.area.type = 'IMAGE_EDITOR' # 切换到 bpy.ops.image.save_as(filepath=filename_tex) bpy.context.area.type = 'TEXT_EDITOR' # 切换到文本编辑器 if __name__ == '__main__': workdir = 'E:\\117080\\print_model' # filename = f'{workdir}\\117080_12cm_x1.obj' filename = f'{workdir}\\117080.obj' filename_tex = f'{workdir}\\117080Tex1.jpg' save_obj = f"{workdir}\\bake\\117080Tex1.obj" save_tex = f"{workdir}\\bake\\117080Tex1.jpg" get_obj_max_foot()