refactor: replace STL intermediary with OCC-native STEP→GLB pipeline

- export_step_to_gltf.py: STEP→GLB via RWGltf_CafWriter + BRepBuilderAPI_Transform
  (mm→m pre-scaling, XCAFDoc_ShapeTool.GetComponents_s static method)
- Blender scripts (blender_render.py, still_render.py, turntable_render.py,
  export_gltf.py, export_blend.py): import GLB instead of STL, remove _scale_mm_to_m
- step_tasks.py: add generate_gltf_production_task, remove generate_stl_cache,
  replace _bbox_from_stl with _bbox_from_glb (trimesh), auto-queue geometry GLB
  after thumbnail render
- render_blender.py: replace _stl_from_cache_or_convert with _glb_from_step,
  remove convert_step_to_stl and export_per_part_stls
- domains/rendering/tasks.py: update render_turntable_task, export_gltf/blend tasks
  to use GLB instead of STL
- cad.py: remove STL download/generate endpoints, add generate-gltf-production
- admin.py: generate-missing-stls → generate-missing-geometry-glbs
- Frontend: replace STL cache UI with GLB generate buttons, remove stl_cached field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 16:49:18 +01:00
parent 3eba7b2d37
commit 95cfe0aa93
20 changed files with 809 additions and 1301 deletions
-44
View File
@@ -73,7 +73,6 @@ export default function AdminPage() {
blender_eevee_samples: number
threejs_render_size: number
thumbnail_format: string
stl_quality: string
blender_smooth_angle: number
cycles_device: string
blender_max_concurrent_renders: number
@@ -159,14 +158,6 @@ export default function AdminPage() {
onError: (e: any) => toast.error(e.response?.data?.detail || 'Import failed'),
})
const generateMissingStlsMut = useMutation({
mutationFn: () => api.post('/admin/settings/generate-missing-stls'),
onSuccess: (res) => {
toast.success(res.data.message || 'STL generation queued')
},
onError: (e: any) => toast.error(e.response?.data?.detail || 'Failed'),
})
const reextractMetadataMut = useMutation({
mutationFn: () => api.post('/admin/settings/reextract-metadata'),
onSuccess: (res) => {
@@ -397,29 +388,6 @@ export default function AdminPage() {
</div>
</div>
{/* STL quality */}
<div className="flex items-center gap-6 flex-wrap">
<span className="text-sm font-medium text-content-secondary w-28 shrink-0">STL quality</span>
{(['low', 'high'] as const).map((q) => (
<button
key={q}
onClick={() => setBlenderDraft((d) => ({ ...d, stl_quality: q }))}
className={`px-4 py-1.5 rounded-full border text-sm font-medium transition-colors ${
blender.stl_quality === q
? 'bg-blue-600 text-white border-blue-600'
: 'bg-surface text-content-secondary border-border-default hover:border-blue-400 hover:text-blue-600'
}`}
>
{q === 'low' ? 'Low (fast)' : 'High (detailed)'}
</button>
))}
<p className="text-xs text-content-muted">
{blender.stl_quality === 'high'
? 'Fine mesh (tol=0.01) — slower STEP→STL, sharper edges.'
: 'Coarse mesh (tol=0.3) — faster, good for previews.'}
</p>
</div>
{/* Smooth by angle */}
<div className="flex items-center gap-4 flex-wrap">
<span className="text-sm font-medium text-content-secondary w-28 shrink-0">Smooth angle</span>
@@ -704,18 +672,6 @@ export default function AdminPage() {
</button>
<p className="text-xs text-content-muted">Registers existing renders &amp; CAD thumbnails in the Media Browser.</p>
</div>
<div className="flex flex-col gap-1">
<button
onClick={() => generateMissingStlsMut.mutate()}
disabled={generateMissingStlsMut.isPending}
className="btn-secondary text-sm w-full justify-start"
title="Queue STL conversion for every low/high quality that is not yet cached on disk"
>
<RefreshCw size={14} className={generateMissingStlsMut.isPending ? 'animate-spin' : ''} />
{generateMissingStlsMut.isPending ? 'Queueing…' : 'Generate Missing STLs'}
</button>
<p className="text-xs text-content-muted">Generates low + high STL files for completed STEP files missing them.</p>
</div>
<div className="flex flex-col gap-1">
<button
onClick={() => reextractMetadataMut.mutate()}