Three.js viewer supporting glb/gltf/obj/fbx/dae/ifc/ply via both server (SSR) and drag&drop (CSR) paths. - Streaming OBJ parser (src/viewer/objStream.ts) for files past the V8 max string length (>~1GB text) that OBJLoader can't handle; indexed geometry, per-vertex color from MTL Kd, float64 recenter baked in. - In-viewer float64 recenter (objRecenter.ts) for huge CAD/survey coordinates (~1e8) so float32 vertex buffers keep precision (no cracked faces). - Z-up right-handed world; Y-up formats rotated on load. - OBJ+MTL+texture drag&drop (LoadingManager URL-modifier maps dropped images). - OrbitControls ground-plane panning (road/rail alignment workflow). - UI: Zoom Fit, perspective/orthographic toggle, feature-edge outline. - DoubleSide for CAD OBJ; PLY mesh + point-cloud support. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tools/ — Offline asset pipeline
Offline tooling for preparing 3D assets served by the hmwebviewer. Nothing here ships to the browser at runtime; these produce the optimized assets and pre-rendered placeholders that the viewer consumes.
preprocess.mjs — Draco + KTX2 compression
Compresses a .glb/.gltf into a single self-contained .optimized.glb using
gltf-transform: Draco for geometry, KTX2/Basis
Universal for textures, in one pass. Output loads directly in the viewer's
GLTFLoader (with DRACOLoader + KTX2Loader wired on the shared loader).
Install (once)
These dev dependencies are not in package.json yet. Run before first use:
npm install -D @gltf-transform/core @gltf-transform/functions \
@gltf-transform/extensions @gltf-transform/cli
The CLI package provides the KTX2 encoder wiring. gltf-transform fetches the platform basis encoder automatically on first KTX2 run (network needed once).
Usage
node tools/preprocess.mjs <input.glb> [output.glb] [--draco-bits N] [--ktx2|--no-ktx2]
- Default output:
<input>.optimized.glb(e.g.model.glb→model.optimized.glb). --draco-bits N— position quantization bits, 8..16 (default 14). Lower = smaller file, lossier geometry. Tune per asset.--ktx2/--no-ktx2— texture encoding toggle (default on).
Prints before/after byte sizes and reduction %.
Outputs
- Compressed GLBs land in
samples/(samples/*.optimized.glb). - Pre-rendered WebP placeholders land in
public/previews/(*.webp) — see the pre-render section below.
If a required package is missing at runtime, the script prints the install command above and exits non-zero (no silent skip).
Sample assets
Place source .glb files under samples/ (suggested split: small / medium /
large). Compress with preprocess.mjs and commit the .optimized.glb outputs.
Samples are not committed yet — to be added when a sample asset is available.
Pre-render pipeline (360° animated WebP placeholder)
For Path A (server asset) the server ships a pre-rendered 360° turntable so the
page shows motion instantly while the real model decodes in the background.
Output: public/previews/<asset>.webp (animated, with alpha).
Status: to be implemented when a sample asset is available. Two options:
-
Blender headless CLI (preferred). Camera parented to an empty at the model origin, Z axis 0→360° keyframed over N frames. Render frames:
blender -b scene.blend -o //frame_### -f 1..N -F PNGAssemble to animated WebP (alpha) or WebM VP9 via ffmpeg:
ffmpeg -framerate 24 -i frame_%03d.png -loop 0 -plays 0 out.webp -
Puppeteer + headless three.js (fallback). Spin a minimal three.js page, rotate the model, and call
page.screenshot({ type: 'webp' })per rotation step. Stitch frames into an animated WebP.
Both options need a committed sample asset first; blocked on P4-2.