ADR-002: 3D Viewer & CAD Pipeline
Adopt React Three Fiber as the rendering engine across all phases; move STEP-to-GLB conversion server-side from day one
Table of Contents
- ADR Header
- Context
- Options Considered
- Decision
- Architecture
- Consequences
- Changes from Initial Draft
- Related Documents
ADR Header
| Field | Value |
|---|---|
| Status | Accepted (Revised) |
| Date | 2026-03-06 |
| Decision | Use React Three Fiber + drei as the rendering engine; server-side STEP-to-GLB conversion via OpenCascade CLI (Docker); no client-side WASM conversion |
| Supersedes | Initial draft that proposed opencascade.js in-browser conversion with a Phase 3 migration to Hoops |
| Deciders | Architecture team |
| Categories | 3D rendering, CAD data pipeline, digital twin viewer |
Context
The MetaForge Dashboard requires an interactive 3D viewer for the Digital Twin page. Engineers need to inspect hardware assemblies, select individual parts, view BOM annotations, toggle exploded views, and (in Phase 3) overlay live telemetry data onto 3D models.
The viewer must:
- Render GLB meshes converted from STEP/IGES CAD files
- Support interactive part selection with highlight and annotation
- Integrate naturally with the React + TypeScript dashboard stack
- Handle assemblies of moderate complexity (hundreds of parts in Phase 1-2, thousands in Phase 3+)
- Provide a path to CAD-grade precision (measurement, sectioning, PMI) for later phases
Key Insight: Two Separate Decisions
The initial draft of this ADR conflated two independent concerns:
- Rendering engine — what draws meshes in the browser
- CAD data pipeline — how STEP files become viewable meshes
These should be evaluated and decided separately. The rendering engine (R3F) is well-suited for the task across all phases. The real risk was in the CAD conversion pipeline — specifically, running opencascade.js as a WASM module in the browser.
Options Considered
Rendering Engine
Option A: React Three Fiber (Three.js) — Selected
React Three Fiber (R3F) is a React renderer for Three.js, the most widely adopted WebGL library.
Strengths:
- Native React integration — components, hooks, state management work naturally
- Largest WebGL ecosystem; extensive community, tutorials, and third-party libraries
- drei provides ready-made controls: OrbitControls, environment lighting, selection helpers
- Fast time-to-MVP — working viewer achievable in days, not weeks
- MIT licensed, no runtime costs
Weaknesses:
- General-purpose 3D engine, not a CAD kernel — no native dimensioning, sectioning, or PMI
- Performance degrades on very large assemblies (10k+ parts) without custom LOD/culling
- CAD measurement and annotation tools must be built from scratch if needed
Option B: Babylon.js — Rejected
Rejected because: No first-class React integration, heavier bundle, and shares the same CAD precision limitations as Three.js without the ecosystem advantage.
Option C: Hoops Communicator — Rejected as primary renderer
Rejected because: Commercial license cost is premature for MVP. The rendering capabilities of R3F are sufficient — the real gap was in the data pipeline, not the renderer. Hoops remains a candidate for its CAD tooling (measurement, sectioning) as an overlay component in Phase 3+, not as a full renderer replacement.
Option D: CADExchanger Web Toolkit — Rejected as primary renderer
Rejected because: Same reasoning as Hoops. May be evaluated for its server-side conversion SDK (see pipeline decision below).
CAD Conversion Pipeline
Option E: opencascade.js in-browser (WASM) — Rejected
The initial draft proposed this approach. Rejected because:
- Community-maintained WASM port, not production-hardened
- Runs on client — large STEP files (50MB+) will exhaust browser memory
- Tessellation quality is inconsistent on complex geometry
- No PMI or metadata preservation
- Debugging conversion failures in a Web Worker is painful
Option F: Server-side OpenCascade CLI (Docker) — Selected
Run OpenCascade Technology (OCCT) as a server-side conversion service in a Docker container.
Strengths:
- Production-grade OCCT kernel — the same geometry engine underlying FreeCAD, KiCad, and others
- Server resources (RAM, CPU) handle large assemblies that would crash browser tabs
- Can preserve part hierarchy and metadata in the GLB output
- Can tune tessellation quality per-use-case (coarse for preview, fine for inspection)
- Single Docker container — minimal infrastructure overhead
- Conversion results are cacheable (STEP hash to GLB)
Weaknesses:
- Adds a server-side dependency (vs. fully client-side)
- Conversion latency on first upload (mitigated by caching and async processing)
Option G: CADExchanger CLI — Alternative
Commercial server-side converter with broader format support and PMI preservation. Hold as Phase 2+ alternative if OCCT tessellation quality proves insufficient for specific formats.
Decision
Rendering: React Three Fiber across all phases
R3F is the rendering engine. There is no planned migration to a different renderer. The Three.js ecosystem is sufficient for mesh rendering, part selection, annotations, and telemetry overlays. If CAD-grade measurement tools are needed in Phase 3+, they will be evaluated as supplementary components, not as a renderer replacement.
Pipeline: Server-side STEP conversion from day one
STEP-to-GLB conversion runs server-side via OpenCascade CLI in a Docker container. There is no client-side WASM conversion.
| Component | Phase 1 (MVP) | Phase 2 | Phase 3+ |
|---|---|---|---|
| Renderer | React Three Fiber + drei | React Three Fiber + drei | React Three Fiber + drei |
| STEP conversion | Server-side OCCT Docker container | + GLB caching layer, LOD variants | + PMI extraction, evaluate CADExchanger CLI |
| CAD tools | Part selection, BOM annotation | Exploded views, layer toggles | Evaluate measurement/sectioning (custom R3F or Hoops overlay) |
| Client bundle | Lightweight — only loads pre-converted GLB | Same | Same |
Architecture
Conversion Pipeline
flowchart LR
UPLOAD["STEP/IGES Upload"] --> API["Conversion API"]
API --> HASH{"Cache\nhit?"}
HASH -->|Yes| CACHE["GLB Cache\n(S3 / local)"]
HASH -->|No| OCCT["OCCT Container\nSTEP → GLB"]
OCCT --> META["Extract metadata\npart tree, names"]
META --> CACHE
CACHE --> CLIENT["Dashboard\n(R3F Viewer)"]
The conversion service:
- Accepts STEP/IGES files via API
- Hashes the input file for cache lookup
- On cache miss, invokes OCCT in a Docker container to produce:
- GLB mesh with named parts (preserving assembly hierarchy)
- JSON metadata (part tree, material assignments, bounding boxes)
- Stores results in cache (S3 or local filesystem)
- Returns GLB + metadata to the client
Viewer Architecture
ConversionAPI (server)
|
GLB + metadata
|
v
ModelManifest <-- graph binding (mesh name → Neo4j node ID)
|
v
ViewerAdapter <-- thin abstraction (keeps door open for future tooling)
|
v
R3FViewer
(all phases)
Constraints
1. No Client-Side CAD Processing
The browser never parses STEP files. All CAD processing happens server-side. The client receives pre-converted GLB meshes and JSON metadata only. This keeps the client bundle lightweight and avoids browser memory issues.
2. No Domain Logic in the Viewer
The 3D viewer and all dashboard panels are read-only projections of Digital Twin state. The viewer subscribes to twin state changes and renders them. It never computes domain logic. This is already specified in the Digital Twin Evolution doc and must be maintained.
3. Engine-Agnostic Mesh-to-Graph Mapping
The mesh_to_node_map (mesh name to Neo4j node ID) defined in the ModelManifest is keyed by mesh name in the GLB, not by any Three.js-specific object reference. This mapping works with any renderer that can load GLB files.
4. Cacheable Conversion Results
Every conversion result is keyed by a content hash of the input STEP file. Re-uploading the same file returns the cached GLB instantly. Cache invalidation is explicit (re-convert on demand).
5. Tessellation Quality Tiers
The conversion service supports quality parameters:
| Tier | Use Case | Mesh Density |
|---|---|---|
| Preview | Thumbnail, dashboard card | Low (fast load) |
| Standard | Interactive viewer, part selection | Medium (default) |
| Fine | Close-up inspection, measurement prep | High (on-demand) |
Consequences
Positive
- Better tessellation quality from day one — server-side OCCT produces consistent, tunable mesh output
- Lightweight client — no WASM blob, no in-browser conversion; client only loads GLB
- No browser memory bombs — large STEP files processed server-side with proper resource limits
- No renderer migration needed — R3F stays across all phases, eliminating migration risk and cost
- Cacheable — repeat views are instant; conversion cost is paid once per STEP file
- Fast MVP — R3F + Docker container is achievable in the same timeframe as the original plan
- Hiring — Three.js is the most common WebGL skill in the market
Negative
- Server dependency — conversion requires a running service (adds to infrastructure)
- Upload latency — first conversion of a new STEP file takes seconds to minutes depending on complexity
- No offline conversion — unlike the WASM approach, the browser cannot convert files without server access
Risks and Mitigations
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| OCCT tessellation insufficient for specific CAD formats | Low | Medium | Evaluate CADExchanger CLI as a drop-in server-side replacement |
| Assembly size exceeds R3F rendering capability | Medium | High | Implement instanced rendering, progressive LOD; generate multiple tessellation tiers |
| Conversion service becomes a bottleneck | Low | Medium | Horizontal scaling (stateless containers); cache hit rate reduces load over time |
| Engineers need CAD measurement tools earlier than Phase 3 | Medium | Medium | Build lightweight measurement on R3F (point-to-point distance, cross-section plane); evaluate Hoops as an overlay, not a replacement |
| No offline/disconnected workflow | Low | Low | Accept as a limitation; MetaForge is a cloud-native platform |
Changes from Initial Draft
| Aspect | Initial Draft | Revised Decision | Rationale |
|---|---|---|---|
| Renderer strategy | R3F Phase 1-2, migrate to Hoops Phase 3+ | R3F all phases | Migration plans are high-risk; R3F is sufficient for rendering |
| STEP conversion | opencascade.js in-browser (WASM) | Server-side OCCT Docker | Better quality, no browser memory issues, cacheable |
| Hoops/CADExchanger role | Future renderer replacement | Potential supplementary CAD tooling (measurement, PMI) | Separates rendering from CAD tool concerns |
| Client complexity | Heavy (WASM + GLB rendering) | Light (GLB rendering only) | Simpler client, better user experience |
Related Documents
| Document | Relationship |
|---|---|
| Digital Twin Page (3D Viewer) | UI specification for the viewer — implements this decision |
| Digital Twin Evolution | Architectural context for viewer as projection of twin state |
| ADR-001: Agent Orchestration | Prior ADR in this series |
| Dashboard Overview | Parent dashboard specification listing R3F as a dependency |