fix(export_gltf): use edit-mode mark_sharp+mark_seam for proper GLB sharp edges
Replace shade_smooth_by_angle with explicit edit-mode operators: - edges_select_sharp(angle) → mark_sharp() + mark_seam() - Produces vertex splits at sharp edges (6027 split positions verified) - Remove OCC custom_normal attribute before processing to prevent pre-baked normals overriding Blender's sharp edge processing - Update comment: calc_normals_split() removed in Blender 5.0 Verified: production GLB has 812 extra vertices vs geometry GLB, 6027 positions with multiple normals = sharp edges correctly encoded. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -130,21 +130,45 @@ def main() -> None:
|
|||||||
if cleared_normals:
|
if cleared_normals:
|
||||||
print(f"Cleared OCC custom_normal attribute from {cleared_normals} mesh objects")
|
print(f"Cleared OCC custom_normal attribute from {cleared_normals} mesh objects")
|
||||||
|
|
||||||
# Apply smooth shading using the configured angle threshold
|
# Mark sharp edges and seams using the configured angle threshold.
|
||||||
|
# We use Blender's edit-mode operators (mark_sharp + mark_seam) rather than
|
||||||
|
# shade_smooth_by_angle alone, because:
|
||||||
|
# 1. mark_sharp() sets the sharp_edge boolean attribute on edges — the glTF
|
||||||
|
# exporter creates vertex splits (duplicate vertices with different normals)
|
||||||
|
# at sharp edges, which is how glTF encodes hard edges.
|
||||||
|
# 2. mark_seam() ensures UV splits at the same edges (stepper-addon behaviour).
|
||||||
|
# Note: calc_normals_split() was removed in Blender 5.0 — not needed here
|
||||||
|
# because export_apply=True triggers vertex splitting automatically.
|
||||||
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"Marking sharp edges + seams at {args.smooth_angle}° ({smooth_rad:.3f} rad)")
|
||||||
|
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
total_sharp = 0
|
||||||
for obj in mesh_objects:
|
for obj in mesh_objects:
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
try:
|
|
||||||
bpy.ops.object.shade_smooth_by_angle(angle=smooth_rad)
|
|
||||||
except Exception:
|
|
||||||
bpy.ops.object.shade_smooth()
|
|
||||||
if hasattr(obj.data, 'use_auto_smooth'):
|
|
||||||
obj.data.use_auto_smooth = True
|
|
||||||
obj.data.auto_smooth_angle = smooth_rad
|
|
||||||
|
|
||||||
# Apply OCC sharp edges if available (overrides pure dihedral-angle shading)
|
# Set all faces smooth
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
for poly in obj.data.polygons:
|
||||||
|
poly.use_smooth = True
|
||||||
|
|
||||||
|
# Enter edit mode, deselect, select sharp edges by angle, mark sharp+seam
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
bpy.ops.mesh.select_all(action='DESELECT')
|
||||||
|
bpy.ops.mesh.edges_select_sharp(sharpness=smooth_rad)
|
||||||
|
bpy.ops.mesh.mark_sharp()
|
||||||
|
bpy.ops.mesh.mark_seam()
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
|
# Count how many edges were marked
|
||||||
|
n_sharp = sum(1 for e in obj.data.edges if e.use_edge_sharp)
|
||||||
|
total_sharp += n_sharp
|
||||||
|
obj.select_set(False)
|
||||||
|
|
||||||
|
print(f"Marked {total_sharp} sharp/seam edges across {len(mesh_objects)} objects")
|
||||||
|
|
||||||
|
# Apply OCC sharp edges if available (additional explicit sharp edges from CAD data)
|
||||||
sharp_pairs = mesh_attributes.get("sharp_edge_pairs") or []
|
sharp_pairs = mesh_attributes.get("sharp_edge_pairs") or []
|
||||||
if sharp_pairs:
|
if sharp_pairs:
|
||||||
_apply_sharp_edges_from_occ(mesh_objects, sharp_pairs)
|
_apply_sharp_edges_from_occ(mesh_objects, sharp_pairs)
|
||||||
|
|||||||
Reference in New Issue
Block a user