refactor: remove dead export_gltf.py, cleanup rendering tasks, improve tessellation UI

- Remove export_gltf.py (Blender-based GLB export replaced by OCC direct)
- Remove unused export_gltf_for_order_line_task
- Add Ultra tessellation preset to Admin settings
- Improve tessellation preset descriptions and styling
- Minor cleanup across media, rendering, and workflow modules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 10:37:35 +01:00
parent d843162e5f
commit ec667dd56a
14 changed files with 106 additions and 478 deletions
+88 -46
View File
@@ -1454,7 +1454,7 @@ export default function AdminPage() {
<div className="p-4 border-b border-border-default">
<h2 className="font-semibold text-content">Tessellation Quality</h2>
<p className="text-sm text-content-muted mt-0.5">
OCC mesh precision for GLB export. Lower values = finer mesh + larger files + slower export.
Controls how STEP geometry is converted to triangle meshes. These settings affect both the 3D viewer and Blender renders.
</p>
</div>
<div className="p-4 space-y-6">
@@ -1463,48 +1463,70 @@ export default function AdminPage() {
const PRESETS = [
{
label: 'Draft',
description: 'Fast export, visible faceting on large curves',
color: 'border-amber-400 text-amber-700',
icon: '',
description: 'Fast preview visible faceting on curved surfaces',
useCase: 'Quick checks, large assemblies',
color: 'border-amber-400',
activeColor: 'border-amber-500 ring-2 ring-amber-200',
values: { scene_linear_deflection: 0.2, scene_angular_deflection: 0.3, render_linear_deflection: 0.05, render_angular_deflection: 0.1 },
},
{
label: 'Standard',
description: 'Smooth curves, no fan artifacts recommended',
color: 'border-blue-400 text-blue-700',
icon: '',
description: 'Smooth curves, good quality-to-size ratio',
useCase: 'Recommended for most parts',
color: 'border-blue-400',
activeColor: 'border-blue-500 ring-2 ring-blue-200',
values: { scene_linear_deflection: 0.1, scene_angular_deflection: 0.1, render_linear_deflection: 0.03, render_angular_deflection: 0.05 },
},
{
label: 'Fine',
description: 'Maximum quality, very large files, slow export',
color: 'border-emerald-400 text-emerald-700',
icon: '',
description: 'Near-perfect surfaces, 3-5x larger files',
useCase: 'Close-up renders, small precision parts',
color: 'border-emerald-400',
activeColor: 'border-emerald-500 ring-2 ring-emerald-200',
values: { scene_linear_deflection: 0.05, scene_angular_deflection: 0.05, render_linear_deflection: 0.01, render_angular_deflection: 0.02 },
},
{
label: 'Ultra',
icon: '',
description: 'Maximum fidelity, very slow export',
useCase: 'Marketing renders, extreme close-ups',
color: 'border-purple-400',
activeColor: 'border-purple-500 ring-2 ring-purple-200',
values: { scene_linear_deflection: 0.02, scene_angular_deflection: 0.02, render_linear_deflection: 0.005, render_angular_deflection: 0.01 },
},
]
const isActive = (preset: typeof PRESETS[0]) =>
tess.scene_linear_deflection === preset.values.scene_linear_deflection &&
tess.scene_angular_deflection === preset.values.scene_angular_deflection &&
tess.render_linear_deflection === preset.values.render_linear_deflection &&
tess.render_angular_deflection === preset.values.render_angular_deflection
const isCustom = !PRESETS.some(isActive)
return (
<div>
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide mb-2">Presets</p>
<div className="flex gap-3">
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide mb-2">Quality Presets</p>
<div className="grid grid-cols-4 gap-3">
{PRESETS.map(preset => (
<button
key={preset.label}
onClick={() => setTessellationDraft(preset.values)}
className={`flex-1 p-3 rounded-lg border-2 text-left transition-colors ${isActive(preset) ? preset.color + ' bg-opacity-10' : 'border-border-default text-content hover:border-blue-300'}`}
className={`p-3 rounded-lg border-2 text-left transition-all ${isActive(preset) ? preset.activeColor : preset.color + ' opacity-60 hover:opacity-100'}`}
style={isActive(preset) ? { backgroundColor: 'var(--color-bg-surface-alt)' } : undefined}
>
<div className="font-semibold text-sm">{preset.label}</div>
<div className="text-xs text-content-muted mt-0.5">{preset.description}</div>
<div className="text-xs font-mono text-content-secondary mt-1 space-y-0.5">
<div>scene: {preset.values.scene_angular_deflection} rad / {preset.values.scene_linear_deflection} mm</div>
<div>render: {preset.values.render_angular_deflection} rad / {preset.values.render_linear_deflection} mm</div>
<div className="flex items-center gap-1.5">
<span className="text-base">{preset.icon}</span>
<span className="font-semibold text-sm">{preset.label}</span>
</div>
<div className="text-xs text-content-muted mt-1">{preset.description}</div>
<div className="text-xs text-content-secondary mt-1.5 italic">{preset.useCase}</div>
</button>
))}
</div>
{isCustom && (
<p className="text-xs text-amber-600 mt-2">Current values don't match any preset (custom configuration)</p>
)}
</div>
)
})()}
@@ -1512,27 +1534,40 @@ export default function AdminPage() {
{/* Tessellation engine selector */}
<div className="space-y-2">
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide">Tessellation Engine</p>
<div className="flex items-start gap-4">
<div className="flex flex-col gap-2">
{[
{ value: 'occ', label: 'OCC BRepMesh', description: 'Default engine. Fast, but produces fan triangles at cylinder seam edges.' },
{ value: 'gmsh', label: 'GMSH Frontal-Delaunay', description: 'Conforming mesh no fan triangles on cylinders. +1030% export time. Recommended for cylindrical parts.' },
].map(opt => (
<label key={opt.value} className="flex items-start gap-3 cursor-pointer p-3 rounded-lg border border-border-default hover:border-blue-400 transition-colors">
<input
type="radio"
name="tessellation_engine"
value={opt.value}
checked={(tess.tessellation_engine ?? 'occ') === opt.value}
onChange={() => setTessellationDraft(d => ({ ...d, tessellation_engine: opt.value }))}
className="mt-0.5 shrink-0"
/>
<div>
<div className="text-sm font-medium">{opt.label}</div>
<div className="text-xs text-content-muted mt-0.5">{opt.description}</div>
</div>
</label>
))}
<div className="flex flex-col gap-2">
{[
{ value: 'occ', label: 'OCC BRepMesh', description: 'Default engine. Fast, but produces fan-shaped triangles at cylinder seam lines.' },
{ value: 'gmsh', label: 'GMSH Frontal-Delaunay', description: 'Uniform mesh — eliminates fan artifacts on cylindrical parts. 10-30% slower. Recommended for bearings.' },
].map(opt => (
<label key={opt.value} className="flex items-start gap-3 cursor-pointer p-3 rounded-lg border border-border-default hover:border-blue-400 transition-colors">
<input
type="radio"
name="tessellation_engine"
value={opt.value}
checked={(tess.tessellation_engine ?? 'occ') === opt.value}
onChange={() => setTessellationDraft(d => ({ ...d, tessellation_engine: opt.value }))}
className="mt-0.5 shrink-0"
/>
<div>
<div className="text-sm font-medium">{opt.label}</div>
<div className="text-xs text-content-muted mt-0.5">{opt.description}</div>
</div>
</label>
))}
</div>
</div>
{/* Explanation of deflection parameters */}
<div className="rounded-lg border border-border-default p-4 space-y-3" style={{ backgroundColor: 'var(--color-bg-surface-alt)' }}>
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide">How deflection values work</p>
<div className="grid grid-cols-2 gap-4 text-xs text-content-muted">
<div>
<p className="font-medium text-content-secondary mb-1">Linear deflection (mm)</p>
<p>Maximum allowed distance between the original curved surface and the generated triangles. A value of 0.1 mm means no triangle edge can deviate more than 0.1 mm from the true surface. Lower values produce smoother curves but more triangles.</p>
</div>
<div>
<p className="font-medium text-content-secondary mb-1">Angular deflection (rad)</p>
<p>Maximum angle between adjacent triangle normals. Controls how finely curved regions are subdivided. A value of 0.1 rad (~6°) means neighboring triangles can differ by at most ~6°. Primarily affects small fillets and tight curvatures.</p>
</div>
</div>
</div>
@@ -1542,14 +1577,17 @@ export default function AdminPage() {
className="text-xs text-accent hover:underline flex items-center gap-1 mt-1"
>
{showAdvancedTess ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
{showAdvancedTess ? 'Hide manual values' : 'Advanced: manual deflection values'}
{showAdvancedTess ? 'Hide manual values' : 'Advanced: edit values manually'}
</button>
{/* Manual inputs */}
{showAdvancedTess && (<>
<div className="grid grid-cols-2 gap-6">
<div className="space-y-4">
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide">Scene (USD Master)</p>
<div>
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide">3D Viewer + USD Master</p>
<p className="text-xs text-content-muted mt-0.5">Used for the interactive 3D viewer GLB and the canonical USD scene file. Optimized for real-time display.</p>
</div>
<div className="flex items-center gap-3">
<label className="text-sm text-content-secondary w-36 shrink-0">Linear deflection</label>
<input
@@ -1576,10 +1614,12 @@ export default function AdminPage() {
/>
<span className="text-sm text-content-muted">rad</span>
</div>
<p className="text-xs text-content-muted">Used for the USD master + 3D viewer GLB (canonical scene). Smaller = smoother surfaces.</p>
</div>
<div className="space-y-4">
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide">Render output</p>
<div>
<p className="text-xs font-semibold text-content-secondary uppercase tracking-wide">Blender Render Output</p>
<p className="text-xs text-content-muted mt-0.5">Used for final Blender renders (stills, turntables). Higher quality since render time matters more than file size.</p>
</div>
<div className="flex items-center gap-3">
<label className="text-sm text-content-secondary w-36 shrink-0">Linear deflection</label>
<input
@@ -1606,7 +1646,6 @@ export default function AdminPage() {
/>
<span className="text-sm text-content-muted">rad</span>
</div>
<p className="text-xs text-content-muted">Used for final render output. Smaller = smoother surfaces, larger file sizes.</p>
</div>
</div>
</>)}
@@ -1983,11 +2022,14 @@ function AssetLibraryPanel() {
<div>
<p className="text-xs font-medium text-content-muted mb-1">Materials</p>
<div className="flex flex-wrap gap-1">
{lib.catalog.materials.map((m) => (
<span key={m} className="text-xs px-2 py-0.5 rounded bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
{m}
</span>
))}
{lib.catalog.materials.map((m) => {
const name = typeof m === 'string' ? m : m.name
return (
<span key={name} className="text-xs px-2 py-0.5 rounded bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
{name}
</span>
)
})}
</div>
</div>
)}