【Blender】Blender脚本代码记录 - 闲橙砸的小站(可能更新的会更多)
注意事项和使用方法
自动加载仅适用于有 UI 面板的脚本,没有 UI 的脚本复制或在脚本编辑器中打开,点击执行按钮,即可生效。数据无价,请提前备份!!!
- 创建一个笔记本(记事本)
- 将代码复制进去
- 将名称修改为 XXXX.py
- 将改完名的脚本放在安装目录的startup 文件夹中,脚本会自动加载
blender-4.1.1-windows-x64\4.1\scripts\startup
一般情况下“工具分类”已经被我改成“Orange“ 👇
Python:
此处为语雀内容卡片,点击链接查看:Python笔记(未完待续) · 语雀
材质
一键覆盖所选对象材质参数
import bpy
class OrangeColorProperties(bpy.types.PropertyGroup):
# 定义属性组以存储插件的参数
base_color: bpy.props.FloatVectorProperty(
name="基础色",
subtype='COLOR',
size=4,
min=0.0, max=1.0,
default=(1.0, 0.5, 0.0, 1.0),
description="基础色"
)
roughness: bpy.props.FloatProperty(
name="粗糙度",
min=0.0, max=1.0,
default=0.5,
description="粗糙度"
)
metalness: bpy.props.FloatProperty(
name="金属度",
min=0.0, max=1.0,
default=0.0,
description="金属度"
)
emission_color: bpy.props.FloatVectorProperty(
name="自发光颜色",
subtype='COLOR',
size=4,
min=0.0, max=1.0,
default=(1.0, 0.5, 0.0, 1.0),
description="自发光颜色"
)
emission_strength: bpy.props.FloatProperty(
name="自发光强度",
min=0.0,
default=0.0,
description="自发光强度"
)
base_color_enable: bpy.props.BoolProperty(
name="启用基础色",
default=True,
description="是否修改基础色"
)
roughness_enable: bpy.props.BoolProperty(
name="启用粗糙度",
default=True,
description="是否修改粗糙度"
)
metalness_enable: bpy.props.BoolProperty(
name="启用金属度",
default=True,
description="是否修改金属度"
)
emission_color_enable: bpy.props.BoolProperty(
name="启用自发光颜色",
default=True,
description="是否修改自发光颜色"
)
emission_strength_enable: bpy.props.BoolProperty(
name="启用自发光强度",
default=True,
description="是否修改自发光强度"
)
class OBJECT_OT_orange_color(bpy.types.Operator):
bl_idname = "object.orange_color"
bl_label = "Do it"
def execute(self, context):
scene = context.scene
props = scene.orange_color_props
for obj in context.selected_objects:
if obj.type == 'MESH':
for slot in obj.material_slots:
mat = slot.material
if mat and mat.node_tree:
for node in mat.node_tree.nodes:
if node.type == 'BSDF_PRINCIPLED':
if props.base_color_enable:
node.inputs[0].default_value = props.base_color
if props.roughness_enable:
node.inputs[2].default_value = props.roughness
if props.metalness_enable:
node.inputs[1].default_value = props.metalness
if props.emission_color_enable:
node.inputs[26].default_value = props.emission_color
if props.emission_strength_enable:
node.inputs[27].default_value = props.emission_strength
return {'FINISHED'}
class VIEW3D_PT_orange_color_panel(bpy.types.Panel):
bl_label = "一键覆盖所选对象材质参数"
bl_idname = "VIEW3D_PT_orange_color_panel"
bl_space_type = 'VIEW_3D' # 面板所在的区域类型
bl_region_type = 'UI' # 面板所在的空间类型
bl_category = "Orange" # 面板所在的标签类别
def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.orange_color_props
row = layout.row()
row.prop(props, "base_color_enable")
row.active = props.base_color_enable
row.prop(props, "base_color", text="")
row = layout.row()
row.prop(props, "roughness_enable")
row.active = props.roughness_enable
row.prop(props, "roughness", text="")
row = layout.row()
row.prop(props, "metalness_enable")
row.active = props.metalness_enable
row.prop(props, "metalness", text="")
row = layout.row()
row.prop(props, "emission_color_enable")
row.active = props.emission_color_enable
row.prop(props, "emission_color", text="")
row = layout.row()
row.prop(props, "emission_strength_enable")
row.active = props.emission_strength_enable
row.prop(props, "emission_strength", text="")
layout.operator("object.orange_color")
def register():
bpy.utils.register_class(OrangeColorProperties)
bpy.utils.register_class(OBJECT_OT_orange_color)
bpy.utils.register_class(VIEW3D_PT_orange_color_panel)
bpy.types.Scene.orange_color_props = bpy.props.PointerProperty(type=OrangeColorProperties)
def unregister():
bpy.utils.unregister_class(OrangeColorProperties)
bpy.utils.unregister_class(OBJECT_OT_orange_color)
bpy.utils.unregister_class(VIEW3D_PT_orange_color_panel)
del bpy.types.Scene.orange_color_props
if __name__ == "__main__":
register()
注意事项:
- 本代码仅用4.1.1版本测试过
- 有UI面板,在脚本编辑器中运行后会在3D视口的右侧有名为工具的分类栏(和正常插件一样)
- 脚本中指定材质参数通过blender中右键复制的数据路径的数字代码实现,详情可参考文章
《Blender批量修改场景中材质属性》 - 闲橙砸的小站 Blender批量修改场景中材质属性 - 闲橙砸的小站
一键清理场景中网格上没有分配材质的空材质槽
import bpy
class OBJECT_PT_ClearEmptySlots(bpy.types.Panel):
"""面板用于清除物体上的空材质槽。"""
bl_label = "清除空材质槽"
bl_idname = "OBJECT_PT_clear_empty_slots"
bl_space_type = 'VIEW_3D' # 面板所在的区域类型
bl_region_type = 'UI' # 面板所在的空间类型
bl_category = "Orange" # 面板所在的标签类别
def draw(self, context):
layout = self.layout
row = layout.row()
row.alignment = 'CENTER' # 设置按钮文本居中对齐
row.operator("object.clear_empty_slots", text="清除空材质槽")
class OBJECT_OT_ClearEmptySlots(bpy.types.Operator):
"""操作用于清除物体上的空材质槽。"""
bl_idname = "object.clear_empty_slots"
bl_label = "清除空材质槽"
def execute(self, context):
clear_empty_material_slots()
return {'FINISHED'}
def clear_empty_material_slots():
"""函数用于移除物体上的空材质槽。"""
for obj in bpy.context.scene.objects:
if obj.type == 'MESH':
material_slots = obj.material_slots
if material_slots:
for i in range(len(material_slots) - 1, -1, -1):
if not material_slots[i].material:
bpy.context.view_layer.objects.active = obj
obj.active_material_index = i
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.material_slot_remove()
self.report({'INFO'}, f"已移除物体 {obj.name} 上的空材质槽 {i}")
return {'FINISHED'}
def register():
bpy.utils.register_class(OBJECT_PT_ClearEmptySlots)
bpy.utils.register_class(OBJECT_OT_ClearEmptySlots)
def unregister():
bpy.utils.unregister_class(OBJECT_PT_ClearEmptySlots)
bpy.utils.unregister_class(OBJECT_OT_ClearEmptySlots)
if __name__ == "__main__":
register()
在脚本编辑器中运行
清理场景中的所有模型上没有分配材质的材质槽
UI 有个按钮
查找指定材质并孤立显示包含此材质的模型(在视图层孤立)
import bpy
# 定义一个新类,继承自 `bpy.types.Panel`
class MATERIAL_PT_FindObjects(bpy.types.Panel):
"""创建一个Panel,用于查找使用指定材质的所有物体"""
bl_label = "查找使用材质的模型" # 面板标题
bl_idname = "MATERIAL_PT_find_objects" # 面板唯一标识符
bl_space_type = 'VIEW_3D' # 面板所在的区域类型
bl_region_type = 'UI' # 面板所在的空间类型
bl_category = "Orange" # 面板所在的标签类别
def draw(self, context):
layout = self.layout
scene = context.scene
# 显示简明操作介绍
#box = layout.box()
#box.label(text="操作介绍:")
#box.label(text="1. 输入材质名称")
#box.label(text="2. 点击'查找'按钮")
#box.label(text="3. 使用指定材质的模型将被突出显示")
# 材质名称输入框
row = layout.row()
row.prop(scene, "material_name", text="请输入材质名称")
# 查找按钮
row = layout.row()
op = row.operator("object.find_material_objects", text="查找")
op.show_warning = True # 添加一个属性,用于控制是否显示警告
# 操作符定义,用于执行查找功能
class OBJECT_OT_FindMaterialObjects(bpy.types.Operator):
"""查找使用指定材质的所有物体"""
bl_idname = "object.find_material_objects"
bl_label = "查找"
show_warning: bpy.props.BoolProperty(default=False) # 新增属性,用于控制是否显示警告
def invoke(self, context, event):
if self.show_warning:
return context.window_manager.invoke_props_dialog(self)
return self.execute(context)
def draw(self, context):
self.layout.label(text="警告:本脚本会影响对象的显示状态,以查找材质优先。")
def execute(self, context):
material_name = context.scene.material_name
if not material_name:
self.report({'ERROR'}, "请输入材质名称")
return {'CANCELLED'}
# 获取当前活动的视图层
view_layer = context.view_layer
# 记录原始的视图层对象可见性状态
original_visibility = {}
# 遍历所有集合
for collection in bpy.data.collections:
# 如果集合在当前视图层中并且没有被排除
if collection.name in view_layer.layer_collection.children and not view_layer.layer_collection.children[collection.name].exclude:
for obj in collection.objects:
if obj.type == 'MESH': # 只考虑网格对象
original_visibility[obj.name] = obj.hide_get(view_layer=view_layer)
# 隐藏所有非目标材质的对象
for obj in context.scene.objects:
if obj.type == 'MESH' and obj.visible_get(view_layer=view_layer): # 只考虑在当前视图层中可见的对象
target_material_found = False
for slot in obj.material_slots:
if slot.material and slot.material.name == material_name:
target_material_found = True
break
# 如果对象不包含目标材质,且原本是可见的,则隐藏它
if not target_material_found and original_visibility.get(obj.name, False) is False:
obj.hide_set(True)
# 显示包含目标材质的对象
found_objects = []
for obj in context.scene.objects:
if obj.type == 'MESH' and obj.visible_get(view_layer=view_layer): # 只考虑在当前视图层中可见的对象
for slot in obj.material_slots:
if slot.material and slot.material.name == material_name:
# 如果对象原本是隐藏的,则显示它
if original_visibility.get(obj.name, True) is True:
obj.hide_set(False)
found_objects.append(obj)
break
# 输出提示信息
if found_objects:
self.report({'INFO'}, f"已找到使用材质 '{material_name}' 的对象")
else:
self.report({'WARNING'}, "当前未排除集合中未找到使用材质 '{material_name}' 的对象")
return {'FINISHED'}
# 注册和注销函数
def register():
bpy.utils.register_class(MATERIAL_PT_FindObjects)
bpy.utils.register_class(OBJECT_OT_FindMaterialObjects)
bpy.types.Scene.material_name = bpy.props.StringProperty(name="", default="请输入材质名称")
def unregister():
bpy.utils.unregister_class(MATERIAL_PT_FindObjects)
bpy.utils.unregister_class(OBJECT_OT_FindMaterialObjects)
del bpy.types.Scene.material_name
# 只有当脚本直接运行时才注册
if __name__ == "__main__":
register()
使用说明:
- 将上述脚本复制到Blender的文本编辑器中。
- 运行脚本(Ctrl + Enter)。
- 在3D视口的右侧工具栏中找到新创建的“查找使用材质的模型”面板。
- 输入要查找的材质名称,然后点击“查找”按钮。
- 所有使用该材质的对象将会被显示,其他对象则会被隐藏。
请注意,这个脚本会根据您的当前视图层来操作,因此请确保您处于正确的视图层。此外,脚本中的材质查找是基于材质槽(material_slots
)的,这意味着如果一个对象有多个材质槽,它可能会被多次检查。
- 当有多个集合被排除,脚本不会对被排除的集合进行查找
模型
将所选的一个或多个曲线的物体属性→几何数据→倒角→圆(四舍五入)的深度值(bevel_depth)改为1000mm
import bpy
def set_bevel_depth(curve_objects, depth):
"""
修改选定曲线对象的倒角深度。
:param curve_objects: 曲线对象列表
:param depth: 倒角深度值 (单位: 米)
"""
for obj in curve_objects:
if obj.type == 'CURVE':
obj.data.bevel_depth = depth
print(f"Set bevel depth of {obj.name} to {depth}m")
# 主函数
def main():
selected_objects = bpy.context.selected_objects
curve_objects = [obj for obj in selected_objects if obj.type == 'CURVE']
# 定义倒角深度(按需改数值)
bevel_depth_value = 1.0 # 1000mm = 1m
set_bevel_depth(curve_objects, bevel_depth_value)
# 运行主函数
if __name__ == "__main__":
main()
这个脚本将会遍历所有选定的曲线对象,并将它们的倒角深度设置为1米。如果您需要修改倒角深度值,只需更改 bevel_depth_value
变量即可。
没有UI面板
名称及结构
导出所选模型名称列表为.csv文件
import bpy
import csv
import os
# 获取用户的桌面路径
desktop_path = os.path.expanduser("~/Desktop")
# 定义一个新的UI面板
class OBJECT_PT_CSVExporterPanel(bpy.types.Panel):
bl_label = "导出选定对象"
bl_idname = "OBJECT_PT_csv_exporter"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = '工具'
def draw(self, context):
layout = self.layout
col = layout.column(align=True) # 创建一个居中的列
box = col.box()
row = box.row()
row.alignment = 'CENTER' # 设置行的对齐方式为居中
row.label(text="将选定对象的名字导出到桌面上的CSV文件。")
row = box.row()
row.alignment = 'CENTER'
row.label(text="CSV文件名会与当前Blender项目名相同。")
row = box.row()
row.alignment = 'CENTER'
row.operator("object.export_selected", text="导出到CSV表格")
# 定义一个操作符来执行导出操作
class OBJECT_OT_CSVExporterOperator(bpy.types.Operator):
bl_idname = "object.export_selected"
bl_label = "将选定对象导出到CSV文件"
def execute(self, context):
selected_objects = [obj for obj in bpy.context.selected_objects]
# 获取当前项目的文件名
blend_file_path = bpy.data.filepath
if not blend_file_path:
# 如果项目未保存,则使用默认名称
file_name = "未命名"
else:
# 提取文件名(不包括路径和扩展名)
file_name = os.path.splitext(os.path.basename(blend_file_path))[0]
# 创建CSV文件
csv_file_path = os.path.join(desktop_path, f"{file_name}.csv")
with open(csv_file_path, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['对象名称'])
for obj in selected_objects:
writer.writerow([obj.name])
self.report({'INFO'}, f"对象已导出到 {csv_file_path}")
return {'FINISHED'}
# 注册和注销函数
def register():
bpy.utils.register_class(OBJECT_PT_CSVExporterPanel)
bpy.utils.register_class(OBJECT_OT_CSVExporterOperator)
def unregister():
bpy.utils.unregister_class(OBJECT_PT_CSVExporterPanel)
bpy.utils.unregister_class(OBJECT_OT_CSVExporterOperator)
if __name__ == "__main__":
register()
使用说明:
- 运行此脚本后,您会在3D视口右侧的工具区域看到一个名为“导出选定对象”的面板。
- 在该面板中有一个简短的说明,告诉用户如何使用此功能。
- 面板上有一个“导出到CSV”按钮。
- 当您选择了想要导出名称的对象后,点击“导出到CSV”按钮,它将在您的桌面上创建一个名为当前项目名称的CSV文件,并将所有选定对象的名称写入其中。
- 如果当前项目尚未保存,文件将被命名为
未命名.csv
。
请确保在运行此脚本之前已经保存了Blender项目,以便能够正确获取项目名称。如果没有保存项目,脚本将使用默认名称未命名.csv
。
一键合并所选对象子集中的网格物体并替换掉所选对象并重名为所选对象的名称
import bpy
class CleanUpOperator(bpy.types.Operator):
"""清理所选对象的子集"""
bl_idname = "object.cleanup_operator"
bl_label = "干他!"
def execute(self, context):
# 获取活动对象
active_object = context.view_layer.objects.active
if not active_object:
self.report({'ERROR'}, "没有活动的对象")
return {'CANCELLED'}
# 检查是否只有一个对象被选中
selected_objects = [obj for obj in context.selected_objects if obj != active_object]
if len(selected_objects) > 0:
self.report({'ERROR'}, "请只选择一个对象进行清理")
return {'CANCELLED'}
# 存储父级关系
parent = active_object.parent
# 收集所有网格子对象
mesh_children = [child for child in active_object.children_recursive if child.type == 'MESH']
# 清除父级关系,同时应用变换
bpy.ops.object.select_all(action='DESELECT')
for obj in mesh_children:
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
# 合并所有网格对象
bpy.ops.object.join()
merged_object = context.view_layer.objects.active
merged_object.name = active_object.name
# 删除非网格对象
non_mesh_children = [child for child in active_object.children_recursive if child.type != 'MESH']
for obj in non_mesh_children:
bpy.data.objects.remove(obj, do_unlink=True)
# 删除原选择对象
bpy.data.objects.remove(active_object, do_unlink=True)
# 将合并后的对象设置为新子对象
if parent:
merged_object.parent = parent
return {'FINISHED'}
class OneClickConsolidatePanel(bpy.types.Panel):
"""在3D视图中创建一个'一键整合'面板"""
bl_label = "一键整合"
bl_idname = "OBJECT_PT_onclick_consolidate"
bl_space_type = 'VIEW_3D' # 面板所在的区域类型
bl_region_type = 'UI' # 面板所在的空间类型
bl_category = "Orange" # 面板所在的标签类别
def draw(self, context):
layout = self.layout
layout.operator("object.cleanup_operator")
def register():
bpy.utils.register_class(CleanUpOperator)
bpy.utils.register_class(OneClickConsolidatePanel)
def unregister():
bpy.utils.unregister_class(CleanUpOperator)
bpy.utils.unregister_class(OneClickConsolidatePanel)
if __name__ == "__main__":
register()
注意事项:
- 有UI面板,在脚本编辑器中运行后会在3D视口的右侧有名为工具的分类栏(和正常插件一样)
- 未优化的脚本,得到的网格会有“.001”后缀
- 需要事先确认父级的变换状态(重置变换)(比如Z轴转了180度,执行脚本之后得到的模型会在Z轴转180度)
- 将所选对象包含的子集中的所有网格物体合并并替换到所选对象的位置并重命名
- 对名称中如ABC.01这种结尾用数字编号命名的对象可能会把尾缀的.01这种非blender自动添加的数字替换为.001,需要注意
- 当场景中对象过多或者面数过高时候可能速度会变得不如手动快
一键清理所选对象的尾缀(.001)
可以在代码中按需修改
import bpy
def remove_suffix(obj, suffix):
"""从对象名称中移除指定的后缀"""
if obj.name.endswith(suffix):
obj.name = obj.name[:-len(suffix)]
def main():
"""主函数,用于处理所有选中的对象"""
suffix = ".001"
for obj in bpy.context.selected_objects:
remove_suffix(obj, suffix)
if __name__ == "__main__":
main()
注意事项:
- 确保你已经选择了想要修改的对象。
- 修改代码第10行引号中的内容,运行之后会删除对应后缀。
- 如果对象名称没有以 ".001" 结尾,则不会进行任何更改。
- 请确保备份你的场景数据,以防意外发生。
现在,当你运行脚本时,它会直接对所有选中的对象进行处理,移除名称中的 ".001" 后缀。
按前中后结构给对象重命名
import bpy
# 定义一个操作符,用于重命名所选对象
class OBJECT_OT_rename(bpy.types.Operator):
"""重命名所选对象的操作符"""
bl_idname = "object.rename" # 操作符的唯一标识符
bl_label = "重命名对象" # 显示在界面上的名称
def execute(self, context):
# 获取前部、中部和后部
prefix = context.scene.my_tool.prefix
infix = context.scene.my_tool.infix
suffix = context.scene.my_tool.suffix
# 遍历所有选中的对象并重命名
for obj in context.selected_objects:
obj.name = f"{prefix}{infix}{suffix}" # 使用前部、中部和后部重命名对象
return {'FINISHED'} # 返回执行状态
# 定义一个面板,放置在3D视图的右侧
class OBJECT_PT_rename_panel(bpy.types.Panel):
"""创建一个面板,用于重命名对象"""
bl_label = "重命名对象" # 面板标题
bl_idname = "OBJECT_PT_rename" # 面板的唯一标识符
bl_space_type = 'VIEW_3D' # 面板所在的区域类型
bl_region_type = 'UI' # 面板所在的空间类型
bl_category = "Orange" # 面板所在的标签类别
def draw(self, context):
# 创建布局
layout = self.layout
# 获取自定义属性
my_tool = context.scene.my_tool
# 添加输入框用于输入前部、中部和后部
layout.prop(my_tool, "prefix") # 前部
layout.prop(my_tool, "infix") # 中部
layout.prop(my_tool, "suffix") # 后部
# 添加按钮用于执行重命名操作
layout.operator("object.rename", text="给他改名") # 重命名按钮
# 定义一个自定义属性,用于存储面板上的数据
class MyTool(bpy.types.PropertyGroup):
prefix: bpy.props.StringProperty(name="前部", default="") # 前字段
infix: bpy.props.StringProperty(name="中部", default="") # 中字段
suffix: bpy.props.StringProperty(name="后部", default="") # 后字段
def register():
# 向场景添加自定义属性
bpy.utils.register_class(OBJECT_OT_rename)
bpy.utils.register_class(OBJECT_PT_rename_panel)
bpy.utils.register_class(MyTool)
bpy.types.Scene.my_tool = bpy.props.PointerProperty(type=MyTool)
def unregister():
# 移除场景中的自定义属性
del bpy.types.Scene.my_tool
bpy.utils.unregister_class(OBJECT_OT_rename)
bpy.utils.unregister_class(OBJECT_PT_rename_panel)
bpy.utils.unregister_class(MyTool)
if __name__ == "__main__":
register()
如何使用:
- 复制脚本:将上面的脚本复制到 Blender 的文本编辑器中。
- 运行脚本:点击脚本菜单中的“运行 Script”按钮。
- 访问面板:转到 3D 视图的右侧,找到“工具”标签页下的“重命名对象”面板。
- 输入信息:在面板中输入所需的前缀、中缀、后缀。
- 操作按钮:使用面板上的按钮进行重命名操作。
翻译
搜索
复制