在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,顶点序号,暴力匹配顶点位置

可以调整阈值,使其吸附判定的范围增大。