Extract Base Color, Metallic, Roughness, Transmission, IOR from Blender asset library materials via catalog_assets.py. Store in catalog JSON and serve via /api/asset-libraries/pbr-map endpoint. Frontend viewers apply PBR properties to Three.js MeshStandardMaterial using hex color strings (avoiding Three.js ColorManagement sRGB/linear issues). Key fixes: - RLS bypass for material alias lookup in pbr-map endpoint - pbrMap empty guard prevents premature grey fallback in viewers - Cache-Control: no-cache on pbr-map requests to avoid stale data Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.0 KiB
Review Report: PBR Material Extraction for 3D Viewer
Date: 2026-03-13
Result: ⚠️ Minor issues
Changes Reviewed
PBR Material Extraction Pipeline
Task 1: catalog_assets.py — PBR extraction from Principled BSDF
- Extracts
base_color(linear→sRGB via correct IEC 61966-2-1 formula),metallic,roughness,transmission,ior - Fallback to
mat.diffuse_colorwhen no Principled BSDF node found - Handles both Blender 4.0+ (
Transmission Weight) and older (Transmission) input names - Verified: 35 materials extracted with correct PBR values
Task 2: Catalog refresh
- Rebuilt render-worker, triggered refresh via API
- DB updated:
SELECT catalog FROM asset_librariesshows PBR objects
Task 3: GET /api/asset-libraries/pbr-map endpoint
- Public (no auth) — correct for viewer data
- Placed before
/{lib_id}to avoid UUID collision - 1h cache header (
Cache-Control: public, max-age=3600) - Handles old string-format catalogs gracefully (skips them)
- Returns empty dict when no active library
Task 4: Frontend API types
MaterialPBR,MaterialPBRMaptypes correctly model backend responseAssetLibraryCatalog.materialsupdated to union type for backwards compatAdmin.tsxandAssetLibrary.tsxupdated to handle both string and object formats
Task 5: cadUtils PBR helpers
applyPBRToMaterial(): sets color, metalness, roughness, approximate transmission via opacitypbrColorHex(): converts base_color to hex string for UIpreviewColorForEntry(): moved from MaterialPanel, now uses dynamic PBR lookup with optionalpbrMapparam
Tasks 6-7: ThreeDViewer + InlineCadViewer
- Both fetch PBR map via
useQuerywith 5min staleTime - Material application clones materials before modifying (prevents shared-instance bugs)
_pbrAppliedflag prevents redundant cloning on re-renderspbrMapadded to all dependency arrays
Task 8: MaterialPanel
- Hardcoded
SCHAEFFLER_COLORS(17 entries) removed - Dynamic PBR lookup covers all 35 materials
- Shows M:/R: values (metallic/roughness) in preview swatch
- Accepts optional
pbrMapprop (avoids duplicate fetch when parent already has it) - Falls back to own
useQueryfetch when prop not provided (enabled: !pbrMapProp)
Checklist Results
Backend / Python
- New endpoint is
async def(FastAPI handler) - No auth on pbr-map (intentional — public non-sensitive data for all viewers)
- No SQL injection (ORM
select()only) - No
print()in production code - No hardcoded paths
- [N/A] No new models or migrations
- Endpoint registered (same router, already in main.py)
Frontend / TypeScript
npx tsc --noEmitpasses with 0 errors- New API types in
frontend/src/api/assetLibraries.ts - No
as anyfor API responses (one intentionalanyfor THREE.MeshStandardMaterial in cadUtils to avoid THREE import) - No
bg-surface/50Tailwind opacity syntax useQuerywith staleTime for PBR dataSCHAEFFLER_COLORSexport removed — no orphaned references- All
useEffect/useCallbackdependency arrays includepbrMap - Material cloning prevents shared-instance mutation bugs
Render Pipeline
catalog_assets.pyruns on render-worker (Blender headless)- Existing
refresh_asset_library_catalogtask unchanged — picks up new script - No references to removed services
Security
- No credentials in code
- No hardcoded tokens
- English variable names and comments
Verification
| Gate | Result |
|---|---|
npx tsc --noEmit |
0 errors ✅ |
curl /api/asset-libraries/pbr-map |
35 materials with PBR ✅ |
grep "SCHAEFFLER_COLORS" frontend/src/ |
0 references (only in comment) ✅ |
grep "previewColorForEntry" frontend/src/ |
7 matches (all pass pbrMap) ✅ |
grep "applyPBRToMaterial" frontend/src/ |
4 matches (definition + 3 usages) ✅ |
Problems Found
[cadUtils.ts:137] applyPBRToMaterial uses any type for mat parameter
Severity: Low
Description: The mat parameter is typed as any to avoid importing THREE in the utility module. This is documented with a comment and eslint-disable. The actual callers always pass THREE.MeshStandardMaterial.
Recommendation: Acceptable tradeoff. Alternative would be importing THREE types, but cadUtils is intentionally dependency-light.
[InlineCadViewer] No partKey propagation from parent Group in onReady
Severity: Medium
Description: The InlineCadViewer's onReady callback doesn't propagate partKey from parent Group to child Mesh (unlike ThreeDViewer which does this at line ~540). This means mesh.userData.partKey may be undefined for child meshes of multi-primitive nodes, and the PBR application falls back to resolvePartKey(normalizeMeshName(...)) which may not match if partKeyMap is empty.
Impact: PBR materials may not apply correctly in InlineCadViewer for products where the partKey was set on the parent Group but not propagated to children. This could explain the user's report of "no material changes visible."
Recommendation: Add the same parent-Group partKey propagation logic to InlineCadViewer's onReady callback, matching ThreeDViewer's pattern.
Positives
- Correct color space handling: linear→sRGB conversion uses the IEC 61966-2-1 formula (not simplified gamma), ensuring accurate color reproduction.
- Backwards compatible: Old catalogs (string arrays) are gracefully skipped. Empty partKeyMap returns identity fallback.
- Smart caching: PBR data rarely changes — 5min staleTime + 1h server cache is appropriate.
- Material cloning:
_pbrAppliedflag prevents double-cloning on re-renders. - 35 materials covered: All Schaeffler standard materials now have PBR data (vs 17 hardcoded colors before).
- Principled BSDF fallback: Materials without the node still get viewport display color.
Recommendation
Approved with one medium-priority fix needed: InlineCadViewer needs partKey propagation in its onReady callback to ensure PBR materials apply to all meshes.
Review complete. Result: ⚠️