38 KiB
RFC 0001: Canonical STEP to USD Workflow for Visualization and Rendering
- Status: Proposed
- Author: Codex
- Date: 2026-03-11
Summary
This RFC proposes replacing the current dual-GLB CAD visualization pipeline with a canonical USD-based workflow:
STEP -> USDbecomes the primary geometry and scene-authoring path.- One canonical USD stage becomes the source of truth for preview, rendering, and downstream conversions.
- Part metadata is preserved directly on USD prims instead of being tunneled through GLB extras or Blender object names.
- Material assignment and replacement remain supported through USD material bindings plus Blender-time shader realization.
- The current browser-side part picking, isolation, hide/ghost, and manual material assignment workflow is preserved through a derived interactive preview asset keyed by canonical USD
partKey. - Admin/backend concepts are simplified around one canonical scene plus optional derived preview artifacts instead of a geometry-GLB vs production-GLB split.
- STEPper-like seam, sharp-edge, and UV-supporting topology data is computed once during tessellation/export and carried forward as USD-authored mesh metadata.
The target state is not "USD everywhere on day one". The target state is "USD is the canonical persisted scene asset", with derived formats generated only where a consumer still requires them.
Motivation
The current pipeline is split between:
- a geometry GLB exported directly from STEP via OCC
- a production GLB exported by re-importing geometry into Blender, rebuilding topology cues, replacing materials, and exporting another GLB
That split introduces avoidable duplication, fragility, and impedance mismatches:
- STEP is tessellated multiple times for different outputs.
- Metadata preservation depends on ad hoc transport mechanisms.
- Material replacement depends on object-name matching after format round-trips.
- Sharp edges and seams are re-derived late instead of authored once near the source geometry.
- The frontend and media model are forced to treat "geometry" and "production" as separate assets even though they describe the same product.
The code confirms this architecture:
- Geometry export task: backend/app/domains/pipeline/tasks/export_glb.py
- Production export task: backend/app/domains/pipeline/tasks/export_glb.py
- Blender production export script: render-worker/scripts/export_gltf.py
- OCC GLB exporter with XCAF name/color preservation and sharp-edge extras: render-worker/scripts/export_step_to_gltf.py
- Media asset model: backend/app/domains/media/models.py
- Frontend viewer contract: frontend/src/components/cad/ThreeDViewer.tsx
Goals
- Establish one canonical persisted scene asset for the visualization pipeline.
- Preserve STEP/XCAF hierarchy, names, colors, and relevant product metadata.
- Support material assignment and replacement without duplicating geometry assets.
- Preserve the current browser-side 3D material assignment workflow, including click-to-select, isolate, hide/ghost other geometry, and assignment of missing materials from Blender asset-library material names.
- Preserve or improve current render quality.
- Preserve or improve UV-unwrapping support via seam and sharp-edge data.
- Reduce format round-trips and late-stage topology repair.
- Allow phased migration without breaking current browser preview or render outputs.
- Simplify admin/backend settings, APIs, and operational actions around one canonical scene model.
Non-Goals
- Replacing Blender as the final shader/render engine.
- Solving browser-native USD visualization immediately.
- Designing a full custom USD schema package in the first phase.
- Eliminating all derived preview/export assets on day one.
Current State
Export Pipeline
The current backend has two first-class CAD outputs:
gltf_geometrygltf_production
The geometry task exports a GLB directly from STEP using OCC tessellation and optional per-part color mapping. The production task re-exports the STEP file at a higher tessellation quality, then runs Blender headless to:
- import the GLB
- clear OCC-authored custom normals
- mark seams and sharp edges
- append materials from a Blender asset library
- export another GLB
This means the "production" asset is effectively a post-processed derivative of geometry data that already existed earlier in the pipeline.
Metadata Handling
The OCC exporter already preserves meaningful data:
- XCAF part names
- embedded colors
- sharp edge segment pairs injected into GLB extras
However, this data is not represented in a durable scene data model. Some of it is embedded in GLB extras and consumed indirectly by Blender, while some downstream logic still relies on object-name matching.
Material Assignment
Material mapping already exists conceptually in the domain model:
- product-level
cad_part_materials - canonical material/alias resolution in backend/app/domains/materials/service.py
The weak point is the last mile: materials are currently assigned in Blender by matching imported object names from a GLB round-trip in render-worker/scripts/export_gltf.py.
Admin and Settings Surface
The admin/backend model still mirrors the dual-GLB architecture:
- separate preview and production tessellation settings in backend/app/api/routers/admin.py
- a bulk action specifically for missing geometry GLBs in backend/app/api/routers/admin.py
- an admin UI that exposes preview-vs-production GLB tessellation controls in frontend/src/pages/Admin.tsx
- product detail logic that queries both
gltf_geometryandgltf_productionassets in frontend/src/pages/ProductDetail.tsx
That duplication is operationally expensive and should be reduced as part of the refactor, not carried forward under new names.
Seam and Unwrap Support
Blender currently recreates seams by angle selection and then supplements them with OCC-derived sharp-edge segment pairs.
STEPper shows a stronger approach:
- edge seams derived from triangle batch/material boundaries
- sharpness derived from normal discontinuity across shared topology
- UV data preserved from OCC triangulation
Relevant references:
Problems With the Current Design
1. Duplicate canonical assets
The system treats preview geometry and production geometry as separate first-class artifacts even though they represent the same assembly. This forces duplicated media types, viewer logic, and task orchestration.
2. Late topology repair
Sharp edges, seams, and related unwrap hints are reconstructed in Blender after import instead of being preserved as part of the authored scene.
3. Name-based material matching
Material replacement currently depends on imported object naming conventions. That is brittle across exporters, instancing, suffix normalization, and hierarchy flattening.
4. Format-driven workflow instead of scene-driven workflow
GLB is used as both transport and canonical source. That works for browser preview, but it is not a strong scene interchange format for preserving richer metadata, overrides, and layered authoring decisions.
5. Frontend/backend coupling to an implementation detail
The frontend API assumes two separate GLB assets. That is a product of today’s implementation, not a business requirement.
6. Admin coupling to the legacy artifact split
Admin settings, API vocabulary, and repair actions are more complex than necessary because they expose the current implementation detail of two first-class GLB outputs.
Proposal
Overview
Introduce a canonical usd_master asset authored directly from STEP/XCAF tessellation output. This USD stage becomes the single source of truth for:
- geometry
- hierarchy
- source metadata
- default display bindings
- seam/sharp/unwrap support data
Derived outputs become optional compatibility products:
- browser preview GLB, if needed
- Blender render session imports
- downstream packaged deliveries
Important constraint:
- the browser viewer must retain today’s interactive material-assignment workflow
Therefore this RFC does not assume that the browser renders the canonical USD directly. The canonical scene is USD, but the browser may continue to consume a derived interactive preview mesh package as long as it preserves canonical part identity.
The existing dual-GLB split is replaced conceptually by:
- one canonical authored scene
- zero or more derived consumer-specific outputs
Canonical USD Stage Structure
Recommended stage layout:
/Root
/Root/Assembly
/Root/Assembly/<AssemblyNode>
/Root/Assembly/<AssemblyNode>/<PartKey>
/Root/Assembly/<AssemblyNode>/<PartKey>/Mesh
/Root/Looks
/Root/Looks/<MaterialName>
Prim identity
Each part prim must have a stable identity independent of display name. Use a normalized part key derived from:
- XCAF label path or stable assembly path
- original part name
- fallback deterministic hash where required
This key becomes the system’s canonical join key for:
- product material mapping
- render overrides
- frontend selection
- audit/debug output
Object names imported into Blender must no longer be the primary identity mechanism.
USD Authored Data
Each part prim should carry:
hartomat:partKeyhartomat:sourceNamehartomat:sourceAssemblyPathhartomat:sourceColorhartomat:rawMaterialNamehartomat:canonicalMaterialNamehartomat:tessellation:linearDeflectionMmhartomat:tessellation:angularDeflectionRadhartomat:cadFileIdhartomat:productIdwhen availablehartomat:mesh:topologyHash
Each mesh prim should carry:
- points
- face vertex counts / indices
- normals
- UV set(s)
- display color where useful
- seam/sharp topology payload
Seam and sharp-edge payload
USD does not have a first-class standard seam concept for Blender UV editing, so this RFC proposes storing authored topology support data as custom primvars or custom attributes on the mesh prim:
primvars:hartomat:seamEdgeVertexPairsprimvars:hartomat:sharpEdgeVertexPairsprimvars:hartomat:faceBatchIdsprimvars:hartomat:sourceUv
The exact encoding can evolve, but the initial implementation should optimize for deterministic Blender reconstruction rather than elegance.
Recommended first-phase encoding:
- edge vertex index pairs in local mesh topology space
- optional fallback world-space segment pairs only if index-space authoring is not yet practical
Index-space is preferred because it survives transforms cleanly and avoids current KD-tree matching workarounds.
Material Assignment and Replacement
Design requirements
Material assignment must remain possible at:
- product level
- order-line/render level
- interactive replacement level
without duplicating or re-authoring geometry each time.
Proposed model
Use USD material bindings as the durable scene-level material assignment mechanism.
The canonical USD contains:
- geometry
- default display materials or placeholder bindings
- canonical material identifiers attached to prims
Render-time or session-time replacement happens through:
- material binding overrides
- optional lightweight USD override layers
- Blender-time shader realization against the
.blendmaterial library
Browser-side assignment model
Browser-side assignment must continue to work even when Excel names and tessellated part identities do not match cleanly.
The important design change is this:
- Excel names are an input source for proposed assignments.
- Tessellated USD part keys are the canonical targets that users can actually assign in the browser.
In other words, browser assignment should not depend on "fixing" the Excel name mismatch perfectly. The browser should operate on the actual tessellated scene graph that the user sees, and save overrides against canonical partKey values from the USD stage.
Recommended browser workflow:
- Export USD with stable
partKeymetadata on every tessellated part. - Run a reconciliation pass that tries to match Excel rows to those
partKeytargets. - Store the result as:
- proposed auto-matches
- unmatched Excel rows
- unmatched tessellated parts
- user-authored overrides
- Let the browser assign or repair materials by clicking actual scene parts, not by editing raw Excel names.
- Persist browser-authored overrides by
partKey, with provenance indicating they came from manual user assignment.
This preserves the current capability to assign missing materials in the browser, but moves the identity model from fragile name matching to stable part-targeted overrides.
Preserve current 3D viewer behavior
The current viewer supports several behaviors that must not regress:
- clicking an individual visible geometry in the 3D scene
- pinning that selection
- hiding or ghosting other geometry
- highlighting unassigned parts
- assigning a target material from Blender asset-library material names
- fixing missing assignments interactively when automatic matching fails
Those behaviors currently operate on GLB mesh objects in:
- frontend/src/components/cad/ThreeDViewer.tsx
- frontend/src/components/cad/ThreeDViewer.tsx
- frontend/src/components/cad/ThreeDViewer.tsx
- frontend/src/components/cad/MaterialPanel.tsx
The USD refactor must preserve these capabilities. The replacement is not "browser renders USD directly". The replacement is:
- USD remains the canonical scene and material-binding source of truth
- the browser consumes a derived interactive preview asset
- every preview mesh carries the canonical USD
partKey - browser interactions save overrides against
partKey
This means the browser continues to provide the same UX even if the underlying canonical asset is USD.
Viewer package design
To preserve current functionality, the system should produce a browser-facing viewer package derived from the canonical USD scene:
usd_masterpreview.glborpreview.gltfscene_manifest.json
The preview mesh is not a second canonical asset. It is an interaction/render surrogate for the browser.
The manifest or preview-mesh metadata should carry, per selectable mesh:
partKeysourceNameprimPatheffectiveMaterialassignmentProvenanceisUnassigned
This allows the browser to continue doing:
- per-part picking
- isolation and ghost/hide of other geometry
- assigned/unassigned highlighting
- library material assignment by visible part
without relying on exporter-specific name normalization.
Reconciliation model for Excel mismatches
The current system needs normalization and prefix heuristics because the Excel/imported names do not always match the tessellated output exactly. That problem does not disappear with USD, so the workflow needs an explicit reconciliation layer.
Recommended persistence shape:
source_material_assignments- raw imported Excel assignments keyed by source name
resolved_material_assignments- auto-matched assignments keyed by canonical
partKey
- auto-matched assignments keyed by canonical
manual_material_overrides- user-authored browser assignments keyed by canonical
partKey
- user-authored browser assignments keyed by canonical
unmatched_source_rows- Excel rows that could not be mapped
unassigned_parts- tessellated parts with no resolved material
Resolution priority should be:
- manual browser override by
partKey - resolved auto-match by
partKey - source color/default display material
- explicit "unassigned" state in the UI
That makes missing assignments visible instead of silently failing.
Material assignment data model cleanup
The current backend already has the right conceptual split, but the naming is misleading:
- backend/app/domains/products/models.py
Product.cad_part_materialsbehaves like imported or product-authored source material rows - backend/app/domains/products/models.py
CadFile.part_materialsbehaves like viewer-side manual assignments or overrides - backend/app/api/routers/cad.py still presents those overrides as a part-name keyed map
- backend/app/api/routers/products.py performs Excel reconciliation directly into the product-side list
The USD refactor should formalize this into three explicit layers:
source_material_assignments- imported Excel or product-authored rows keyed by source-side names
resolved_material_assignments- auto-matched canonical assignments keyed by
partKey
- auto-matched canonical assignments keyed by
manual_material_overrides- browser-authored overrides keyed by
partKey
- browser-authored overrides keyed by
effective_material_assignments should be computed from those layers rather than stored as a fourth conflicting source of truth.
This should be reflected in API semantics as well:
- product/admin APIs manage source assignments and reconciliation results
- viewer APIs manage manual overrides by canonical
partKey - scene/preview responses expose effective assignment plus provenance
The immediate migration does not require renaming every DB column on day one, but the RFC recommends moving the API contract and service-layer vocabulary to these meanings first and treating legacy field names as internal compatibility details until a later migration.
Browser UI implications
The browser should expose a material-assignment mode based on the canonical tessellated part list:
- click a visible part in the viewer
- inspect its
partKey, source name, current resolved material, and assignment provenance - assign a library material or color directly to that part
- optionally bulk-apply to similar/unassigned parts after user confirmation
The browser should also show a reconciliation panel with:
- unmatched Excel material rows
- unassigned tessellated parts
- confidence or match reason for auto-resolved assignments
This is preferable to treating the viewer as a thin visual shell around a name-based import table.
Asset-library material names in the browser
The browser must continue to present assignable materials using the existing Blender asset-library naming model. That means the user-facing picker still offers canonical library material names, and browser assignments continue to save one of those names as the selected target.
The change is only in the assignment target:
- today: assignment is effectively keyed by normalized GLB mesh name
- target state: assignment is keyed by canonical USD
partKey
This preserves the current operational workflow for users and avoids a regression in missing-material recovery.
Material identity
The material service remains relevant:
- raw material names from Excel or upstream systems are resolved to canonical names
- canonical names are stored on USD prims or material-binding metadata
- Blender maps canonical names to appended material assets from the existing library
The key change is that matching is done by partKey and binding metadata, not by imported object names.
USD representation of browser assignments
Browser-authored assignments should be serializable as material-binding overrides against the canonical USD stage.
Internally, the system should keep:
- canonical USD geometry and metadata stable
- user overrides as
partKey -> canonicalMaterialName
Those overrides can then be:
- applied live in the browser preview layer
- written to a USD override layer for render/export
- flattened into a single USD when a single-file artifact is required
This preserves interactive browser editing without requiring the browser to directly edit Blender assets or depend on exporter-specific naming quirks.
Single USD versus override layers
The user requirement is one single USD for the whole workflow. There are two practical ways to satisfy that:
Option A: Flat single-file publish
- Material bindings are rewritten directly into the canonical root layer.
- Every material change produces a newly published single USD file.
Pros:
- simplest mental model
- easiest artifact handling
Cons:
- mutates the canonical authored stage
- harder to track override intent separately
Option B: Canonical USD plus override layer, flattened for delivery
- Canonical geometry and metadata stay stable.
- Material replacements are authored into a lightweight override layer.
- For consumers that require "one file", the stage is flattened on publish/export.
Pros:
- best authoring model
- clean separation between geometry truth and per-context material overrides
Cons:
- slightly more implementation work
Recommendation:
- Use Option B internally.
- Publish a flattened single USD when a single-file artifact is required externally.
This satisfies the "one single USD" requirement at delivery/runtime boundaries without sacrificing maintainability.
STEPper-Inspired Seam and UV Integration
What should be reused
STEPper already demonstrates three behaviors worth preserving:
- UVs are derived from OCC triangulation nodes.
- Seams can be inferred from face/batch boundaries.
- Sharpness can be inferred from discontinuity across shared edge topology.
That logic currently exists inside Blender addon code, but the underlying idea belongs earlier in the pipeline.
Proposed integration
Port or reimplement the relevant topology derivation into the exporter stage that authors USD:
- tessellate once from STEP
- generate UV coordinates directly from OCC triangulation
- compute seam candidates from batch/material/face boundaries
- compute sharp edges from discontinuity tests
- write those results to USD mesh metadata
Then add a Blender import helper that:
- imports the USD stage
- reads the authored seam/sharp payload
- marks
edge.seamand sharp flags on imported meshes - preserves authored UVs for unwrapping and texture workflows
Why this is better
This moves seam and unwrap support from "best-effort Blender reconstruction" to "authored mesh semantics". Blender remains a consumer of that data, not the only place where the data exists.
System Design Changes
1. Export Layer
Add a new exporter script:
render-worker/scripts/export_step_to_usd.py
Responsibilities:
- read STEP with XCAF
- tessellate once
- preserve hierarchy, names, and colors
- author canonical USD stage
- attach metadata and seam/sharp payload
- optionally emit compatibility GLB derived from USD or from the same tessellation pass
The existing export_step_to_gltf.py can remain temporarily for migration and fallback.
2. Media Asset Model
Extend backend/app/domains/media/models.py with at least:
usd_master
Optional later additions:
usd_previewusd_render_package
The important change is that usd_master becomes the canonical CAD scene artifact associated with a cad_file.
3. Pipeline Tasks
Replace the current dual-export mental model in backend/app/domains/pipeline/tasks/export_glb.py with:
generate_usd_master_task- optional
generate_preview_glb_task - optional
generate_delivery_usd_taskif flattening/packaging is separated
The production GLB task should be retired once Blender can render from USD and browser preview is handled separately.
4. Render Service
The render service in backend/app/services/render_blender.py currently converts STEP -> GLB -> Blender render.
Target flow:
USD -> Blender render
The render subprocess should:
- import USD
- read canonical part metadata
- apply material overrides by
partKey - restore seam/sharp marks from authored USD mesh metadata when needed
- render stills and turntables
This removes the need for a production GLB as an intermediate render artifact.
5. Frontend
The current viewer API in frontend/src/components/cad/ThreeDViewer.tsx assumes:
- one geometry GLB
- one production GLB
Target contract:
- one canonical scene asset reference
- one derived interactive preview artifact reference for browser consumption
- one scene-manifest payload carrying canonical per-part identity and assignment state
Short term:
- keep browser rendering on GLB or glTF
- derive the preview asset from the canonical USD workflow
- preserve today’s select/isolate/hide/material-assign interactions in the browser
Long term:
- evaluate a USD-capable viewer only if it is clearly worth the operational complexity
The frontend should stop encoding the architectural distinction between geometry and production assets, but it must retain the current interactive viewer workflow.
6. Viewer Assignment API
The current viewer override endpoint in backend/app/api/routers/cad.py should evolve from a raw part-name keyed map into a canonical scene-assignment endpoint.
Target behavior:
GET /cad/{id}/part-materialsreturns manual overrides keyed bypartKeyplus effective assignment metadataPUT /cad/{id}/part-materialsaccepts a full override map keyed bypartKey- the API response includes assignment provenance so the browser can distinguish auto-match from manual repair
- the browser does not need to know whether the preview asset came from GLB, glTF, or another future format
This preserves the current operational workflow while removing exporter-specific identity assumptions.
7. Admin and Backend Simplification
Settings
The current admin settings model exposes four tessellation knobs:
gltf_preview_linear_deflectiongltf_preview_angular_deflectiongltf_production_linear_deflectiongltf_production_angular_deflection
That made sense when both outputs were first-class. In the USD model, the settings should be simplified to reflect the actual scene pipeline:
- canonical scene tessellation profile for
usd_master - optional preview-derivation profile if the browser preview truly needs a lower-cost surrogate
- optional render-import or delivery profile only if direct USD consumption proves insufficient
Recommendation:
- collapse the current preview/production naming into
scene_*andpreview_*concepts - do not expose a separate production-GLB quality preset in the target design
- keep the number of admin-visible tessellation profiles to the minimum required operationally
Bulk actions and repair flows
Admin actions should also move from artifact-specific wording to scene pipeline wording.
Examples:
- replace "generate missing geometry GLBs" with "generate missing canonical scenes" and, if needed, "regenerate missing viewer previews"
- keep "reextract metadata" but scope it to canonical scene metadata, not GLB extras
- ensure "re-process STEP" means rebuild canonical USD, manifest, and preview derivatives together
Frontend/admin simplification
The admin and product-detail UI should stop presenting gltf_geometry and gltf_production as equal first-class business objects.
Target UI model:
- one canonical scene status
- one preview availability status
- one effective material-assignment status, including unmatched and manually overridden counts
This should remove a class of user confusion where two 3D artifacts exist for the same product but only one is actually canonical.
Proposed Data Contract
Canonical scene metadata
For a cad_file, API responses should eventually expose:
- canonical scene asset id
- canonical scene asset type
- preview asset url if browser preview uses GLB or glTF
- scene manifest for canonical part identity and effective material state
- list of part identities and metadata extracted from the canonical scene
Example shape:
{
"cad_file_id": "uuid",
"scene_asset": {
"asset_type": "usd_master",
"url": "/media/..."
},
"preview_asset": {
"asset_type": "gltf_preview",
"url": "/media/..."
},
"scene_manifest": {
"parts": [
{
"part_key": "ring_outer",
"source_name": "RingOuter_AF0",
"prim_path": "/Root/Assembly/Bearing/RingOuter",
"effective_material": "HARTOMAT_010102_Steel-Polished",
"assignment_provenance": "manual",
"is_unassigned": false
}
]
},
"parts": [
{
"part_key": "ring_outer",
"source_name": "RingOuter_AF0",
"canonical_material_name": "Steel_Polished"
}
]
}
Migration Plan
Phase 1: Dual-write USD beside GLB
- add
export_step_to_usd.py - add
usd_mastermedia asset type - export USD in parallel with existing GLB path
- validate hierarchy, metadata, part identity, and tessellation parity
Exit criteria:
- USD contains all required part metadata
- part count and hierarchy are stable versus current outputs
Phase 2: Move material identity to canonical part keys
- stop relying on Blender object-name matching
- resolve material mappings to canonical part keys
- persist canonical material identity on USD prims
Exit criteria:
- material replacement works without name heuristics
Phase 3: Move seam/sharp/UV data into authored scene output
- port STEPper-inspired topology logic into exporter
- write seam/sharp payload to USD
- create Blender-side reconstruction helper for import
Exit criteria:
- Blender unwrap workflow can use authored seams
- current seam quality is matched or improved
Phase 4: Switch Blender render from GLB to USD
- update Blender render scripts and backend service entrypoints
- import USD directly
- bind materials from canonical material metadata
Exit criteria:
- still and turntable outputs match current production quality
- production GLB is no longer required for rendering
Phase 5: Decouple frontend from dual-GLB model
- update API/viewer contract to use one canonical scene reference
- retain a derived interactive preview asset
- introduce a manifest-driven browser identity model based on
partKey - preserve current isolate/hide/material-assignment behavior in the 3D viewer
Exit criteria:
- frontend no longer depends on
geometryGltfUrlplusproductionGltfUrl - frontend still supports current browser-side material assignment workflow without regression
Phase 6: Retire legacy GLB pipeline split
- remove
gltf_geometry/gltf_productionas canonical workflow states - keep preview GLB only as a derived compatibility artifact if still needed
Phase 7: Simplify admin and product-facing operational surfaces
- collapse admin settings and labels to canonical scene plus preview concepts
- align CAD/product APIs with source, resolved, and manual material assignment semantics
- remove product-detail assumptions that two GLB assets are first-class workflow outputs
Exit criteria:
- admin no longer exposes preview-vs-production GLB tessellation as the primary mental model
- product and CAD APIs expose canonical part-keyed assignment semantics
- operators can regenerate canonical scenes and viewer previews without reasoning about legacy GLB pipeline stages
Non-Regression Requirements
The USD refactor is unacceptable if it removes the existing browser-assisted material repair workflow. The following capabilities are mandatory:
- a user can click a visible part in the browser preview and the selection resolves to a stable canonical
partKey - a user can pin a selection and isolate, hide, or ghost non-selected geometry
- the viewer can visually identify unassigned parts
- a user can assign a Blender asset-library material name to the selected part
- the assignment is persisted as a manual override keyed by
partKey - reloading the viewer restores the same effective assignment and unassigned-state visualization
- render/export consumers use the same effective assignment state that the browser shows
- the workflow still functions when Excel reconciliation fails for some rows
Acceptance criteria:
- For a CAD file with mismatched Excel names, the system still produces a canonical scene, a selectable preview asset, a list of unmatched source rows, and a list of unassigned parts.
- In the 3D viewer, selecting a part and assigning a library material updates the effective assignment immediately without depending on raw GLB mesh-name heuristics.
- After refresh, the same part remains assigned through the persisted
partKey -> canonicalMaterialNameoverride mapping. - A subsequent Blender render or export consumes that same override and produces matching material output.
- The preview asset exposes canonical
partKeyidentity for every selectable mesh. - The migrated workflow does not require
geometryGltfUrlplusproductionGltfUrlas separate first-class viewer inputs.
Risks
1. Browser preview remains GLB-bound
USD is a strong canonical scene format, but web runtime support is still weaker than GLB. The likely near-term result is a canonical USD plus a derived browser preview asset. This is acceptable and explicitly part of the design, provided the preview asset is treated as non-canonical and keeps stable partKey metadata for interaction.
2. Blender USD import behavior may not preserve custom topology semantics automatically
Seam and sharp-edge reconstruction will likely require explicit Blender-side helper code. This is expected and acceptable, but it should be treated as a planned integration task, not assumed to work out of the box.
3. Material library remains Blender-native
If the authoritative shaders stay in .blend assets, then USD carries material identity and binding intent, while final shader realization still happens in Blender. That is acceptable, but it should be acknowledged explicitly.
4. Single-file requirement can conflict with layered authoring
A strict "always exactly one USD file at every intermediate step" requirement would push the system toward root-layer mutation. That is possible, but it is worse than using override layers internally and flattening when needed.
Alternatives Considered
Keep the current GLB split and improve it incrementally
Rejected because it preserves the wrong abstraction boundary. It can reduce pain locally, but it does not solve the duplication between canonical geometry, render geometry, and metadata transport.
Use Blender as the canonical scene authoring tool
Rejected because STEP/XCAF metadata and topology are better preserved at the OCC/export layer, not after import into Blender.
Move directly to USD-only everywhere
Rejected for the first implementation because browser preview and some tooling still benefit from GLB compatibility outputs.
Open Questions
- Which USD authoring library will be used in the exporter environment?
- Should seam/sharp payload use custom attributes, primvars, or a lightweight in-house schema from the start?
- Do any downstream consumers require per-face material subsets instead of per-part bindings?
- Is a flattened single-file USD required for all persisted states, or only for delivery and render handoff?
- Should preview GLB be derived from USD, or should both USD and GLB be authored from the same tessellation pass during migration?
Recommendation
Adopt USD as the canonical persisted scene asset and migrate in phases.
The implementation stance should be:
- author geometry, hierarchy, metadata, and seam/sharp payload once from STEP
- store that in
usd_master - drive material replacement via part-keyed USD bindings and overrides
- keep Blender as a renderer/material-realization consumer
- keep GLB only as a compatibility derivative where needed
This is the cleanest path to a single-scene workflow without sacrificing material replacement, metadata fidelity, or unwrap support.
Initial Work Breakdown
- Add
usd_mastermedia asset type and migration. - Implement
export_step_to_usd.pywith hierarchy, names, colors, and part keys. - Add canonical part-key generation and persistence.
- Port STEPper-inspired UV and seam/sharp derivation into exporter output.
- Add Blender USD import helper for seam/sharp restoration and material rebinding.
- Introduce explicit source, resolved, and manual material-assignment service semantics keyed by
partKey. - Change frontend/API contract to a canonical scene asset plus optional preview asset.
- Preserve the current browser viewer workflow through preview-manifest driven part selection and assignment.
- Simplify admin settings and repair actions around canonical scenes and derived previews.
- Switch still/turntable render path to USD input.