feat: GPU rendering + material matching + perf improvements
- GPU: fix Cycles device activation order — set compute_device_type BEFORE engine init, re-set AFTER open_mainfile wipes preferences - GPU: remove _mark_sharp_and_seams edit-mode loop (redundant with Blender 5.0 shade_smooth_by_angle), saves ~200s/render on 175 parts - Material: fix _AFN suffix mismatch — build AF-stripped mat_map keys and add prefix fallback in _apply_material_library (blender_render.py) - Material: production GLB now uses get_material_library_path() which checks active AssetLibrary instead of empty legacy system setting - Admin: RenderTemplateTable multi-select output types (M2M frontend) - Admin: MaterialLibraryPanel replaced with link to Asset Libraries - UX: move Toaster to top-left to avoid dispatch button overlap - SQLAlchemy: add .unique() to all RenderTemplate M2M collection queries - Logging: flush=True on all Blender progress prints, stdout reconfigure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,80 +1,49 @@
|
||||
# Plan: Fix GLB Export Pipeline + Viewer Staleness
|
||||
# Plan: Migrate blender_render.py from STL to GLB Import
|
||||
|
||||
## Root Cause Analysis
|
||||
## Kontext
|
||||
|
||||
### Bug 1 — `export_colors` not valid in Blender 5.0 (CRITICAL)
|
||||
**File**: `render-worker/scripts/export_gltf.py`
|
||||
`bpy.ops.export_scene.gltf(export_colors=False)` → Blender exits code 1:
|
||||
`keyword "export_colors" unrecognized`
|
||||
→ Blender path always fails → always falls back to trimesh → no materials, no sharp edges, faceted mesh.
|
||||
This is confirmed in every single log entry. Blender has never successfully exported a GLB.
|
||||
`render_blender.py` (backend service) correctly converts STEP→GLB via OCC and passes the GLB path as `argv[0]` to `blender_render.py`. However, `blender_render.py` still calls `_import_stl()` which uses `bpy.ops.wm.stl_import()` and `_scale_mm_to_m()`. The GLB from OCC is already in metres (scaled 0.001 internally by `export_step_to_gltf.py`), so no scaling is needed.
|
||||
|
||||
### Bug 2 — `GlbModel` `cloned` ref never resets on URL change (CRITICAL)
|
||||
**File**: `frontend/src/components/cad/InlineCadViewer.tsx`
|
||||
`cloned = useRef<THREE.Group | null>(null)` with guard `if (!cloned.current)` only clones once.
|
||||
When `glbBlobUrl` changes (new GLB generated), React does NOT remount `GlbModel` (same position in tree),
|
||||
so `cloned.current` still holds the old geometry → old mesh shown forever.
|
||||
Fix: add `key={glbBlobUrl}` to `<GlbModel>` → forces remount on each new URL.
|
||||
`still_render.py` already has a correct `_import_glb()` implementation using `bpy.ops.import_scene.gltf()` — this serves as the reference.
|
||||
|
||||
### Bug 3 — `glbBlobUrl` not cleared between fetches (UX)
|
||||
**File**: `frontend/src/components/cad/InlineCadViewer.tsx`
|
||||
When `downloadUrl` changes, cleanup revokes the old blob URL, but `glbBlobUrl` state still holds
|
||||
the (now revoked) old URL → `GlbModel` tries to render a revoked URL for the duration of the new fetch.
|
||||
Fix: `setGlbBlobUrl(null)` at the start of the effect before fetching.
|
||||
This caused render failures for order SA-2026-00099: Blender tried to STL-import a `.glb` file → silent failure → cancelled renders.
|
||||
|
||||
### Bug 4 — `staleTime: 30_000` delays detecting new GLB (UX)
|
||||
**File**: `frontend/src/components/cad/InlineCadViewer.tsx`
|
||||
After "Generate GLB" the task completes and a new MediaAsset is written to DB,
|
||||
but the assets query is cached for 30 seconds → `downloadUrl` stays stale → viewer fetches old GLB.
|
||||
Fix: reduce `staleTime` to `0` so the query always refetches on focus/mount after invalidation.
|
||||
## Betroffene Dateien
|
||||
|
||||
---
|
||||
| Datei | Änderung |
|
||||
|-------|----------|
|
||||
| `blender-renderer/blender_render.py` | Replace `_import_stl` with `_import_glb`, remove `_scale_mm_to_m`, rename `stl_path` → `glb_path` |
|
||||
|
||||
## Affected Files
|
||||
## Tasks (in Reihenfolge)
|
||||
|
||||
| File | Change | Bug |
|
||||
|------|--------|-----|
|
||||
| `render-worker/scripts/export_gltf.py` | Remove invalid `export_colors=False` | 1 |
|
||||
| `frontend/src/components/cad/InlineCadViewer.tsx` | key={glbBlobUrl} on GlbModel + clear state + staleTime=0 | 2, 3, 4 |
|
||||
### [x] Task 1: Replace `_import_stl()` with `_import_glb()` in `blender_render.py`
|
||||
|
||||
---
|
||||
- **Datei**: `blender-renderer/blender_render.py`
|
||||
- **Was**:
|
||||
1. Replace `_import_stl()` function (lines ~206-289) with `_import_glb()` modeled on `still_render.py:196-229`:
|
||||
- Use `bpy.ops.import_scene.gltf(filepath=glb_path)`
|
||||
- Collect imported mesh objects
|
||||
- No scaling needed (GLB already in metres)
|
||||
2. Remove `_scale_mm_to_m()` function (lines ~166-182) — no longer needed
|
||||
3. Remove all calls to `_scale_mm_to_m(parts)` (Mode A ~line 466, Mode B ~line 386)
|
||||
4. Replace all calls to `_import_stl(stl_path)` with `_import_glb(glb_path)` (Mode A ~line 464, Mode B ~line 384)
|
||||
5. Rename variable `stl_path` → `glb_path` throughout (line 65, 715, and all references)
|
||||
6. Update docstring/comments referencing STL
|
||||
- **Akzeptanzkriterium**: `blender_render.py` imports GLB via `bpy.ops.import_scene.gltf()`, no STL references remain, no mm→m scaling
|
||||
- **Abhängigkeiten**: keine
|
||||
|
||||
## Tasks
|
||||
## Migrations-Check
|
||||
|
||||
### Task 1: Fix Blender GLTF export parameters
|
||||
**File**: `render-worker/scripts/export_gltf.py`
|
||||
Remove `export_colors=False` from `bpy.ops.export_scene.gltf()` call.
|
||||
Keep `export_materials="EXPORT"` and `export_image_format="AUTO"` — these are valid in Blender 5.0.
|
||||
**Acceptance**: Blender exits 0, GLB file is created with materials.
|
||||
**Requires rebuild**: yes — scripts are COPY'd into container.
|
||||
Keine Migration nötig — reine Script-Änderung.
|
||||
|
||||
### Task 2: Fix GlbModel stale mesh on regeneration
|
||||
**File**: `frontend/src/components/cad/InlineCadViewer.tsx`
|
||||
Add `key={glbBlobUrl}` on the `<GlbModel>` element inside the Canvas.
|
||||
This forces React to unmount+remount GlbModel whenever the blob URL changes,
|
||||
resetting the `cloned` ref and loading the fresh geometry.
|
||||
**Acceptance**: After generating a new GLB, the viewer shows the new mesh, not the old one.
|
||||
## Reihenfolge-Empfehlung
|
||||
|
||||
### Task 3: Clear stale blob URL before new fetch
|
||||
**File**: `frontend/src/components/cad/InlineCadViewer.tsx`
|
||||
At the top of the `useEffect([downloadUrl, token])` body, add `setGlbBlobUrl(null)` before the fetch.
|
||||
This shows the loading spinner instead of a broken/stale model during re-fetch.
|
||||
**Acceptance**: After regeneration, viewer shows spinner while new GLB loads.
|
||||
1. Task 1 (blender_render.py)
|
||||
2. Rebuild render-worker: `docker compose up -d --build render-worker`
|
||||
3. Test: trigger a thumbnail render or order render and check logs
|
||||
|
||||
### Task 4: Remove staleTime delay on asset query
|
||||
**File**: `frontend/src/components/cad/InlineCadViewer.tsx`
|
||||
Change `staleTime: 30_000` → `staleTime: 0` on the `gltf_geometry` assets query.
|
||||
The `qc.invalidateQueries()` call after generating already forces a refetch,
|
||||
but staleTime=0 also ensures refetch on window focus/tab return.
|
||||
**Acceptance**: New MediaAsset is picked up within seconds of task completion.
|
||||
## Risiken / Offene Fragen
|
||||
|
||||
---
|
||||
|
||||
## Reihenfolge
|
||||
Task 1 (rebuild) + Tasks 2/3/4 (frontend hot-reload) in parallel.
|
||||
|
||||
## Risiken
|
||||
- `export_materials="EXPORT"` and `export_image_format="AUTO"` may also be invalid in Blender 5.0.
|
||||
If so, remove them too and test with bare minimum params (format + apply only).
|
||||
- If the Schaeffler .blend library materials use custom node groups instead of Principled BSDF,
|
||||
the GLTF exporter will still export flat grey — that requires material baking, out of scope here.
|
||||
1. **`_apply_material_library()`** and **`_resolve_part_name()`** work on Blender objects after import — they should work identically regardless of import format (STL vs GLB).
|
||||
2. **Auto-camera computation** uses bounding box of imported objects — works the same with GLB meshes.
|
||||
3. **`turntable_render.py`** and **`turntable_setup.py`** — need to check if they also still use STL import. If so, they need the same fix (but out of scope for this plan unless confirmed).
|
||||
|
||||
Reference in New Issue
Block a user