fix(materials): universal FailedMaterial sentinel for unmatched mesh objects
- export_gltf.py: replace single-material fallback (only fired when len(appended)==1) with a universal sentinel that appends SCHAEFFLER_059999_FailedMaterial unconditionally and assigns it to every mesh object not matched by name-based lookup. Also adds in-memory magenta fallback if library append fails. Removes 2 temporary [DEBUG] print lines from investigation. - blender_render.py: add FailedMaterial assignment inside _apply_material_library() for unmatched parts (was log-only before). Includes copy-on-write guard (users > 1) matching existing pattern. Also added alias 'Stahl; Durotect CMT' (semicolon) → Durotect-Blue to cover STEP files using semicolon separator instead of comma. Verified: 23/25 objects matched correctly, 2 ISO8734 dowel pins (empty material) receive SCHAEFFLER_059999_FailedMaterial as sentinel. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,8 @@ import json
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
FAILED_MATERIAL_NAME = "SCHAEFFLER_059999_FailedMaterial"
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
argv = sys.argv
|
||||
@@ -268,26 +270,49 @@ def main() -> None:
|
||||
assigned += 1
|
||||
assigned_names.add(obj.name)
|
||||
else:
|
||||
pass # name-matching miss — may be covered by single-material fallback below
|
||||
pass # unmatched → will receive FailedMaterial sentinel below
|
||||
print(f"Material substitution: {assigned}/{len(mesh_objects)} mesh objects assigned")
|
||||
|
||||
# Single-material fallback: if only one library material was loaded,
|
||||
# apply it to every object that name-matching missed.
|
||||
# (mat_map_lower may contain unresolvable pass-through values like
|
||||
# "Stahl; Durotect CMT", so checking appended is more reliable.)
|
||||
if len(appended) == 1:
|
||||
default_mat_name, default_mat = next(iter(appended.items()))
|
||||
if default_mat:
|
||||
fallback = 0
|
||||
for obj in mesh_objects:
|
||||
if obj.name not in assigned_names:
|
||||
if obj.data.users > 1:
|
||||
obj.data = obj.data.copy()
|
||||
obj.data.materials.clear()
|
||||
obj.data.materials.append(default_mat)
|
||||
fallback += 1
|
||||
if fallback:
|
||||
print(f"Single-material fallback: applied '{default_mat_name}' to {fallback} unmatched objects")
|
||||
# Universal FailedMaterial sentinel: assign SCHAEFFLER_059999_FailedMaterial
|
||||
# to every mesh object that was not matched by name-based lookup above.
|
||||
# Replaces the old single-material fallback that only fired when len(appended)==1.
|
||||
failed_mat = None
|
||||
try:
|
||||
bpy.ops.wm.append(
|
||||
filepath=f"{args.asset_library_blend}/Material/{FAILED_MATERIAL_NAME}",
|
||||
directory=f"{args.asset_library_blend}/Material/",
|
||||
filename=FAILED_MATERIAL_NAME,
|
||||
link=False,
|
||||
)
|
||||
if FAILED_MATERIAL_NAME in bpy.data.materials:
|
||||
failed_mat = bpy.data.materials[FAILED_MATERIAL_NAME]
|
||||
print(f"Appended sentinel material: {FAILED_MATERIAL_NAME}")
|
||||
else:
|
||||
print(f"WARNING: sentinel '{FAILED_MATERIAL_NAME}' not found in library — "
|
||||
f"creating in-memory magenta fallback", file=sys.stderr)
|
||||
except Exception as exc:
|
||||
print(f"WARNING: failed to append sentinel '{FAILED_MATERIAL_NAME}': {exc}",
|
||||
file=sys.stderr)
|
||||
|
||||
if failed_mat is None:
|
||||
# Library append failed: create in-memory magenta so export is never silently wrong
|
||||
failed_mat = bpy.data.materials.new(name=FAILED_MATERIAL_NAME)
|
||||
failed_mat.use_nodes = True
|
||||
bsdf = failed_mat.node_tree.nodes.get("Principled BSDF")
|
||||
if bsdf:
|
||||
bsdf.inputs["Base Color"].default_value = (1.0, 0.0, 1.0, 1.0) # magenta
|
||||
|
||||
fallback_count = 0
|
||||
for obj in mesh_objects:
|
||||
if obj.name not in assigned_names:
|
||||
if obj.data.users > 1:
|
||||
obj.data = obj.data.copy()
|
||||
obj.data.materials.clear()
|
||||
obj.data.materials.append(failed_mat)
|
||||
fallback_count += 1
|
||||
if fallback_count:
|
||||
print(f"FailedMaterial sentinel: assigned '{FAILED_MATERIAL_NAME}' "
|
||||
f"to {fallback_count} unmatched objects")
|
||||
|
||||
# Purge orphan data-blocks (palette materials mat_0/mat_1/... from the geometry
|
||||
# GLB that now have users=0 after library material substitution).
|
||||
|
||||
Reference in New Issue
Block a user