表面纹理
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

462 lines
24 KiB

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