feat: sharp edge pipeline V02, tessellation presets, media cache-bust, GMSH plan
Sharp Edge Pipeline V02:
- export_step_to_gltf.py: replace BRep_Tool.Polygon3D_s (returns None in XCAF) with
GCPnts_UniformAbscissa curve sampling at 0.3mm step — extracts 17,129 segment pairs
- Inject sharp_edge_pairs + sharp_threshold_deg into GLB extras (scenes[0].extras)
via binary GLB JSON-chunk patching (no extra dependency)
- export_gltf.py: read schaeffler_sharp_edge_pairs from Blender scene custom props,
apply via KD-tree to mark edges sharp=True + seam=True (OCC mm Z-up → Blender transform)
- tools/restore_sharp_marks.py: dual-pass (dihedral angle + OCC pairs), updated coordinate
transform (X, -Z, Y) * 0.001
Tessellation:
- Admin UI: Draft / Standard / Fine preset buttons with active-state highlighting
- Default angular deflection: preview 0.5→0.1 rad, production 0.2→0.05 rad
- export_glb.py: read updated defaults from system_settings
Media / Cache:
- media/service.py: get_download_url appends ?v={file_size_bytes} cache-buster
- media/router.py: Cache-Control: no-cache for all download/thumbnail endpoints
Render pipeline:
- still_render.py / turntable_render.py: shared GPU activation + camera improvements
- render_order_line.py: global render position support
- render_thumbnail.py: updated defaults
Frontend:
- InlineCadViewer: file_size_bytes-aware URL update triggers re-fetch on regeneration
- ThreeDViewer: material panel, part selection, PBR mode improvements
- Admin.tsx: tessellation preset cards, GMSH setting dropdown
- MediaBrowser, ProductDetail, OrderDetail, Orders: various UI improvements
- New: MaterialPanel, GlobalRenderPositionsPanel, StepIndicator components
- New: renderPositions.ts API client
Plans / Docs:
- plan.md: GMSH Frontal-Delaunay tessellation plan (6 tasks)
- LEARNINGS.md: OCC Polygon3D_s None issue + GCPnts fix
- .gitignore: add backend/core (core dump from root process)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
"""Blender companion script: restore sharp + seam edge marks after importing a production GLB.
|
||||
|
||||
After importing a Schaeffler production GLB in Blender, run this script once via
|
||||
the Scripting workspace (Text Editor → Run Script). It reads the sharp angle that
|
||||
was baked into the GLB at export time and re-applies mark_sharp() + mark_seam() on
|
||||
every mesh object.
|
||||
the Scripting workspace (Text Editor → Run Script). It reads the sharp angle AND the
|
||||
OCC B-rep sharp edge pairs baked into the GLB at export time, and re-applies
|
||||
mark_sharp() + mark_seam() on every mesh object.
|
||||
|
||||
The GLB visual shading already encodes the sharp edges via vertex splits (normals).
|
||||
This script restores the blue sharp-crease and red seam markers in Edit Mode for
|
||||
further editing in Blender.
|
||||
further editing in Blender — including for UV unwrapping.
|
||||
|
||||
Usage:
|
||||
1. File → Import → glTF 2.0 (.glb) — open your production GLB
|
||||
@@ -18,14 +18,17 @@ Usage:
|
||||
|
||||
import bpy
|
||||
import math
|
||||
|
||||
angle_deg = bpy.context.scene.get("schaeffler_sharp_angle_deg", 30.0)
|
||||
smooth_rad = math.radians(float(angle_deg))
|
||||
import bmesh
|
||||
import mathutils
|
||||
|
||||
mesh_objects = [o for o in bpy.data.objects if o.type == "MESH"]
|
||||
if not mesh_objects:
|
||||
print("No mesh objects found in scene.")
|
||||
else:
|
||||
# --- Pass 1: dihedral-angle-based sharp/seam marks ---
|
||||
angle_deg = bpy.context.scene.get("schaeffler_sharp_angle_deg", 30.0)
|
||||
smooth_rad = math.radians(float(angle_deg))
|
||||
|
||||
total_sharp = 0
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
for obj in mesh_objects:
|
||||
@@ -42,5 +45,58 @@ else:
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
total_sharp += sum(1 for e in obj.data.edges if e.use_edge_sharp)
|
||||
obj.select_set(False)
|
||||
print(f"Restored sharp/seam marks at {angle_deg}°: "
|
||||
f"{total_sharp} edges across {len(mesh_objects)} objects.")
|
||||
print(f"Pass 1 (dihedral {angle_deg}°): {total_sharp} sharp/seam edges across {len(mesh_objects)} objects.")
|
||||
|
||||
# --- Pass 2: OCC B-rep sharp edges from GLB extras ---
|
||||
# The production GLB embeds schaeffler_sharp_edge_pairs (OCC B-rep topology,
|
||||
# dense curve samples at 0.3mm) in scenes[0].extras, which Blender maps to
|
||||
# scene custom properties on import. These cover geometrically sharp edges
|
||||
# that the dihedral-angle pass misses due to tessellation noise.
|
||||
#
|
||||
# Coordinate convention (mirrors export_gltf.py _apply_sharp_edges_from_occ):
|
||||
# OCC STEP space (Z-up, mm) → Blender (Z-up, m):
|
||||
# Blender(X, Y, Z) = OCC(X*0.001, -Z*0.001, Y*0.001)
|
||||
occ_pairs = bpy.context.scene.get("schaeffler_sharp_edge_pairs") or []
|
||||
if occ_pairs:
|
||||
print(f"Pass 2 (OCC B-rep): applying {len(occ_pairs)} sharp edge segment pairs...")
|
||||
|
||||
SCALE = 0.001 # OCC mm → Blender m
|
||||
TOL = 0.0005 # 0.5 mm tolerance in metres
|
||||
|
||||
marked_total = 0
|
||||
for obj in mesh_objects:
|
||||
bm_obj = bmesh.new()
|
||||
bm_obj.from_mesh(obj.data)
|
||||
bm_obj.verts.ensure_lookup_table()
|
||||
bm_obj.edges.ensure_lookup_table()
|
||||
|
||||
world_mat = obj.matrix_world
|
||||
kd = mathutils.kdtree.KDTree(len(bm_obj.verts))
|
||||
for v in bm_obj.verts:
|
||||
kd.insert(world_mat @ v.co, v.index)
|
||||
kd.balance()
|
||||
|
||||
marked = 0
|
||||
for pair in occ_pairs:
|
||||
v0_bl = mathutils.Vector((pair[0][0] * SCALE, -pair[0][2] * SCALE, pair[0][1] * SCALE))
|
||||
v1_bl = mathutils.Vector((pair[1][0] * SCALE, -pair[1][2] * SCALE, pair[1][1] * SCALE))
|
||||
_co0, idx0, dist0 = kd.find(v0_bl)
|
||||
_co1, idx1, dist1 = kd.find(v1_bl)
|
||||
if dist0 > TOL or dist1 > TOL:
|
||||
continue
|
||||
if idx0 == idx1:
|
||||
continue
|
||||
bv0, bv1 = bm_obj.verts[idx0], bm_obj.verts[idx1]
|
||||
edge = bm_obj.edges.get((bv0, bv1)) or bm_obj.edges.get((bv1, bv0))
|
||||
if edge is not None:
|
||||
edge.smooth = False
|
||||
edge.seam = True
|
||||
marked += 1
|
||||
|
||||
bm_obj.to_mesh(obj.data)
|
||||
bm_obj.free()
|
||||
marked_total += marked
|
||||
|
||||
print(f"Pass 2 (OCC B-rep): {marked_total} additional edges marked across {len(mesh_objects)} objects.")
|
||||
else:
|
||||
print("Pass 2 (OCC B-rep): no schaeffler_sharp_edge_pairs in GLB extras — skipped.")
|
||||
|
||||
Reference in New Issue
Block a user