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

  1. ADR Header
  2. Context
    1. Key Insight: Two Separate Decisions
  3. Options Considered
    1. Rendering Engine
      1. Option A: React Three Fiber (Three.js) — Selected
      2. Option B: Babylon.js — Rejected
      3. Option C: Hoops Communicator — Rejected as primary renderer
      4. Option D: CADExchanger Web Toolkit — Rejected as primary renderer
    2. CAD Conversion Pipeline
      1. Option E: opencascade.js in-browser (WASM) — Rejected
      2. Option F: Server-side OpenCascade CLI (Docker) — Selected
      3. Option G: CADExchanger CLI — Alternative
  4. Decision
    1. Rendering: React Three Fiber across all phases
    2. Pipeline: Server-side STEP conversion from day one
  5. Architecture
    1. Conversion Pipeline
    2. Viewer Architecture
    3. Constraints
      1. 1. No Client-Side CAD Processing
      2. 2. No Domain Logic in the Viewer
      3. 3. Engine-Agnostic Mesh-to-Graph Mapping
      4. 4. Cacheable Conversion Results
      5. 5. Tessellation Quality Tiers
  6. Consequences
    1. Positive
    2. Negative
    3. Risks and Mitigations
  7. Changes from Initial Draft
  8. 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:

  1. Rendering engine — what draws meshes in the browser
  2. 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:

  1. Accepts STEP/IGES files via API
  2. Hashes the input file for cache lookup
  3. 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)
  4. Stores results in cache (S3 or local filesystem)
  5. 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

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