[blender暴力匹配顶点对称插件]【修复blender在雕刻形态键时,出现的网格拓扑不对称现象,导致镜像无法开启】

在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, BoolProperty

class 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 = False

def update_use_l_as_reference(self, context):
    if self.use_l_as_reference:
        context.scene.use_r_as_reference = False

bpy.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_z

if __name__ == "__main__":
    register()
 

-------------------------------------------------------------------------------------------------------------------------

(以上内容直接复制到控制台运行即可,使用方法为1.选中模型,2.设置好对称轴,3.点击“开始修复”)它的原理是自动查找已选轴的对称坐标的最近顶点,并将它们匹配到正确一侧的对称轴上。

优点是可以不破坏拓扑,UVmap,顶点序号,暴力匹配顶点位置

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

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值