Skip to content

Steam Controller 2026 visualizer (Valve CAD → optimized GLB)#6

Merged
petegordon merged 1 commit into
mainfrom
feat/steam-controller-2026-visualizer
May 24, 2026
Merged

Steam Controller 2026 visualizer (Valve CAD → optimized GLB)#6
petegordon merged 1 commit into
mainfrom
feat/steam-controller-2026-visualizer

Conversation

@petegordon

Copy link
Copy Markdown
Member

Summary

Wires Valve's official 2026 Steam Controller CAD release into the visualizer. The pending DEVICES entry for the Steam Controller already had a steam-controller protocol slot; this PR makes it visually ready so the moment the real vid:pid is captured (when hardware ships in the wild) and the entry is promoted to DEVICES, the overlay loads the correct model with no further visualizer changes.

Asset pipeline

Step Tool In → Out
Source Valve's GitLab sc_solid_stl_20260429.stl (78.9 MB, 1.58M tris)
Convert node tools/stl-to-glb.js <stl> <raw.glb> 0.001 binary STL → indexed GLB (37.9 MB)
Optimize npx @gltf-transform/cli optimize --simplify-ratio 0.03 37.9 MB → 961 KB, 259K tris

The new tools/stl-to-glb.js is a general-purpose binary-STL → GLB converter (dedups vertices via micrometer-quantized keys, supports unit scaling, no external deps) — useful for future vendor CAD releases.

License note ⚠️

The single asset steam-controller.glb is CC BY-NC-SA 4.0 (Valve's license), unlike the rest of the visualizer which is MIT. Full attribution and commercial-use implications are spelled out in packages/visualizer/assets/controllers/STEAM_CONTROLLER_ATTRIBUTION.md.

TL;DR: fine for personal / non-commercial / open-source use; remove or replace the GLB if shipping the lab in a commercial product.

Known limitation

STL is a single solid body — no separated parts for buttons, sticks, triggers, trackpads, or paddles. PROFILES['steam-controller'] is therefore body-only: gyro rotates the whole mesh, but button presses won't animate. Achieving full-fidelity animations would require converting the upstream STEP file (preserves component hierarchy) via FreeCAD/OpenCascade + a Blender pass — separate future work.

Test plan

  • npm install at root — no new deps.
  • node tools/stl-to-glb.js runs without errors on the upstream STL.
  • npx @gltf-transform/cli inspect packages/visualizer/assets/controllers/steam-controller.glb shows ~259K tris, ~961KB.
  • When a real Steam Controller pid is captured later and the entry is promoted to DEVICES, the overlay loads steam-controller.glb correctly (regression check then).

🤖 Generated with Claude Code

Wires Valve's official 2026 Steam Controller CAD into the visualizer.
The pending DEVICES entry already had a steam-controller protocol slot;
this PR makes it visually ready so the moment the real vid:pid is
captured and the entry is promoted to DEVICES, the overlay loads the
correct model without further visualizer changes.

Source pipeline:
  Source : gitlab.steamos.cloud/SteamHardware/SteamController
           sc_solid_stl_20260429.stl (binary STL, 78.9 MB, 1.58M tris)
  Step 1 : node tools/stl-to-glb.js <stl> <raw.glb> 0.001
           (new converter — dedup vertices, mm→m scale, 78.9 MB → 37.9 MB)
  Step 2 : npx -y @gltf-transform/cli optimize <raw.glb> <out.glb>
             --simplify-ratio 0.03
           (1.58M → 259K tris, 37.9 MB → 961 KB)

The new tools/stl-to-glb.js is a general-purpose binary-STL → GLB
converter: dedups vertices via micrometer-quantized keys (CAD exports
have tiny float noise), supports unit scaling, no external deps. Useful
for any future vendor CAD release that ships STL (Sony, Nintendo, etc.).

LICENSE NOTE: this single asset (steam-controller.glb) is
CC BY-NC-SA 4.0 (Valve's license), unlike the rest of the visualizer
which is MIT. Full attribution + commercial-use implications spelled
out in packages/visualizer/assets/controllers/STEAM_CONTROLLER_ATTRIBUTION.md.

Known limitation: STL is a single solid body. PROFILES['steam-controller']
is body-only — gyro rotates the whole mesh, but button presses won't
animate. To get full-fidelity button animations would require the
STEP file (preserves component hierarchy) and a Blender pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
petegordon added a commit that referenced this pull request May 24, 2026
…ender

The Super Nova GLB (and any other GLB run through `gltf-transform
optimize` with defaults, including the Steam Controller GLB queued in
PR #6) ships with EXT_meshopt_compression marked as a *required*
glTF extension. Three.js's GLTFLoader supports the extension — but
only if you call `loader.setMeshoptDecoder(MeshoptDecoder)` first.
Without that, meshopt-compressed buffer views silently fail to
decode and the mesh renders as an empty axis-aligned bounding box,
which is what showed up as a featureless black rectangular block
when the auto-detect correctly swapped the overlay to load
gamesir-super-nova.glb.

Three changes wire it up:

  packages/visualizer/src/controller-overlay.js
    + import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'
    + loader.setMeshoptDecoder(MeshoptDecoder) at the top of the GLB
      load Promise (called per-load; Three's loader is idempotent here)

  apps/overlay/scripts/copy-three.js
    + add 'libs/meshopt_decoder.module.js' to the addons list so the
      vendored Three.js bundle that the no-bundler importmap serves
      includes it (matches the existing fflate vendor pattern)

The fix benefits *every* future GLB the lab ingests via the documented
`gltf-transform optimize` pipeline — Steam Controller, future Cyclone 2
model, etc. The alternative (skipping meshopt during optimize) would
ship 30-50% larger GLBs for everything; better to support the standard
extension once and reap the size benefit going forward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
petegordon added a commit that referenced this pull request May 24, 2026
…ender

The Super Nova GLB (and any other GLB run through `gltf-transform
optimize` with defaults, including the Steam Controller GLB queued in
PR #6) ships with EXT_meshopt_compression marked as a *required*
glTF extension. Three.js's GLTFLoader supports the extension — but
only if you call `loader.setMeshoptDecoder(MeshoptDecoder)` first.
Without that, meshopt-compressed buffer views silently fail to
decode and the mesh renders as an empty axis-aligned bounding box,
which is what showed up as a featureless black rectangular block
when the auto-detect correctly swapped the overlay to load
gamesir-super-nova.glb.

Three changes wire it up:

  packages/visualizer/src/controller-overlay.js
    + import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'
    + loader.setMeshoptDecoder(MeshoptDecoder) at the top of the GLB
      load Promise (called per-load; Three's loader is idempotent here)

  apps/overlay/scripts/copy-three.js
    + add 'libs/meshopt_decoder.module.js' to the addons list so the
      vendored Three.js bundle that the no-bundler importmap serves
      includes it (matches the existing fflate vendor pattern)

The fix benefits *every* future GLB the lab ingests via the documented
`gltf-transform optimize` pipeline — Steam Controller, future Cyclone 2
model, etc. The alternative (skipping meshopt during optimize) would
ship 30-50% larger GLBs for everything; better to support the standard
extension once and reap the size benefit going forward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@petegordon petegordon merged commit a7e451c into main May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant