在blender雕刻形态键时,即使开了对称,对称部位部位也会有轻微的非常微小的位移,尤其是对称模式的平滑雕刻操作,会导致两侧的顶点位置无法对应
这样在编辑模式时,会导致对称无法使用。
对称编辑的条件是对称轴的值互为+-
以x轴对称为例:
x=x*-1,y=y,z=z
这个代码会解决小范围的不对称情况:
bl_info = {
"name": "Symmetry Fixer",
"author": "Your Name",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "View3D > Sidebar > Symmetry Fix Panel",
"description": "Fix symmetry of the mesh",
"warning": "",
"wiki_url": "",
"category": "3D View",
}import bpy
import bmesh
from mathutils import Vector
from bpy.props import FloatProperty, BoolPropertyclass SymmetryFixOperator(bpy.types.Operator):
bl_idname = "object.symmetry_fix"
bl_label = "开始修复"
bl_options = {'REGISTER', 'UNDO'} def execute(self, context):
# 获取用户设置的值
EPSILON = context.scene.epsilon
use_r_as_reference = context.scene.use_r_as_reference
use_l_as_reference = context.scene.use_l_as_reference
use_x = context.scene.use_x
use_y = context.scene.use_y
use_z = context.scene.use_z # 如果当前不是编辑模式,切换到编辑模式
if bpy.context.object.mode != 'EDIT':
bpy.ops.object.mode_set(mode='EDIT') # 获取当前编辑的物体
obj = bpy.context.edit_object
me = obj.data # 创建一个BMesh实例
bm = bmesh.from_edit_mesh(me) # 选中所有顶点
bpy.ops.mesh.select_all(action='SELECT') # 创建两个字典,用于存储左右两侧的顶点
# 创建两个字典,用于存储左边和右边的顶点
vertex_dict_l = {}
vertex_dict_r = {} # 遍历所有顶点
for v in bm.verts:
# 获取顶点的局部坐标
co_local = v.co
# 如果x坐标值、y坐标值和z坐标值都等于0的顶点,取消选择并跳过当前循环
if abs(co_local.x) <= EPSILON and abs(co_local.y) <= EPSILON and abs(co_local.z) <= EPSILON:
v.select = False
continue
# 根据x坐标的正负,将顶点添加到左或右的字典中
if co_local.x > EPSILON:
vertex_dict_l[tuple(co_local)] = v
elif co_local.x < -EPSILON:
vertex_dict_r[tuple(co_local)] = v # 遍历左边的字典
for co, v in vertex_dict_l.items():
# 计算对称位置的坐标
co_sym = (-co[0], co[1], co[2])
# 如果右边的字典中有这个坐标,那么这个顶点已经处于对称位置,取消选择它
if co_sym in vertex_dict_r:
v.select = False
vertex_dict_r[co_sym].select = False# 现在,被选择的顶点应该是那些未处于对称位置上的顶点
# 根据用户选择的参考方向,选择相应的顶点字典
if use_r_as_reference:
reference_dict = vertex_dict_r
target_dict = vertex_dict_l
elif use_l_as_reference:
reference_dict = vertex_dict_l
target_dict = vertex_dict_r
else:
return {'CANCELLED'} # 遍历参考字典中的所有顶点
for co_local_ref, v_ref in reference_dict.items():
# 计算这个顶点的对称坐标
co_sym = (co_local_ref[0] if not use_x else -co_local_ref[0],
co_local_ref[1] if not use_y else -co_local_ref[1],
co_local_ref[2] if not use_z else -co_local_ref[2])
# 找到目标字典中最近的顶点
co_local_tar, v_tar = min(target_dict.items(), key=lambda item: (Vector(item[0]) - Vector(co_sym)).length)
# 将这个顶点的坐标修改为对称坐标
v_tar.co = Vector(co_sym) # 更新BMesh到物体的网格数据
bmesh.update_edit_mesh(me) return {'FINISHED'}
class SymmetryFixPanel(bpy.types.Panel):
bl_label = "Symmetry Fix"
bl_idname = "OBJECT_PT_symmetry_fix"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Symmetry Fix Panel' def draw(self, context):
layout = self.layout layout.prop(context.scene, 'epsilon')
layout.prop(context.scene, 'use_x')
layout.prop(context.scene, 'use_y')
layout.prop(context.scene, 'use_z')
row = layout.row()
row.prop(context.scene, 'use_r_as_reference', toggle=True)
row.prop(context.scene, 'use_l_as_reference', toggle=True)
layout.operator("object.symmetry_fix")def update_use_r_as_reference(self, context):
if self.use_r_as_reference:
context.scene.use_l_as_reference = Falsedef update_use_l_as_reference(self, context):
if self.use_l_as_reference:
context.scene.use_r_as_reference = Falsebpy.types.Scene.use_r_as_reference = bpy.props.BoolProperty(
name="R",
description="Use right side as reference",
default=False,
update=update_use_r_as_reference
)bpy.types.Scene.use_l_as_reference = bpy.props.BoolProperty(
name="L",
description="Use left side as reference",
default=False,
update=update_use_l_as_reference
)bpy.types.Scene.use_x = bpy.props.BoolProperty(
name="X",
description="Enable symmetry detection on X axis",
default=False
)bpy.types.Scene.use_y = bpy.props.BoolProperty(
name="Y",
description="Enable symmetry detection on Y axis",
default=False
)bpy.types.Scene.use_z = bpy.props.BoolProperty(
name="Z",
description="Enable symmetry detection on Z axis",
default=False
)def register():
bpy.utils.register_class(SymmetryFixOperator)
bpy.utils.register_class(SymmetryFixPanel)
bpy.types.Scene.epsilon = FloatProperty(name="Epsilon", description="Threshold for symmetry detection", min=0.00000001, max=1000, default=0.0001)
bpy.types.Scene.use_r_as_reference = BoolProperty(name="R", description="Use right side as reference", default=False, update=update_use_r_as_reference)
bpy.types.Scene.use_l_as_reference = BoolProperty(name="L", description="Use left side as reference", default=False, update=update_use_l_as_reference)
bpy.types.Scene.use_x = BoolProperty(name="X", description="Enable symmetry detection on X axis", default=False)
bpy.types.Scene.use_y = BoolProperty(name="Y", description="Enable symmetry detection on Y axis", default=False)
bpy.types.Scene.use_z = BoolProperty(name="Z", description="Enable symmetry detection on Z axis", default=False)def unregister():
bpy.utils.unregister_class(SymmetryFixOperator)
bpy.utils.unregister_class(SymmetryFixPanel)
del bpy.types.Scene.epsilon
del bpy.types.Scene.use_r_as_reference
del bpy.types.Scene.use_l_as_reference
del bpy.types.Scene.use_x
del bpy.types.Scene.use_y
del bpy.types.Scene.use_zif __name__ == "__main__":
register()
-------------------------------------------------------------------------------------------------------------------------
(以上内容直接复制到控制台运行即可,使用方法为1.选中模型,2.设置好对称轴,3.点击“开始修复”)它的原理是自动查找已选轴的对称坐标的最近顶点,并将它们匹配到正确一侧的对称轴上。
优点是可以不破坏拓扑,UVmap,顶点序号,暴力匹配顶点位置
可以调整阈值,使其吸附判定的范围增大。