import bpy from bpy.props import * from bpy.types import Operator, Panel import os import mathutils import bpy.utils.previews import sys from bpy_extras.io_utils import ExportHelper from mathutils import Vector # Load custom icon # main_dir = os.path.dirname(__file__) # icon_logo_path = os.path.join(main_dir, "sinewave.png") # icon_collection = bpy.utils.previews.new() # icon_collection.load("icon_logo", icon_logo_path, 'IMAGE') ############################################################################################################ class VIEW3D_PT_MeshFixLocalPanel(bpy.types.Panel): bl_idname = "VIEW3D_PT_MeshFixLocalPanel" bl_label = "Local Fix" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Mesh Repair" #bl_options = {'DEFAULT_CLOSED'} # # Note: All drawers are already closed by default @classmethod def poll(cls, context): return context.object is not None and context.object.mode == 'EDIT' def draw(self, context): layout = self.layout scene = context.scene props = scene.meshfixtool_properties space = bpy.context.space_data wiz_bool = props.wiz_boolean # Deprecated from 4.2 # row = layout.row() # row.operator("object.flatten_local", text="Flatten Surface", icon ='HIDE_ON') # row.prop(props, "bumper_reduction") #------ V2 #layout.box() row = layout.row() row.operator("mesh.select_more", text="Select More", icon ='EVENT_PLUS') row.operator("mesh.select_less", text="Select Less", icon ='EVENT_MINUS') # layout.separator() # row = layout.row() # col = row.column() # col.prop(space.overlay, "show_face_orientation", text="Face Orientation") # col = row.column() # col.enabled = hasattr(space, 'overlay') and space.overlay.show_face_orientation # col.operator("object.local_face_normal", text="Unify/Flip Face", icon ='ORIENTATION_NORMAL') # col = row.column() # if hasattr(bpy.context.scene, "fix_wizard_properties") and wiz_bool: # col.operator("object.refind_local", text="Refine", icon ='KEYTYPE_KEYFRAME_VEC') # else: # col.operator("object.refind_local", text="Refine", icon ='MESH_ICOSPHERE') # row = layout.row() # if hasattr(bpy.context.scene, "fix_wizard_properties") and wiz_bool: # row.operator("object.remesh_local_v2", text="Remesh", icon ='KEYTYPE_KEYFRAME_VEC') # else: # row.operator("object.remesh_local_v2", text="Remesh", icon ='MOD_REMESH') # row.operator("object.smooth_local_v2", text="Smooth", icon = 'MOD_SMOOTH') # row.operator("object.reduce_local", text="Decimate", icon = 'MOD_DECIM') has_wiz = hasattr(scene, "fix_wizard_properties") and wiz_bool row = layout.row() col = row.column() col.prop(space.overlay, "show_face_orientation", text="Face Normal") col = row.column() col.enabled = hasattr(space, 'overlay') and space.overlay.show_face_orientation col.operator("object.local_face_normal", text="Unify/Flip", icon ='ORIENTATION_NORMAL') col = row.column() if has_wiz: col.operator("object.refind_local", text="Refine", icon ='KEYTYPE_KEYFRAME_VEC') else: col.operator("object.refind_local", text="Refine", icon ='MESH_ICOSPHERE') row = layout.row() if has_wiz: row.operator("object.remesh_local_v2", text="Remesh", icon ='KEYTYPE_KEYFRAME_VEC') else: row.operator("object.remesh_local_v2", text="Remesh", icon ='MOD_REMESH') row.operator("object.smooth_local_v2", text="Smooth", icon = 'MOD_SMOOTH') row.operator("object.reduce_local", text="Reduce", icon = 'MOD_DECIM') ############################################################################################################ class VIEW3D_PT_MeshFixGlobalPanel(bpy.types.Panel): bl_idname = "VIEW3D_PT_MeshFixGlobalPanel" bl_label = "Global Fix" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Mesh Repair" #bl_options = {'DEFAULT_CLOSED'} # # Note: All drawers are already closed by default @classmethod def poll(cls, context): return context.object is not None def draw(self, context): layout = self.layout scene = context.scene props = scene.meshfixtool_properties wiz_bool = props.wiz_boolean fill_settings = props.wiz_fill_settings_boolean full_func = props.tri_boolean or props.quad_boolean # or props.poly_boolean active_obj = context.active_object nvs = 0 ls_bool = False size_lmt = None if active_obj is not None and active_obj.type == 'MESH': nvs = len(active_obj.data.vertices) #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# if active_obj and active_obj.type == 'MESH': bbox = [active_obj.matrix_world @ Vector(corner) for corner in active_obj.bound_box] size_x = (bbox[4] - bbox[0]).length size_y = (bbox[2] - bbox[0]).length size_z = (bbox[1] - bbox[0]).length smallest_size = min(size_x, size_y, size_z) size_lmt = smallest_size / 200 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# if 'Lattice_Structs' in active_obj: ls_bool = True # layout.label(text="Enable the LoopTools") # layout.label(text="Enable the Edit Mesh Tools") # v4.0.2 row = layout.row() row.prop(props, "tri_boolean", text="Tri Mesh", icon ='MOD_TRIANGULATE') row.prop(props, "quad_boolean", text="Quad Mesh", icon ='SPLIT_VERTICAL') # row.prop(props, "poly_boolean", text="Poly Mesh", icon ='MESH_PLANE') layout.prop(props, "face_normal_boolean", icon ='ORIENTATION_NORMAL') row = layout.row() row.prop(props, "minor_parts_boolean", icon ='UNLINKED') row.prop(props, "minor_parts_threshold",text="Min %", slider=True) row = layout.row() row.enabled = full_func row.prop(props, "spikes_boolean", icon ='SHARPCURVE') row.prop(props, "spikes_angle_limit",text="Min Angle", slider=True) row = layout.row() row.enabled = full_func row.prop(props, "intersection_boolean", icon ='MOD_SOLIDIFY') row.prop(props, "intersection_angle_limit",text="Min Angle", slider=True) row = layout.row() row.enabled = full_func row.prop(props, "volume_intersection_boolean", icon ='SELECT_EXTEND') if hasattr(bpy.context.scene, "fix_wizard_properties") and props.wiz_boolean: props_wiz = scene.fix_wizard_properties row = layout.row() row.enabled = full_func row.prop(props_wiz, "record_filled_holes", text="", icon = 'EVENT_NDOF_BUTTON_ROLL_CCW') row.operator("object.wiz_clear_all_holes_record", text="", icon = 'FILE_REFRESH') row.prop(props, "holes_boolean", text = 'Smart Fill', toggle=True) row.prop(props, "wiz_fill_settings_boolean", text = '', icon = 'PREFERENCES') if full_func and props.wiz_fill_settings_boolean: #*************************************************************************************# hole_size_bool = props_wiz.specific_hole_mesh_size_bool hole_ratio_bool = props_wiz.specific_hole_mesh_ratio_bool box = layout.box() row = box.row() col = row.column() col.scale_x = 2.3 col.prop(props_wiz, "specific_hole_mesh_size_bool", toggle=True) col = row.column() col.enabled = hole_size_bool col.scale_x = 3 col.prop(props_wiz, "hole_mesh_size", text="") row = box.row() col = row.column() col.scale_x = 2.3 col.prop(props_wiz, "specific_hole_mesh_ratio_bool", toggle=True) col = row.column() col.enabled = hole_ratio_bool col.scale_x = 3 col.prop(props_wiz, "hole_mesh_ratio", text="") if size_lmt and hole_size_bool and props_wiz.hole_mesh_size < size_lmt: layout.alert = True layout.label(text="Caution: The hole mesh size may be too small.", icon="ERROR") layout.alert = False if hole_ratio_bool and props_wiz.hole_mesh_ratio < 0.2: layout.alert = True layout.label(text="Caution: The hole mesh ratio may be too small.", icon="ERROR") layout.alert = False row = box.row() row.prop(props_wiz, "hole_number_limit", text = "Number Limit", slider = True) row.prop(props_wiz, "hole_max_size_limit", text = "Size Limit") #*************************************************************************************# else: row = layout.row() row.enabled = full_func row.prop(props, "holes_boolean", text = 'Fill Holes', icon ='HOLDOUT_ON') if not full_func: layout.label(text="Enable Tri/Quad Mesh to access full functions", icon="MODIFIER") else: if ls_bool: layout.alert = True layout.label(text="Repairing the lattice structure is not recommended.", icon="ERROR") layout.alert = False elif nvs > 300000: layout.alert = True layout.label(text="Caution: Large mesh detected.", icon="ERROR") layout.alert = False else: layout.separator() # if props.minor_parts_boolean or props.spikes_boolean or props.intersection_boolean or props.holes_boolean or props.face_normal_boolean or props.volume_intersection_boolean: # if not props.meshfixing: # row = layout.row() # row.operator("object.fix_mesh_global", text="AutoFix", icon ='HAND') # row.prop(props, "statistics_boolean",text="", icon ='TEXT') # else: # layout.operator("object.fix_mesh_global", text="Calculating ...", icon ='SEQ_CHROMA_SCOPE') # v4.0.1 if not props.meshfixing: row = layout.row() col = row.column() # v4.0.2 col.enabled = full_func or (props.face_normal_boolean or props.minor_parts_boolean) # col.enabled = ( # full_func or # props.minor_parts_boolean or # props.spikes_boolean or # props.intersection_boolean or # props.holes_boolean or # props.face_normal_boolean or # props.volume_intersection_boolean # ) col.scale_y = 1.2 col.operator("object.fix_mesh_global", text="AutoFix", icon ='HAND') col = row.column() col.scale_y = 1.2 col.prop(props, "statistics_boolean",text="", icon ='TEXT') else: row = layout.row() col = row.column() col.scale_y = 1.2 col.alert = True col.operator("object.fix_mesh_global", text="Calculating ...", icon ='SORTTIME') col.alert = False col = row.column() col.scale_y = 1.2 col.prop(props, "statistics_boolean",text="", icon ='TEXT') layout.separator() if props.statistics_boolean: box = layout.box() row = box.row() row.label(text="Mesh fixed:") # v4.0.1 if hasattr(bpy.context.scene, "fix_wizard_properties"): row.prop(props, "wiz_boolean", text="", icon = 'SOLO_ON' if wiz_bool else 'SOLO_OFF') # row.operator("wm.mrts_open_website", icon = 'INTERNET') row.operator("object.mrts_sinewave", icon = 'FORCE_HARMONIC') box.label(text=f"Verts: {props.sum_vertices}") box.label(text=f"Edges: {props.sum_edges}") box.label(text=f"Faces: {props.sum_faces}") if full_func and props.volume_intersection_boolean: box.label(text=f"Intersect Volumes: {props.sum_volumes}") # v4.0.2 if full_func and props.holes_boolean: if hasattr(bpy.context.scene, "fix_wizard_properties") and props.wiz_boolean: props_wiz = scene.fix_wizard_properties total_hole_number = props_wiz.total_hole_number # failed_number = props_wiz.failed_number fixed_number = props_wiz.fixed_number row = box.row() if total_hole_number > 0 and fixed_number > 0: row.label(text=f"Holes: {fixed_number} 🌟") else: row.label(text="Holes: 0 🧙") if props_wiz.record_filled_holes: row.operator("object.select_next_filled_hole", text="Next Hole", icon = 'LAYER_ACTIVE') row.operator("object.select_all_filled_hole", text="All Holes", icon = 'OUTLINER_OB_POINTCLOUD') # if total_hole_number > 0: # box.label(text=f"Holes:") # row = box.row() # row.label(text=f"Total : {total_hole_number}") # if fixed_number > 0: # row.label(text=f"Filled : {fixed_number}") # if failed_number > 0: # row.alert = True # row.label(text=f"Failed: {failed_number}") # row.alert = False # else: # box.label(text="Holes: 0") else: box.label(text=f"Holes: {props.sum_holes}") ###############################################################################