expre-gen/blender/3.4/scripts/addons/amaranth/modeling/symmetry_tools.py
2025-10-17 09:47:23 +08:00

178 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SPDX-License-Identifier: GPL-2.0-or-later
"""
Symmetry Tools: Find Asymmetric + Make Symmetric (by Sergey Sharybin)
Our character wasnt completely symmetric in some parts where it was
supposed to, this could be by moving vertices by mistake or just reasons.
To fix this in a fast way, Sergey coded this two super useful tools:
* Find Asymmetric:
Selects vertices that dont have the same position on the opposite side.
* Make Symmetric:
Move selected vertices to match the position of those on the other side.
This tools may not apply on every single model out there, but I tried it
in many different characters and it worked. So probably better use it on
those models that were already symmetric at some point, modeled with a
mirror modifier or so.
Search (spacebar) for "Find Asymmetric", and "Make Symmetric""Settings".
> Developed during Caminandes Open Movie Project
"""
import bpy
import bmesh
from mathutils import Vector
class AMTH_MESH_OT_find_asymmetric(bpy.types.Operator):
"""
Find asymmetric vertices
"""
bl_idname = "mesh.find_asymmetric"
bl_label = "Find Asymmetric"
bl_options = {"UNDO", "REGISTER"}
@classmethod
def poll(cls, context):
object = context.object
if object:
return object.mode == "EDIT" and object.type == "MESH"
return False
def execute(self, context):
threshold = 1e-6
object = context.object
bm = bmesh.from_edit_mesh(object.data)
# Deselect all the vertices
for v in bm.verts:
v.select = False
for v1 in bm.verts:
if abs(v1.co[0]) < threshold:
continue
mirror_found = False
for v2 in bm.verts:
if v1 == v2:
continue
if v1.co[0] * v2.co[0] > 0.0:
continue
mirror_coord = Vector(v2.co)
mirror_coord[0] *= -1
if (mirror_coord - v1.co).length_squared < threshold:
mirror_found = True
break
if not mirror_found:
v1.select = True
bm.select_flush_mode()
bmesh.update_edit_mesh(object.data)
return {"FINISHED"}
class AMTH_MESH_OT_make_symmetric(bpy.types.Operator):
"""
Make symmetric
"""
bl_idname = "mesh.make_symmetric"
bl_label = "Make Symmetric"
bl_options = {"UNDO", "REGISTER"}
@classmethod
def poll(cls, context):
object = context.object
if object:
return object.mode == "EDIT" and object.type == "MESH"
return False
def execute(self, context):
threshold = 1e-6
object = context.object
bm = bmesh.from_edit_mesh(object.data)
for v1 in bm.verts:
if v1.co[0] < threshold:
continue
if not v1.select:
continue
closest_vert = None
closest_distance = -1
for v2 in bm.verts:
if v1 == v2:
continue
if v2.co[0] > threshold:
continue
if not v2.select:
continue
mirror_coord = Vector(v2.co)
mirror_coord[0] *= -1
distance = (mirror_coord - v1.co).length_squared
if closest_vert is None or distance < closest_distance:
closest_distance = distance
closest_vert = v2
if closest_vert:
closest_vert.select = False
closest_vert.co = Vector(v1.co)
closest_vert.co[0] *= -1
v1.select = False
for v1 in bm.verts:
if v1.select:
closest_vert = None
closest_distance = -1
for v2 in bm.verts:
if v1 != v2:
mirror_coord = Vector(v2.co)
mirror_coord[0] *= -1
distance = (mirror_coord - v1.co).length_squared
if closest_vert is None or distance < closest_distance:
closest_distance = distance
closest_vert = v2
if closest_vert:
v1.select = False
v1.co = Vector(closest_vert.co)
v1.co[0] *= -1
bm.select_flush_mode()
bmesh.update_edit_mesh(object.data)
return {"FINISHED"}
def ui_symmetry_tools(self, context):
if bpy.context.mode == 'EDIT_MESH':
self.layout.separator()
self.layout.operator(
AMTH_MESH_OT_find_asymmetric.bl_idname,
icon="ALIGN_CENTER", text="Find Asymmetric")
self.layout.operator(
AMTH_MESH_OT_make_symmetric.bl_idname,
icon="ALIGN_JUSTIFY", text="Make Symmetric")
def register():
bpy.utils.register_class(AMTH_MESH_OT_find_asymmetric)
bpy.utils.register_class(AMTH_MESH_OT_make_symmetric)
bpy.types.VIEW3D_MT_edit_mesh.append(ui_symmetry_tools)
def unregister():
bpy.utils.unregister_class(AMTH_MESH_OT_find_asymmetric)
bpy.utils.unregister_class(AMTH_MESH_OT_make_symmetric)
bpy.types.VIEW3D_MT_edit_mesh.remove(ui_symmetry_tools)