571 lines
18 KiB
Python
571 lines
18 KiB
Python
|
|
"""
|
|||
|
|
operators.py — VDE Blender 操作符
|
|||
|
|
=================================
|
|||
|
|
所有 VDE 操作符定义,可在 Blender 中搜索和调用。
|
|||
|
|
|
|||
|
|
操作符列表:
|
|||
|
|
- VDE_OT_import_step — 导入 STEP 文件
|
|||
|
|
- VDE_OT_export_step — 导出选中对象为 STEP
|
|||
|
|
- VDE_OT_create_box — 创建 Box 图元
|
|||
|
|
- VDE_OT_create_cylinder — 创建 Cylinder 图元
|
|||
|
|
- VDE_OT_create_sphere — 创建 Sphere 图元
|
|||
|
|
- VDE_OT_boolean_union — 布尔并集
|
|||
|
|
- VDE_OT_boolean_intersect— 布尔交集
|
|||
|
|
- VDE_OT_boolean_diff — 布尔差集
|
|||
|
|
- VDE_OT_sdf_to_mesh — SDF→Mesh 转换
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
import bpy
|
|||
|
|
from bpy.props import (
|
|||
|
|
StringProperty, FloatProperty, IntProperty,
|
|||
|
|
BoolProperty, EnumProperty, PointerProperty,
|
|||
|
|
)
|
|||
|
|
from bpy_extras.io_utils import ImportHelper, ExportHelper
|
|||
|
|
|
|||
|
|
from . import vde_bridge
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
# 辅助函数
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
def get_prefs():
|
|||
|
|
"""获取插件偏好设置。"""
|
|||
|
|
return bpy.context.preferences.addons[__package__].preferences
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_bridge():
|
|||
|
|
"""获取 VDE 桥接实例。"""
|
|||
|
|
prefs = get_prefs()
|
|||
|
|
return vde_bridge.VDEBridge(
|
|||
|
|
vde_lib_path=prefs.vde_lib_path,
|
|||
|
|
python_executable=prefs.python_path or None,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def report_and_raise(self, error_msg):
|
|||
|
|
"""报告错误并设置操作符状态。"""
|
|||
|
|
self.report({'ERROR'}, error_msg)
|
|||
|
|
return {'CANCELLED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
# 导入/导出操作符
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
class VDE_OT_import_step(bpy.types.Operator, ImportHelper):
|
|||
|
|
"""从 STEP 文件导入几何体为 Blender Mesh。"""
|
|||
|
|
bl_idname = "vde.import_step"
|
|||
|
|
bl_label = "导入 STEP"
|
|||
|
|
bl_description = "使用 VDE 引擎从 STEP/STP 文件导入 B-Rep 几何体"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
filename_ext = ".stp;.step"
|
|||
|
|
filter_glob: StringProperty(
|
|||
|
|
default="*.stp;*.step",
|
|||
|
|
options={'HIDDEN'},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
heal_import: BoolProperty(
|
|||
|
|
name="自动修复",
|
|||
|
|
description="导入后自动修复拓扑缺陷(合并顶点、闭合缝隙)",
|
|||
|
|
default=True,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
deflection: FloatProperty(
|
|||
|
|
name="网格精度",
|
|||
|
|
description="B-Rep 离散化为网格的弦高偏差 (越小越精细)",
|
|||
|
|
default=0.01,
|
|||
|
|
min=0.001,
|
|||
|
|
max=1.0,
|
|||
|
|
precision=4,
|
|||
|
|
subtype='FACTOR',
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
bridge = get_bridge()
|
|||
|
|
self.report({'INFO'}, f"正在导入: {self.filepath}")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
result = bridge.execute("import_step", path=self.filepath)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
name = os.path.basename(self.filepath).rsplit(".", 1)[0]
|
|||
|
|
try:
|
|||
|
|
obj = vde_bridge.bridge_result_to_blender(result, name=name)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
self.report({'INFO'}, f"导入成功: {name} "
|
|||
|
|
f"({result.get('num_vertices', 0)} 顶点, "
|
|||
|
|
f"{result.get('num_faces', 0)} 面)")
|
|||
|
|
return {'FINISHED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
class VDE_OT_export_step(bpy.types.Operator, ExportHelper):
|
|||
|
|
"""将选中对象导出为 STEP 文件。"""
|
|||
|
|
bl_idname = "vde.export_step"
|
|||
|
|
bl_label = "导出 STEP"
|
|||
|
|
bl_description = "使用 VDE 引擎将选中网格导出为 STEP AP242 格式"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
filename_ext = ".stp"
|
|||
|
|
filter_glob: StringProperty(
|
|||
|
|
default="*.stp;*.step",
|
|||
|
|
options={'HIDDEN'},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
include_pmi: BoolProperty(
|
|||
|
|
name="包含 PMI",
|
|||
|
|
description="在 STEP 文件中包含产品和制造信息",
|
|||
|
|
default=False,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
bridge = get_bridge()
|
|||
|
|
|
|||
|
|
selected = [o for o in context.selected_objects if o.type == 'MESH']
|
|||
|
|
if not selected:
|
|||
|
|
return report_and_raise(self, "请先选中至少一个 Mesh 对象。")
|
|||
|
|
|
|||
|
|
meshes = []
|
|||
|
|
for obj in selected:
|
|||
|
|
try:
|
|||
|
|
mesh_data = vde_bridge.blender_mesh_to_bridge_data(obj)
|
|||
|
|
meshes.append(mesh_data)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, f"对象 '{obj.name}': {e}")
|
|||
|
|
|
|||
|
|
self.report({'INFO'}, f"正在导出 {len(meshes)} 个对象到: {self.filepath}")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
result = bridge.execute(
|
|||
|
|
"export_step",
|
|||
|
|
path=self.filepath,
|
|||
|
|
meshes=meshes,
|
|||
|
|
)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
self.report({'INFO'}, f"导出成功: {self.filepath}")
|
|||
|
|
return {'FINISHED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
# 图元创建操作符
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
class VDE_OT_create_box(bpy.types.Operator):
|
|||
|
|
"""创建 Box B-Rep 图元。"""
|
|||
|
|
bl_idname = "vde.create_box"
|
|||
|
|
bl_label = "Box"
|
|||
|
|
bl_description = "使用 VDE 创建 Box 图元"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
width: FloatProperty(
|
|||
|
|
name="宽度 (X)",
|
|||
|
|
default=2.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=100.0,
|
|||
|
|
)
|
|||
|
|
height: FloatProperty(
|
|||
|
|
name="高度 (Y)",
|
|||
|
|
default=2.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=100.0,
|
|||
|
|
)
|
|||
|
|
depth: FloatProperty(
|
|||
|
|
name="深度 (Z)",
|
|||
|
|
default=2.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=100.0,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
bridge = get_bridge()
|
|||
|
|
try:
|
|||
|
|
result = bridge.execute("create_primitive", type="box", params={
|
|||
|
|
"width": self.width,
|
|||
|
|
"height": self.height,
|
|||
|
|
"depth": self.depth,
|
|||
|
|
})
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
vde_bridge.bridge_result_to_blender(
|
|||
|
|
result,
|
|||
|
|
name=f"VDE_Box_{self.width:.1f}x{self.height:.1f}x{self.depth:.1f}"
|
|||
|
|
)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
self.report({'INFO'}, f"创建 Box ({self.width}×{self.height}×{self.depth})")
|
|||
|
|
return {'FINISHED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
class VDE_OT_create_cylinder(bpy.types.Operator):
|
|||
|
|
"""创建 Cylinder B-Rep 图元。"""
|
|||
|
|
bl_idname = "vde.create_cylinder"
|
|||
|
|
bl_label = "Cylinder"
|
|||
|
|
bl_description = "使用 VDE 创建圆柱体图元"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
radius: FloatProperty(
|
|||
|
|
name="半径",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
height: FloatProperty(
|
|||
|
|
name="高度",
|
|||
|
|
default=2.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=100.0,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
bridge = get_bridge()
|
|||
|
|
try:
|
|||
|
|
result = bridge.execute("create_primitive", type="cylinder", params={
|
|||
|
|
"radius": self.radius,
|
|||
|
|
"height": self.height,
|
|||
|
|
})
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
vde_bridge.bridge_result_to_blender(
|
|||
|
|
result,
|
|||
|
|
name=f"VDE_Cylinder_r{self.radius:.1f}_h{self.height:.1f}"
|
|||
|
|
)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
self.report({'INFO'}, f"创建 Cylinder (r={self.radius}, h={self.height})")
|
|||
|
|
return {'FINISHED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
class VDE_OT_create_sphere(bpy.types.Operator):
|
|||
|
|
"""创建 Sphere B-Rep 图元。"""
|
|||
|
|
bl_idname = "vde.create_sphere"
|
|||
|
|
bl_label = "Sphere"
|
|||
|
|
bl_description = "使用 VDE 创建球体图元"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
radius: FloatProperty(
|
|||
|
|
name="半径",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
bridge = get_bridge()
|
|||
|
|
try:
|
|||
|
|
result = bridge.execute("create_primitive", type="sphere", params={
|
|||
|
|
"radius": self.radius,
|
|||
|
|
})
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
vde_bridge.bridge_result_to_blender(
|
|||
|
|
result,
|
|||
|
|
name=f"VDE_Sphere_r{self.radius:.1f}"
|
|||
|
|
)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
self.report({'INFO'}, f"创建 Sphere (r={self.radius})")
|
|||
|
|
return {'FINISHED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
# 布尔运算操作符
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
class VDE_OT_boolean_union(bpy.types.Operator):
|
|||
|
|
"""布尔并集 — 合并两个选中对象。"""
|
|||
|
|
bl_idname = "vde.boolean_union"
|
|||
|
|
bl_label = "布尔并集 (Union)"
|
|||
|
|
bl_description = "使用 VDE 引擎计算两个网格的布尔并集"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
return _execute_boolean(self, context, "union", "Union")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class VDE_OT_boolean_intersect(bpy.types.Operator):
|
|||
|
|
"""布尔交集 — 保留两对象重叠部分。"""
|
|||
|
|
bl_idname = "vde.boolean_intersect"
|
|||
|
|
bl_label = "布尔交集 (Intersect)"
|
|||
|
|
bl_description = "使用 VDE 引擎计算两个网格的布尔交集"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
return _execute_boolean(self, context, "intersect", "Intersect")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class VDE_OT_boolean_diff(bpy.types.Operator):
|
|||
|
|
"""布尔差集 — 从第一个对象减去第二个对象。"""
|
|||
|
|
bl_idname = "vde.boolean_diff"
|
|||
|
|
bl_label = "布尔差集 (Difference)"
|
|||
|
|
bl_description = "使用 VDE 引擎计算两个网格的布尔差集 (A - B)"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
return _execute_boolean(self, context, "diff", "Diff")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _execute_boolean(operator, context, op_type, label):
|
|||
|
|
"""内部布尔运算执行函数。"""
|
|||
|
|
selected = [o for o in context.selected_objects if o.type == 'MESH']
|
|||
|
|
if len(selected) < 2:
|
|||
|
|
return report_and_raise(operator, "请选中恰好两个 Mesh 对象。")
|
|||
|
|
|
|||
|
|
obj_a = context.active_object
|
|||
|
|
obj_b = selected[0] if selected[0] != obj_a else selected[1]
|
|||
|
|
|
|||
|
|
if obj_a is None or obj_b is None:
|
|||
|
|
return report_and_raise(operator, "请设置活动对象并选中另一个对象。")
|
|||
|
|
|
|||
|
|
operator.report(
|
|||
|
|
{'INFO'},
|
|||
|
|
f"正在计算布尔{label}: {obj_a.name} vs {obj_b.name}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
bridge = get_bridge()
|
|||
|
|
try:
|
|||
|
|
mesh_a = vde_bridge.blender_mesh_to_bridge_data(obj_a)
|
|||
|
|
mesh_b = vde_bridge.blender_mesh_to_bridge_data(obj_b)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(operator, str(e))
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
result = bridge.execute(
|
|||
|
|
"boolean",
|
|||
|
|
boolean_type=op_type,
|
|||
|
|
mesh_a=mesh_a,
|
|||
|
|
mesh_b=mesh_b,
|
|||
|
|
)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(operator, str(e))
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
vde_bridge.bridge_result_to_blender(
|
|||
|
|
result,
|
|||
|
|
name=f"VDE_Boolean_{label}_{obj_a.name}"
|
|||
|
|
)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(operator, str(e))
|
|||
|
|
|
|||
|
|
operator.report(
|
|||
|
|
{'INFO'},
|
|||
|
|
f"布尔{label}完成: "
|
|||
|
|
f"{result.get('num_vertices', 0)} 顶点, "
|
|||
|
|
f"{result.get('num_faces', 0)} 面"
|
|||
|
|
)
|
|||
|
|
return {'FINISHED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
# SDF → Mesh 操作符
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
class VDE_OT_sdf_to_mesh(bpy.types.Operator):
|
|||
|
|
"""将 SDF 隐式曲面转换为三角网格。"""
|
|||
|
|
bl_idname = "vde.sdf_to_mesh"
|
|||
|
|
bl_label = "SDF → Mesh"
|
|||
|
|
bl_description = "使用 VDE 引擎将 SDF 隐式几何体转为三角网格"
|
|||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|||
|
|
|
|||
|
|
sdf_type: EnumProperty(
|
|||
|
|
name="SDF 类型",
|
|||
|
|
description="要生成的隐式曲面类型",
|
|||
|
|
items=[
|
|||
|
|
('sphere', "球体", "球体 SDF"),
|
|||
|
|
('box', "Box", "Box SDF"),
|
|||
|
|
('cylinder', "圆柱体", "圆柱体 SDF"),
|
|||
|
|
('torus', "环面", "环面 SDF"),
|
|||
|
|
('custom', "自定义 CSG", "自定义 SDF CSG 组合树"),
|
|||
|
|
],
|
|||
|
|
default='sphere',
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
resolution: IntProperty(
|
|||
|
|
name="分辨率",
|
|||
|
|
description="Marching Cubes 网格分辨率 (越高越精细)",
|
|||
|
|
default=64,
|
|||
|
|
min=16,
|
|||
|
|
max=512,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ══ 球体参数 ══
|
|||
|
|
sphere_radius: FloatProperty(
|
|||
|
|
name="半径",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ══ Box 参数 ══
|
|||
|
|
box_hx: FloatProperty(
|
|||
|
|
name="半长 (X)",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
box_hy: FloatProperty(
|
|||
|
|
name="半长 (Y)",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
box_hz: FloatProperty(
|
|||
|
|
name="半长 (Z)",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ══ 圆柱体参数 ══
|
|||
|
|
cyl_radius: FloatProperty(
|
|||
|
|
name="半径",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
cyl_height: FloatProperty(
|
|||
|
|
name="高度",
|
|||
|
|
default=2.0,
|
|||
|
|
min=0.01,
|
|||
|
|
max=100.0,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ══ 环面参数 ══
|
|||
|
|
torus_major: FloatProperty(
|
|||
|
|
name="主半径",
|
|||
|
|
default=1.0,
|
|||
|
|
min=0.1,
|
|||
|
|
max=50.0,
|
|||
|
|
)
|
|||
|
|
torus_minor: FloatProperty(
|
|||
|
|
name="管半径",
|
|||
|
|
default=0.3,
|
|||
|
|
min=0.01,
|
|||
|
|
max=25.0,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def draw(self, context):
|
|||
|
|
layout = self.layout
|
|||
|
|
layout.prop(self, "sdf_type")
|
|||
|
|
layout.prop(self, "resolution")
|
|||
|
|
|
|||
|
|
box = layout.box()
|
|||
|
|
if self.sdf_type == 'sphere':
|
|||
|
|
box.prop(self, "sphere_radius")
|
|||
|
|
elif self.sdf_type == 'box':
|
|||
|
|
box.prop(self, "box_hx")
|
|||
|
|
box.prop(self, "box_hy")
|
|||
|
|
box.prop(self, "box_hz")
|
|||
|
|
elif self.sdf_type == 'cylinder':
|
|||
|
|
box.prop(self, "cyl_radius")
|
|||
|
|
box.prop(self, "cyl_height")
|
|||
|
|
elif self.sdf_type == 'torus':
|
|||
|
|
box.prop(self, "torus_major")
|
|||
|
|
box.prop(self, "torus_minor")
|
|||
|
|
elif self.sdf_type == 'custom':
|
|||
|
|
box.label(text="自定义 CSG 通过面板配置")
|
|||
|
|
|
|||
|
|
def execute(self, context):
|
|||
|
|
params = {}
|
|||
|
|
node_name = "SDF"
|
|||
|
|
|
|||
|
|
if self.sdf_type == 'sphere':
|
|||
|
|
params = {"radius": self.sphere_radius}
|
|||
|
|
node_name = f"VDE_SDF_Sphere_r{self.sphere_radius:.1f}"
|
|||
|
|
elif self.sdf_type == 'box':
|
|||
|
|
params = {"half_extents": [self.box_hx, self.box_hy, self.box_hz]}
|
|||
|
|
node_name = (f"VDE_SDF_Box_"
|
|||
|
|
f"{self.box_hx:.1f}x{self.box_hy:.1f}x{self.box_hz:.1f}")
|
|||
|
|
elif self.sdf_type == 'cylinder':
|
|||
|
|
params = {"radius": self.cyl_radius, "height": self.cyl_height}
|
|||
|
|
node_name = f"VDE_SDF_Cyl_r{self.cyl_radius:.1f}_h{self.cyl_height:.1f}"
|
|||
|
|
elif self.sdf_type == 'torus':
|
|||
|
|
params = {"major_radius": self.torus_major,
|
|||
|
|
"minor_radius": self.torus_minor}
|
|||
|
|
node_name = (f"VDE_SDF_Torus_"
|
|||
|
|
f"R{self.torus_major:.1f}_r{self.torus_minor:.1f}")
|
|||
|
|
elif self.sdf_type == 'custom':
|
|||
|
|
# 从场景属性读取自定义 CSG 配置
|
|||
|
|
scene = context.scene
|
|||
|
|
params = {
|
|||
|
|
"children": list(scene.vde_sdf_children),
|
|||
|
|
"operations": list(scene.vde_sdf_operations),
|
|||
|
|
}
|
|||
|
|
if scene.vde_sdf_blend_radius > 0:
|
|||
|
|
params["blend_radius"] = scene.vde_sdf_blend_radius
|
|||
|
|
if scene.vde_sdf_round > 0:
|
|||
|
|
params["round"] = scene.vde_sdf_round
|
|||
|
|
node_name = "VDE_SDF_CSG"
|
|||
|
|
|
|||
|
|
bridge = get_bridge()
|
|||
|
|
self.report(
|
|||
|
|
{'INFO'},
|
|||
|
|
f"SDF→Mesh: {self.sdf_type}, res={self.resolution}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
result = bridge.execute(
|
|||
|
|
"sdf_to_mesh",
|
|||
|
|
sdf_type=self.sdf_type,
|
|||
|
|
params=params,
|
|||
|
|
resolution=self.resolution,
|
|||
|
|
)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
vde_bridge.bridge_result_to_blender(result, name=node_name)
|
|||
|
|
except RuntimeError as e:
|
|||
|
|
return report_and_raise(self, str(e))
|
|||
|
|
|
|||
|
|
self.report(
|
|||
|
|
{'INFO'},
|
|||
|
|
f"SDF→Mesh 完成: "
|
|||
|
|
f"{result.get('num_vertices', 0)} 顶点, "
|
|||
|
|
f"{result.get('num_faces', 0)} 面"
|
|||
|
|
)
|
|||
|
|
return {'FINISHED'}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
# 注册/注销
|
|||
|
|
# ═══════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
CLASSES = [
|
|||
|
|
VDE_OT_import_step,
|
|||
|
|
VDE_OT_export_step,
|
|||
|
|
VDE_OT_create_box,
|
|||
|
|
VDE_OT_create_cylinder,
|
|||
|
|
VDE_OT_create_sphere,
|
|||
|
|
VDE_OT_boolean_union,
|
|||
|
|
VDE_OT_boolean_intersect,
|
|||
|
|
VDE_OT_boolean_diff,
|
|||
|
|
VDE_OT_sdf_to_mesh,
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def register():
|
|||
|
|
for cls in CLASSES:
|
|||
|
|
bpy.utils.register_class(cls)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def unregister():
|
|||
|
|
for cls in reversed(CLASSES):
|
|||
|
|
bpy.utils.unregister_class(cls)
|