refactor: replace STL intermediary with OCC-native STEP→GLB pipeline
- export_step_to_gltf.py: STEP→GLB via RWGltf_CafWriter + BRepBuilderAPI_Transform (mm→m pre-scaling, XCAFDoc_ShapeTool.GetComponents_s static method) - Blender scripts (blender_render.py, still_render.py, turntable_render.py, export_gltf.py, export_blend.py): import GLB instead of STL, remove _scale_mm_to_m - step_tasks.py: add generate_gltf_production_task, remove generate_stl_cache, replace _bbox_from_stl with _bbox_from_glb (trimesh), auto-queue geometry GLB after thumbnail render - render_blender.py: replace _stl_from_cache_or_convert with _glb_from_step, remove convert_step_to_stl and export_per_part_stls - domains/rendering/tasks.py: update render_turntable_task, export_gltf/blend tasks to use GLB instead of STL - cad.py: remove STL download/generate endpoints, add generate-gltf-production - admin.py: generate-missing-stls → generate-missing-geometry-glbs - Frontend: replace STL cache UI with GLB generate buttons, remove stl_cached field Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,78 +27,30 @@ def parse_args() -> argparse.Namespace:
|
||||
sys.exit(1)
|
||||
rest = argv[argv.index("--") + 1:]
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--stl_path", required=True)
|
||||
parser.add_argument("--glb_path", required=True,
|
||||
help="Geometry GLB from export_step_to_gltf.py (already in metres)")
|
||||
parser.add_argument("--output_path", required=True)
|
||||
parser.add_argument("--asset_library_blend", default=None)
|
||||
parser.add_argument("--material_map", default="{}")
|
||||
parser.add_argument("--sharp_edges_json", default="[]",
|
||||
help="JSON array of [x, y, z] midpoints (mm) to mark as sharp edges")
|
||||
return parser.parse_args(rest)
|
||||
|
||||
|
||||
def mark_sharp_edges_by_proximity(midpoints_mm: list, threshold_mm: float = 1.0) -> None:
|
||||
"""Mark Blender mesh edges as sharp based on proximity to OCC-derived midpoints.
|
||||
|
||||
midpoints_mm: list of [x, y, z] in mm (from OCC coordinate space).
|
||||
After STL import + scale-apply (mm→m), Blender vertices are in meters, so we
|
||||
convert the edge midpoint back to mm before comparing.
|
||||
threshold_mm: snap distance in mm (default 1.0 mm).
|
||||
"""
|
||||
if not midpoints_mm:
|
||||
return
|
||||
|
||||
import bpy # type: ignore[import]
|
||||
import math
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type != "MESH":
|
||||
continue
|
||||
mesh = obj.data
|
||||
# Blender 4.1+ removed use_auto_smooth — use shade_smooth_by_angle instead
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
try:
|
||||
bpy.ops.object.shade_smooth_by_angle(angle=math.radians(30))
|
||||
except Exception:
|
||||
pass # fallback: stay flat-shaded
|
||||
mw = obj.matrix_world
|
||||
for edge in mesh.edges:
|
||||
v1 = mw @ mesh.vertices[edge.vertices[0]].co
|
||||
v2 = mw @ mesh.vertices[edge.vertices[1]].co
|
||||
# Convert Blender meters → mm for comparison
|
||||
mid_mm = [
|
||||
(v1.x + v2.x) / 2 * 1000,
|
||||
(v1.y + v2.y) / 2 * 1000,
|
||||
(v1.z + v2.z) / 2 * 1000,
|
||||
]
|
||||
for hint in midpoints_mm:
|
||||
dist_sq = sum((a - b) ** 2 for a, b in zip(mid_mm, hint))
|
||||
if dist_sq < threshold_mm ** 2:
|
||||
edge.use_edge_sharp = True
|
||||
break
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
material_map: dict = json.loads(args.material_map)
|
||||
sharp_edge_midpoints: list = json.loads(args.sharp_edges_json)
|
||||
|
||||
import bpy # type: ignore[import]
|
||||
import math as _math
|
||||
|
||||
# Clean scene
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
# Import STL (bpy.ops.wm.stl_import is the Blender 4.0+ API)
|
||||
bpy.ops.wm.stl_import(filepath=args.stl_path)
|
||||
|
||||
# Scale mm → m
|
||||
for obj in bpy.context.selected_objects:
|
||||
obj.scale = (0.001, 0.001, 0.001)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.transform_apply(scale=True)
|
||||
# Import geometry GLB from export_step_to_gltf.py (already in metres, Y-up)
|
||||
bpy.ops.import_scene.gltf(filepath=args.glb_path)
|
||||
print(f"Imported geometry GLB: {args.glb_path} "
|
||||
f"({len([o for o in bpy.data.objects if o.type == 'MESH'])} mesh objects)")
|
||||
|
||||
# Apply smooth shading with 30° angle threshold (Blender 4.1+ API)
|
||||
import math as _math
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "MESH":
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
@@ -108,37 +60,30 @@ def main() -> None:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Mark sharp edges for better UV seams
|
||||
if sharp_edge_midpoints:
|
||||
mark_sharp_edges_by_proximity(sharp_edge_midpoints)
|
||||
print(f"Marked sharp edges from {len(sharp_edge_midpoints)} hint points")
|
||||
|
||||
# Apply asset library materials if provided.
|
||||
# link=False (append) is required for GLB export: the GLTF exporter can only
|
||||
# traverse local (appended) Principled BSDF node trees to extract PBR values.
|
||||
# Linked materials are external references whose node data is not accessible.
|
||||
# link=False (append) is required: the GLTF exporter can only traverse
|
||||
# local (appended) Principled BSDF node trees to extract PBR values.
|
||||
if args.asset_library_blend and material_map:
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from asset_library import apply_asset_library_materials
|
||||
apply_asset_library_materials(args.asset_library_blend, material_map, link=False)
|
||||
|
||||
# Export GLB with full PBR material data
|
||||
# Note: export_colors was removed in Blender 4.x — do not pass it.
|
||||
# Export production GLB with full PBR material data
|
||||
try:
|
||||
bpy.ops.export_scene.gltf(
|
||||
filepath=args.output_path,
|
||||
export_format="GLB",
|
||||
export_apply=True,
|
||||
use_selection=False,
|
||||
export_materials="EXPORT", # export all materials (Principled BSDF → glTF PBR)
|
||||
export_image_format="AUTO", # embed textures (base color, normal, roughness maps)
|
||||
export_materials="EXPORT",
|
||||
export_image_format="AUTO",
|
||||
)
|
||||
except Exception as exc:
|
||||
print(f"GLB export failed: {exc}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"GLB exported to {args.output_path}")
|
||||
print(f"Production GLB exported to {args.output_path}")
|
||||
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user