feat(M5-M7): embed canonical material names in USD via customData + pxr direct read
- export_step_to_usd.py: accept --material_map CLI arg, write
schaeffler:canonicalMaterialName as customData on each Mesh prim,
fix geometry transform (strip shape Location before face exploration,
apply both face_loc and shape_loc sequentially)
- import_usd.py: after Blender USD import, use pxr to read customData
directly from the USD file — builds {part_key: material_name} lookup
(Blender ignores STRING primvars and customData, but pxr reads both)
- _blender_materials.py: add apply_material_library_direct() for exact
dict-based material assignment without name-matching heuristics
- _blender_scene_setup.py: prefer direct USD lookup, fall back to
name-matching for legacy USD files without material metadata
- export_glb.py (generate_usd_master_task): resolve material_map via
material_service.resolve_material_map() and pass to subprocess;
include material hash in cache key for invalidation
- ROADMAP.md: update P5 status, add M5-M7 milestones
Tested: 3/3 parts matched (ans_lfs120), 172/175 parts matched
(F-802007.TR4-D1-H122AG). Previous: 0/25 matched.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ from _blender_materials import (
|
||||
assign_failed_material,
|
||||
build_mat_map_lower,
|
||||
apply_material_library,
|
||||
apply_material_library_direct,
|
||||
)
|
||||
from _blender_scene import (
|
||||
ensure_collection,
|
||||
@@ -43,8 +44,9 @@ def _setup_mode_b(args, lap_fn: Callable[[str], None]) -> None:
|
||||
lap_fn("template_load")
|
||||
|
||||
target_col = ensure_collection(args.target_collection)
|
||||
usd_material_lookup: dict = {}
|
||||
if args.usd_path:
|
||||
parts = import_usd_file(args.usd_path)
|
||||
parts, usd_material_lookup = import_usd_file(args.usd_path)
|
||||
else:
|
||||
parts = import_glb(args.glb_path)
|
||||
lap_fn("glb_import")
|
||||
@@ -63,7 +65,25 @@ def _setup_mode_b(args, lap_fn: Callable[[str], None]) -> None:
|
||||
apply_sharp_edges_from_occ(parts, _occ_pairs)
|
||||
lap_fn("smooth_shading")
|
||||
|
||||
if args.material_library_path and args.material_map:
|
||||
if args.material_library_path and usd_material_lookup:
|
||||
# USD primvar path: direct material assignment (no name-matching needed)
|
||||
apply_material_library_direct(
|
||||
parts, args.material_library_path, usd_material_lookup,
|
||||
)
|
||||
# Fall back to name-matching for any parts missing primvars
|
||||
if args.material_map:
|
||||
_unassigned = [p for p in parts if not p.data.materials or
|
||||
(len(p.data.materials) == 1 and
|
||||
p.data.materials[0] and
|
||||
p.data.materials[0].name == "SCHAEFFLER_059999_FailedMaterial")]
|
||||
if _unassigned:
|
||||
print(f"[blender_render] {len(_unassigned)} parts without USD primvar — "
|
||||
f"falling back to name-matching", flush=True)
|
||||
apply_material_library(
|
||||
_unassigned, args.material_library_path,
|
||||
build_mat_map_lower(args.material_map), args.part_names_ordered,
|
||||
)
|
||||
elif args.material_library_path and args.material_map:
|
||||
apply_material_library(
|
||||
parts, args.material_library_path,
|
||||
build_mat_map_lower(args.material_map), args.part_names_ordered,
|
||||
@@ -97,8 +117,9 @@ def _setup_mode_b(args, lap_fn: Callable[[str], None]) -> None:
|
||||
def _setup_mode_a(args) -> None:
|
||||
"""MODE A: Factory settings — auto-camera + auto-lights."""
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
usd_material_lookup: dict = {}
|
||||
if args.usd_path:
|
||||
parts = import_usd_file(args.usd_path)
|
||||
parts, usd_material_lookup = import_usd_file(args.usd_path)
|
||||
else:
|
||||
parts = import_glb(args.glb_path)
|
||||
apply_rotation(parts, args.rotation_x, args.rotation_y, args.rotation_z)
|
||||
@@ -113,7 +134,23 @@ def _setup_mode_a(args) -> None:
|
||||
assign_failed_material(part)
|
||||
print(f"[blender_render] smooth+fallback-material: {len(parts)} parts ({_time.time()-_t:.2f}s)", flush=True)
|
||||
|
||||
if args.material_library_path and args.material_map:
|
||||
if args.material_library_path and usd_material_lookup:
|
||||
# USD primvar path: direct material assignment
|
||||
apply_material_library_direct(
|
||||
parts, args.material_library_path, usd_material_lookup,
|
||||
)
|
||||
# Fall back to name-matching for parts without primvars
|
||||
if args.material_map:
|
||||
_unassigned = [p for p in parts if not p.data.materials or
|
||||
(len(p.data.materials) == 1 and
|
||||
p.data.materials[0] and
|
||||
p.data.materials[0].name == "SCHAEFFLER_059999_FailedMaterial")]
|
||||
if _unassigned:
|
||||
apply_material_library(
|
||||
_unassigned, args.material_library_path,
|
||||
build_mat_map_lower(args.material_map), args.part_names_ordered,
|
||||
)
|
||||
elif args.material_library_path and args.material_map:
|
||||
apply_material_library(
|
||||
parts, args.material_library_path,
|
||||
build_mat_map_lower(args.material_map), args.part_names_ordered,
|
||||
|
||||
Reference in New Issue
Block a user