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:
@@ -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. +10–30% 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>
|
||||
)}
|
||||
|
||||
@@ -30,7 +30,6 @@ const TYPE_COLORS: Partial<Record<MediaAssetType, string>> = {
|
||||
stl_low: 'badge-yellow',
|
||||
stl_high: 'badge-orange',
|
||||
gltf_geometry: 'badge-green',
|
||||
gltf_production: 'badge-teal',
|
||||
blend_production: 'badge-purple',
|
||||
}
|
||||
|
||||
@@ -43,7 +42,6 @@ const ASSET_TYPES_MEDIA = [
|
||||
|
||||
const ASSET_TYPES_TECHNICAL = [
|
||||
{ value: 'gltf_geometry', label: 'glTF Geometry' },
|
||||
{ value: 'gltf_production', label: 'glTF Production' },
|
||||
{ value: 'blend_production', label: 'Blend (.blend)' },
|
||||
{ value: 'stl_low', label: 'STL Low' },
|
||||
{ value: 'stl_high', label: 'STL High' },
|
||||
@@ -76,7 +74,7 @@ function TypeIcon({ type, size = 32 }: { type: MediaAssetType; size?: number })
|
||||
if (type === 'still' || type === 'thumbnail') return <Image size={size} className="text-content-muted" />
|
||||
if (type === 'turntable') return <Film size={size} className="text-content-muted" />
|
||||
if (type === 'stl_low' || type === 'stl_high') return <Box size={size} className="text-content-muted" />
|
||||
if (type === 'gltf_geometry' || type === 'gltf_production') return <FileCode2 size={size} className="text-content-muted" />
|
||||
if (type === 'gltf_geometry') return <FileCode2 size={size} className="text-content-muted" />
|
||||
return <Layers size={size} className="text-content-muted" />
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user