fix(export_gltf): clear OCC custom_normal attribute before sharp edge processing
The geometry GLB from export_step_to_gltf.py contains a 'custom_normal' attribute (CORNER, INT16_2D) from OCC tessellation. If left in place, Blender's glTF exporter re-exports these pre-baked normals unchanged — ignoring shade_smooth_by_angle processing and our explicit sharp edge marks. Fix: remove the 'custom_normal' attribute from all imported mesh objects immediately after GLB import, before applying smooth shading. Also add orphans_purge() before export to remove palette materials (mat_0/1/2/3) that become users=0 after library material substitution. Same custom_normal clearing applied to blender_render.py for thumbnail renders. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -342,6 +342,16 @@ def _import_glb(glb_file):
|
|||||||
print(f"[blender_render] imported {len(parts)} part(s) from GLB: "
|
print(f"[blender_render] imported {len(parts)} part(s) from GLB: "
|
||||||
f"{[p.name for p in parts[:5]]}")
|
f"{[p.name for p in parts[:5]]}")
|
||||||
|
|
||||||
|
# Remove OCC-baked custom normals so shade_smooth_by_angle can recompute
|
||||||
|
# normals from scratch (respecting our sharp edge marks).
|
||||||
|
cleared = 0
|
||||||
|
for p in parts:
|
||||||
|
if "custom_normal" in p.data.attributes:
|
||||||
|
p.data.attributes.remove(p.data.attributes["custom_normal"])
|
||||||
|
cleared += 1
|
||||||
|
if cleared:
|
||||||
|
print(f"[blender_render] cleared OCC custom_normal from {cleared} mesh objects")
|
||||||
|
|
||||||
# Centre combined bbox at world origin
|
# Centre combined bbox at world origin
|
||||||
all_corners = []
|
all_corners = []
|
||||||
for p in parts:
|
for p in parts:
|
||||||
|
|||||||
@@ -116,6 +116,20 @@ def main() -> None:
|
|||||||
mesh_objects = [o for o in bpy.data.objects if o.type == "MESH"]
|
mesh_objects = [o for o in bpy.data.objects if o.type == "MESH"]
|
||||||
print(f"Imported geometry GLB: {args.glb_path} ({len(mesh_objects)} mesh objects)")
|
print(f"Imported geometry GLB: {args.glb_path} ({len(mesh_objects)} mesh objects)")
|
||||||
|
|
||||||
|
# Remove OCC-baked custom normals from the geometry GLB.
|
||||||
|
# RWGltf_CafWriter embeds per-corner normals from OCC tessellation as a
|
||||||
|
# 'custom_normal' attribute (CORNER, INT16_2D). If left in place, Blender's
|
||||||
|
# glTF exporter re-exports these pre-baked normals unchanged, ignoring our
|
||||||
|
# shade_smooth_by_angle processing and sharp edge marks entirely.
|
||||||
|
# Removing this attribute forces Blender to recompute normals from scratch.
|
||||||
|
cleared_normals = 0
|
||||||
|
for obj in mesh_objects:
|
||||||
|
if "custom_normal" in obj.data.attributes:
|
||||||
|
obj.data.attributes.remove(obj.data.attributes["custom_normal"])
|
||||||
|
cleared_normals += 1
|
||||||
|
if cleared_normals:
|
||||||
|
print(f"Cleared OCC custom_normal attribute from {cleared_normals} mesh objects")
|
||||||
|
|
||||||
# Apply smooth shading using the configured angle threshold
|
# Apply smooth shading using the configured angle threshold
|
||||||
smooth_rad = _math.radians(args.smooth_angle)
|
smooth_rad = _math.radians(args.smooth_angle)
|
||||||
print(f"Applying smooth shading at {args.smooth_angle}° ({smooth_rad:.3f} rad)")
|
print(f"Applying smooth shading at {args.smooth_angle}° ({smooth_rad:.3f} rad)")
|
||||||
@@ -235,6 +249,14 @@ def main() -> None:
|
|||||||
if fallback:
|
if fallback:
|
||||||
print(f"Single-material fallback: applied '{default_mat_name}' to {fallback} unmatched objects")
|
print(f"Single-material fallback: applied '{default_mat_name}' to {fallback} 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).
|
||||||
|
# This prevents stale materials from appearing as duplicates in the export.
|
||||||
|
try:
|
||||||
|
bpy.ops.outliner.orphans_purge(do_recursive=True)
|
||||||
|
except Exception:
|
||||||
|
pass # non-critical; export proceeds regardless
|
||||||
|
|
||||||
# Export production GLB with full PBR material data
|
# Export production GLB with full PBR material data
|
||||||
try:
|
try:
|
||||||
bpy.ops.export_scene.gltf(
|
bpy.ops.export_scene.gltf(
|
||||||
|
|||||||
Reference in New Issue
Block a user