"""Blender headless script: export a STEP-derived scene as a production GLB. Usage: blender --background --python export_gltf.py -- \\ --stl_path /path/to/file.stl \\ --output_path /path/to/output.glb \\ [--asset_library_blend /path/to/library.blend] \\ [--material_map '{"SrcMat": "LibMat"}'] The script: 1. Imports the STL file (with mm→m scale). 2. Optionally applies asset library materials from a .blend. 3. Exports as GLB (Draco-compressed if available, otherwise standard). """ from __future__ import annotations import argparse import json import sys import traceback def parse_args() -> argparse.Namespace: argv = sys.argv if "--" not in argv: print("No arguments after --", file=sys.stderr) sys.exit(1) rest = argv[argv.index("--") + 1:] parser = argparse.ArgumentParser() parser.add_argument("--stl_path", required=True) 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] for obj in bpy.data.objects: if obj.type != "MESH": continue mesh = obj.data mesh.use_auto_smooth = True 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] # Clean scene bpy.ops.wm.read_factory_settings(use_empty=True) # Import STL bpy.ops.import_mesh.stl(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) # 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 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) # Export GLB try: bpy.ops.export_scene.gltf( filepath=args.output_path, export_format="GLB", export_apply=True, use_selection=False, ) except Exception as exc: print(f"GLB export failed: {exc}", file=sys.stderr) sys.exit(1) print(f"GLB exported to {args.output_path}") try: main() except SystemExit: raise except Exception: traceback.print_exc() sys.exit(1)