blenderings/retopo_tools/tool.py

109 lines
3.9 KiB
Python

import bpy
from bpy.types import Context, Object, Mesh, Scene
def assert_mirror_modifier(obj: Object, target: Object = None):
modifier = next((modifier for modifier in obj.modifiers if modifier.type == "MIRROR"), None)
if modifier is None:
modifier = obj.modifiers.new("Mirror", "MIRROR")
modifier.use_clip = True
if target is not None and modifier.mirror_object is None:
modifier.mirror_object = target
def assert_shrinkwrap_modifier(obj: Object, target: Object = None):
modifier = next((modifier for modifier in obj.modifiers if modifier.type == "SHRINKWRAP"), None)
if modifier is None:
modifier = obj.modifiers.new("Shrinkwrap", "SHRINKWRAP")
modifier.show_viewport = False
modifier.wrap_method = "PROJECT"
modifier.wrap_mode = "ABOVE_SURFACE"
modifier.project_limit = 0.0
modifier.use_negative_direction = True
modifier.use_positive_direction = True
vgroup = next((group for group in obj.vertex_groups if "no_shrinkwrap" in group.name), None)
if vgroup is None:
vgroup = obj.vertex_groups.new(name="no_shrinkwrap")
modifier.vertex_group = vgroup.name
modifier.invert_vertex_group = True
if target is not None and modifier.target is None:
modifier.target = target
def assert_retopo_modifiers(ctx: Context, target: Object = None):
active = ctx.active_object
assert_mirror_modifier(active, target)
assert_shrinkwrap_modifier(active, target)
def setup_project_snap_settings(scene: Scene):
scene.tool_settings.use_snap = True
scene.tool_settings.snap_elements = {"FACE"}
scene.tool_settings.use_snap_self = True
scene.tool_settings.use_snap_edit = True
scene.tool_settings.use_snap_nonedit = True
scene.tool_settings.use_snap_selectable = True
scene.tool_settings.use_snap_align_rotation = False
scene.tool_settings.use_snap_backface_culling = True
scene.tool_settings.use_snap_project = True
scene.tool_settings.use_snap_translate = True
scene.tool_settings.use_snap_rotate = True
scene.tool_settings.use_snap_scale = True
def setup_vertex_snap_settings(scene: Scene):
scene.tool_settings.use_snap = True
scene.tool_settings.snap_elements = {"VERTEX"}
scene.tool_settings.use_snap_self = True
scene.tool_settings.use_snap_edit = True
scene.tool_settings.use_snap_nonedit = False
scene.tool_settings.use_snap_selectable = True
scene.tool_settings.use_snap_align_rotation = False
scene.tool_settings.use_snap_backface_culling = True
scene.tool_settings.use_snap_translate = True
scene.tool_settings.use_snap_rotate = True
scene.tool_settings.use_snap_scale = True
def get_context_target(ctx: Context):
if len(ctx.selected_objects) != 2:
return None
return next(obj for obj in ctx.selected_objects if obj is not ctx.active_object)
class RetopoProjectModeOperator(bpy.types.Operator):
bl_idname = "object.retopoprojectmode"
bl_label = "RetopoProjectMode"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(Cls, context: Context):
return context.active_object is not None and type(context.active_object.data) is Mesh
def execute(self, context: Context):
bpy.ops.object.mode_set(mode="EDIT")
target = get_context_target(context)
assert_retopo_modifiers(context, target)
setup_project_snap_settings(context.scene)
self.report({"INFO"}, "Project Mode")
return {'FINISHED'}
class RetopoVertexModeOperator(bpy.types.Operator):
bl_idname = "object.retopovertexmode"
bl_label = "RetopoVertexMode"
def execute(self, context):
bpy.ops.object.mode_set(mode="EDIT")
target = get_context_target(context)
assert_retopo_modifiers(context, target)
setup_vertex_snap_settings(context.scene)
self.report({"INFO"}, "Vertex Mode")
return {'FINISHED'}