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'}