127 lines
4.9 KiB
Python
Executable File
127 lines
4.9 KiB
Python
Executable File
import bpy, sys, time, math
|
|
|
|
def clear_default_objects():
|
|
for name in ["Cube", "Light", "Camera"]:
|
|
if obj := bpy.data.objects.get(name):
|
|
bpy.data.objects.remove(obj)
|
|
|
|
def mark_sharp_edges(obj, angle=30):
|
|
bpy.context.view_layer.objects.active = obj
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
# 标记30°以上的边为锐边
|
|
bpy.ops.mesh.edges_select_sharp(sharpness=math.radians(angle))
|
|
bpy.ops.mesh.mark_sharp()
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
def planar_decimate(obj, angle_limit=35.0, ratio=0.5, protect_uv=True):
|
|
"""使用Decimate修改器+智能参数配置"""
|
|
print(f"启动高级减面流程 | 模式: 平面优化+权重控制")
|
|
|
|
#"""
|
|
# 添加Decimate修改器(平面模式)
|
|
mod = obj.modifiers.new(name="PlanarDecimate", type='DECIMATE')
|
|
mod.decimate_type = 'DISSOLVE' # 平面溶解模式
|
|
mod.angle_limit = math.radians(angle_limit) # 角度转弧度
|
|
mod.use_dissolve_boundaries = False # 溶解边界防止破面[5](@ref)
|
|
#"""
|
|
|
|
"""
|
|
mod = obj.modifiers.new(name="CollapseDecimate", type='DECIMATE')
|
|
mod.decimate_type = 'COLLAPSE'
|
|
mod.ratio = 0.1 # 保留30%面数
|
|
mod.use_symmetry = True
|
|
mod.vertex_group = "high_detail" # 绑定顶点组保护细节区域
|
|
#"""
|
|
|
|
# 保护关键属性
|
|
mod.delimit = {'NORMAL', 'UV'} if protect_uv else {'NORMAL'} # UV保护[1](@ref)
|
|
mod.use_symmetry = True # 对称保护[1](@ref)
|
|
|
|
# 保护边界:创建顶点组标记轮廓边
|
|
if "boundary_edges" not in obj.vertex_groups:
|
|
obj.vertex_groups.new(name="boundary_edges")
|
|
mod.vertex_group = "boundary_edges"
|
|
mod.invert_vertex_group = True # 仅减非边界区域
|
|
|
|
# 应用修改器
|
|
bpy.context.view_layer.objects.active = obj
|
|
bpy.ops.object.modifier_apply(modifier=mod.name)
|
|
print(f"平面优化完成 | 容差角: {angle_limit}°")
|
|
|
|
def collapse_decimate(obj, ratio=0.3, protect_groups=True):
|
|
"""核心减面:Collapse模式+细节保护"""
|
|
mod = obj.modifiers.new(name="CollapseDecimate", type='DECIMATE')
|
|
mod.decimate_type = 'COLLAPSE'
|
|
mod.ratio = ratio
|
|
|
|
# 顶点组保护(需提前在Blender中标记高细节区域)[1](@ref)
|
|
if protect_groups and "high_detail" in obj.vertex_groups:
|
|
mod.vertex_group = "high_detail"
|
|
mod.invert_vertex_group = True # 仅减非保护区域
|
|
print("启用顶点组保护 | 保留高细节区域")
|
|
|
|
# 关键保护设置
|
|
mod.use_symmetry = True
|
|
|
|
bpy.ops.object.modifier_apply(modifier=mod.name)
|
|
print(f"塌陷减面完成 | 保留比例: {ratio*100}%")
|
|
|
|
def triangulate_mesh(obj, quad_method='BEAUTY', ngon_method='BEAUTY'):
|
|
"""将模型三角化[1,2](@ref)"""
|
|
print("启动三角化处理...")
|
|
|
|
# 添加三角化修改器
|
|
mod = obj.modifiers.new(name="Triangulate", type='TRIANGULATE')
|
|
mod.quad_method = quad_method # 四边形处理方法
|
|
mod.ngon_method = ngon_method # 多边形处理方法
|
|
mod.min_vertices = 4 # 对四边形及以上面进行三角化
|
|
|
|
# 应用修改器
|
|
bpy.context.view_layer.objects.active = obj
|
|
bpy.ops.object.modifier_apply(modifier=mod.name)
|
|
|
|
# 验证三角化结果
|
|
tris = sum(1 for poly in obj.data.polygons if len(poly.vertices) == 3)
|
|
total_faces = len(obj.data.polygons)
|
|
print(f"三角化完成 | 三角面比例: {tris/total_faces*100:.1f}% ({tris}/{total_faces})")
|
|
|
|
def optimize_geometry(obj):
|
|
"""后处理:清理拓扑+法线修复[1,6](@ref)"""
|
|
bpy.context.view_layer.objects.active = obj
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.remove_doubles(threshold=0.001) # 合并重复顶点
|
|
bpy.ops.mesh.normals_make_consistent() # 统一法线
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
print("拓扑清理完成 | 法线已重计算")
|
|
|
|
if __name__ == "__main__":
|
|
argv = sys.argv[sys.argv.index("--") + 1:]
|
|
input_path = argv[0]
|
|
angle_limit = float(argv[1]) if len(argv) > 1 else 5.0
|
|
output_path = argv[2] if len(argv) > 2 else input_path.replace(".ply", "_decimated.ply")
|
|
|
|
# 硬件加速配置
|
|
# bpy.context.scene.cycles.device = 'GPU'
|
|
# bpy.context.preferences.system.memory_cache_limit = 4096
|
|
|
|
clear_default_objects()
|
|
# 导入PLY模型
|
|
bpy.ops.wm.ply_import(filepath=input_path)
|
|
obj = bpy.context.selected_objects[0]
|
|
print(f"开始减面操作:{obj.name}")
|
|
|
|
# 执行减面
|
|
mark_sharp_edges(obj)
|
|
planar_decimate(obj, angle_limit) # 先平面优化
|
|
collapse_decimate(obj, ratio=0.2) # 再智能塌陷 // 0.1-3效果好,0.2-3综合不错
|
|
optimize_geometry(obj) # 最终清理
|
|
triangulate_mesh(obj)
|
|
print("减面完成")
|
|
|
|
bpy.ops.wm.ply_export(
|
|
filepath=output_path,
|
|
apply_modifiers=False
|
|
)
|
|
print(f"模型已导出至:{output_path}")
|