import bpy import bmesh import os import sys import numpy as np from mathutils import Vector from ..project_config import PROJECT_CONFIG import datetime import json sys.path.append(PROJECT_CONFIG.executable_dir_path) sys.path.append(PROJECT_CONFIG.library_dir_path) sys.path.append(PROJECT_CONFIG.workplace_dir_path) def read_parameter_file(parameter_file_path): hole_height_arg = 0.0 hole_radius_arg = 0.0 use_honeycomb_type_arg = False hole_distance_arg = 0.0 texture_style_arg = "" use_sharp_edge_arg = False angle_of_se_arg = 0.0 area_threshold_arg = 0 boundary_margin_arg= 0.25 number_id_arg = False with open(parameter_file_path, 'r') as param_file: lines = param_file.readlines() for line in lines: parts = line.strip().split(':') if len(parts) == 2: param_name = parts[0].strip() param_value = parts[1].strip() # 根据参数名称设置相应的属性值 if param_name == "Hole Height": hole_height_arg = float(param_value) elif param_name == "Hole Radius": hole_radius_arg = float(param_value) elif param_name == "Use Honeycomb Type": use_honeycomb_type_arg = param_value.lower() == "true" elif param_name == "Hole Distance": hole_distance_arg = float(param_value) elif param_name == "Texture Style": texture_style_arg = str(param_value) elif param_name == "Use_sharp_edge": use_sharp_edge_arg = param_value.lower() == "true" elif param_name == "Angle_of_se": angle_of_se_arg = float(param_value) elif param_name == "Area_threshold": area_threshold_arg = int(param_value) elif param_name == "Boundary_Margin": boundary_margin_arg = float(param_value) elif param_name == "Number ID": number_id_arg = param_value.lower() == "true" return (hole_height_arg, hole_radius_arg, \ use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,\ angle_of_se_arg, area_threshold_arg, boundary_margin_arg, number_id_arg) class SurfaceHoleOperator(bpy.types.Operator): bl_idname: str = "designauto.batch_texture_3d_surface_hole" bl_label: str = "批量表面打孔纹理" bl_options = {"REGISTER", "UNDO"} parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "qianya1.txt") processed_files_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "processed_files.txt") recent_parameter_file_path= os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "recent.txt") failed_models_files_path=os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "failed_models_files.txt") counter_path=os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "counter.txt") items = [ ('前牙一', '前牙一', ''), ('前牙二', '前牙二', ''), ('后牙一', '后牙一', ''), ('后牙二', '后牙二', ''), ('后牙三', '后牙三', ''), ('贴面一', '贴面一', ''), ('贴面二', '贴面二', ''), ('桥一', '桥一', ''), ('桥二', '桥二', '') ] style_items = [ ('圆台1', '圆台1', ''), ('圆台2', '圆台2', ''), ('圆柱', '圆柱', ''), ('方形', '方形', ''), ('三角形', '三角形', ''), ('六边形', '六边形', ''), ('八边形', '八边形', '') ] with open(recent_parameter_file_path, 'r') as param_file: lines = param_file.readlines() for line in lines: parts = line.strip().split(':') if len(parts) == 2: param_name = parts[0].strip() param_value = parts[1].strip() # 根据参数名称设置相应的属性值 if param_name == "Hole Height": hole_height_arg = float(param_value) elif param_name == "Hole Radius": hole_radius_arg = float(param_value) elif param_name == "Use Honeycomb Type": use_honeycomb_type_arg = param_value.lower() == "true" elif param_name == "Hole Distance": hole_distance_arg = float(param_value) elif param_name == "Texture Style": texture_style_arg = str(param_value) elif param_name == "Use_sharp_edge": use_sharp_edge_arg = param_value.lower() == "true" elif param_name == "Angle_of_se": angle_of_se_arg = float(param_value) elif param_name == "Area_threshold": area_threshold_arg = int(param_value) elif param_name == "Boundary_Margin": boundary_margin_arg = float(param_value) elif param_name == "Number ID": number_id_arg = param_value.lower() == "true" def update_part(self, context): if self.type == '前牙一': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "qianya1.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '前牙二': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "qianya2.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '后牙一': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "houya1.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '后牙二': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "houya2.txt") (hole_height_arg, hole_radius_arg,use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '后牙三': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "houya3.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '贴面一': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "tiemian1.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '贴面二': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "tiemian2.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '桥一': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "qiao1.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() elif self.type == '桥二': parameter_file_path = os.path.join(PROJECT_CONFIG.workplace_dir_path, "parameters", "qiao2.txt") (hole_height_arg, hole_radius_arg, use_honeycomb_type_arg, \ hole_distance_arg, texture_style_arg, use_sharp_edge_arg,angle_of_se_arg, area_threshold_arg,\ boundary_margin_arg, number_id_arg) = read_parameter_file(parameter_file_path) self.hole_height=hole_height_arg self.hole_radius=hole_radius_arg self.use_honeycomb_type=use_honeycomb_type_arg self.hole_distance=hole_distance_arg self.texture_style=texture_style_arg self.use_sharp_edge=use_sharp_edge_arg self.angle_of_se=angle_of_se_arg self.area_threshold=area_threshold_arg self.boundary_margin=boundary_margin_arg self.number_id=number_id_arg bpy.context.view_layer.update() type:bpy.props.EnumProperty(name="Type", items=items,default="前牙一",update=update_part) hole_height: bpy.props.FloatProperty(name="孔洞深度", default=hole_height_arg) hole_radius: bpy.props.FloatProperty(name="孔洞半径", default=hole_radius_arg) use_honeycomb_type:bpy.props.BoolProperty(name="蜂窝型", default=use_honeycomb_type_arg) hole_distance: bpy.props.FloatProperty(name="孔洞间距", default=hole_distance_arg) texture_style:bpy.props.EnumProperty(name="纹理样式",items=style_items, description="Choose the style of texture", default="圆柱") use_sharp_edge: bpy.props.BoolProperty(name="使用锐边算法", default=use_sharp_edge_arg) angle_of_se:bpy.props.FloatProperty(name="锐边阈值", default=angle_of_se_arg) area_threshold:bpy.props.IntProperty(name="面积阈值", default=area_threshold_arg) boundary_margin:bpy.props.FloatProperty(name="边缘距离", description="边缘距离", default=boundary_margin_arg) number_id:bpy.props.BoolProperty(name="自动打标", description="ID", default=number_id_arg) save:bpy.props.BoolProperty(name="保存参数", description="save", default=True) def execute(self, context): counter = 0 folder_path=PROJECT_CONFIG.workplace_dir_path folder_path=folder_path+"/assets" file_list = os.listdir(folder_path) model_files = [filename for filename in file_list if filename.endswith(".stl")] if not model_files: self.report({'ERROR'}, "No STL files found in the folder!") return {'CANCELLED'} with open(self.processed_files_path, 'r') as file: processed_files = file.read().splitlines() with open(self.failed_models_files_path, 'r') as file: failed_models_files = [line.strip().split(' % ')[1] for line in file.readlines()] for model_file in model_files: if model_file in processed_files: print(f'Skipping {model_file}, already processed.') continue if model_file in failed_models_files: print(f'Skipping {model_file}, it is failed model.') continue file_number=0 try: file_number = int(model_file[:3]) except ValueError: print(f"Error extracting the first three digits from {model_file}. Setting counter to 0.") file_number = 0 counter = file_number current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') model_file_with_time = f'{current_time} % {model_file}' with open(self.failed_models_files_path, 'a') as failed_file: failed_file.write(model_file_with_time + '\n') file_path = os.path.join(folder_path, model_file) bpy.ops.import_mesh.stl(filepath=file_path) model_filename = os.path.splitext(os.path.basename(model_file))[0] imported_object = bpy.context.selected_objects[0] bpy.ops.object.select_all(action='DESELECT') imported_object.select_set(True) working_object = context.active_object bpyscene = bpy.context.scene workplace = PROJECT_CONFIG.workplace_dir_path executable_path = PROJECT_CONFIG.executable_dir_path tri_mesh_path = os.path.join(workplace, "assets", "model.obj") bpy.ops.export_scene.obj(filepath=tri_mesh_path, use_selection=True, axis_forward='Y', axis_up='Z', use_edges=False, use_animation=False, use_materials=False, use_uvs=False, use_normals=False, use_mesh_modifiers=False, use_nurbs=False, use_smooth_groups=False, use_vertex_groups=False, use_blen_objects=False, use_smooth_groups_bitflags=False) import dapy_t3d_surface_hole sample_distance = 2.0*self.hole_radius+self.hole_distance hole_factor = 2.0*self.hole_radius / sample_distance texture_num = 0 if self.texture_style == "圆台1": texture_num=1 elif self.texture_style == "圆台2": texture_num=2 elif self.texture_style == "圆柱": texture_num=3 elif self.texture_style == "方形": texture_num=4 elif self.texture_style == "三角形": texture_num=5 elif self.texture_style == "六边形": texture_num=6 elif self.texture_style == "八边形": texture_num=7 try: os.chdir(executable_path) if self.type == "桥一" or self.type == "桥二": if self.number_id == True: mesh_with_hole = dapy_t3d_surface_hole.GenerateSurfaceHoles(self.hole_height, hole_factor,self.use_honeycomb_type, sample_distance, texture_num,self.use_sharp_edge,self.angle_of_se,self.area_threshold,self.boundary_margin,counter,False,True) else: mesh_with_hole = dapy_t3d_surface_hole.GenerateSurfaceHoles(self.hole_height, hole_factor,self.use_honeycomb_type, sample_distance, texture_num,self.use_sharp_edge,self.angle_of_se,self.area_threshold,self.boundary_margin,0,False,True) else: if self.number_id == True: mesh_with_hole = dapy_t3d_surface_hole.GenerateSurfaceHoles(self.hole_height, hole_factor,self.use_honeycomb_type, sample_distance, texture_num,self.use_sharp_edge,self.angle_of_se,self.area_threshold,self.boundary_margin,counter,False,False) else: mesh_with_hole = dapy_t3d_surface_hole.GenerateSurfaceHoles(self.hole_height, hole_factor,self.use_honeycomb_type, sample_distance, texture_num,self.use_sharp_edge,self.angle_of_se,self.area_threshold,self.boundary_margin,0,False,False) except Exception as e: self.report({'ERROR'}, f"Failed to run GenerateSurfaceHoles: {str(e)}") continue working_object.hide_set(True) new_mesh = bpy.data.meshes.new("mesh-" + working_object.name + "-hole") new_mesh.from_pydata(mesh_with_hole.mat_coordinates.tolist(), [], mesh_with_hole.mat_faces.tolist()) new_mesh.update() new_object = bpy.data.objects.new(working_object.name + "-hole", new_mesh) export_filename = f"{counter}_{model_filename}.stl" bpy.context.collection.objects.link(new_object) bpy.context.view_layer.objects.active = new_object new_object.select_set(True) export_path=PROJECT_CONFIG.workplace_dir_path+"/results/"+export_filename bpy.ops.export_mesh.stl(filepath=export_path, use_selection=True, axis_forward='Y', axis_up='Z') bpy.data.objects.remove(imported_object) bpy.data.objects.remove(new_object, do_unlink=True) with open(self.failed_models_files_path, 'r') as file: lines = file.readlines() with open(self.failed_models_files_path, 'w') as file: for line in lines: saved_model_file = line.strip().split(' % ')[1] if saved_model_file != model_file: file.write(line) with open(self.processed_files_path, 'a') as file: file.write(model_file + '\n') with open(self.counter_path, 'w') as counter_file: counter_file.write(str(counter)) with open(self.processed_files_path, 'w') as file: file.write('') with open(self.counter_path, 'w') as file: file.write('') if self.save == True: with open(self.recent_parameter_file_path, 'w') as param_file: param_file.write(f"Hole Height: {self.hole_height}\n") param_file.write(f"Hole Radius: {self.hole_radius}\n") param_file.write(f"Use Honeycomb Type: {self.use_honeycomb_type}\n") param_file.write(f"Hole Distance: {self.hole_distance}\n") param_file.write(f"Texture Style: {self.texture_style}\n") param_file.write(f"Use_sharp_edge: {self.use_sharp_edge}\n") param_file.write(f"Angle_of_se: {self.angle_of_se}\n") param_file.write(f"Area_threshold: {self.area_threshold_arg}\n") param_file.write(f"Boundary_Margin: {self.boundary_margin}\n") param_file.write(f"Number ID: {self.number_id}\n") with open(self.parameter_file_path, 'w') as param_file: param_file.write(f"Hole Height: {self.hole_height}\n") param_file.write(f"Hole Radius: {self.hole_radius}\n") param_file.write(f"Use Honeycomb Type: {self.use_honeycomb_type}\n") param_file.write(f"Hole Distance: {self.hole_distance}\n") param_file.write(f"Texture Style: {self.texture_style}\n") param_file.write(f"Use_sharp_edge: {self.use_sharp_edge}\n") param_file.write(f"Angle_of_se: {self.angle_of_se}\n") param_file.write(f"Area_threshold: {self.area_threshold_arg}\n") param_file.write(f"Boundary_Margin: {self.boundary_margin}\n") param_file.write(f"Number ID: {self.number_id}\n") return {'FINISHED'} def invoke(self, context, event): if context.active_object.type == 'MESH': return context.window_manager.invoke_props_dialog(self) else: self.report({'ERROR'}, "Selected object is not a mesh!") return {'CANCELLED'}