# 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](https://gltf-transform.dev/): 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: ```bash 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 ```bash node tools/preprocess.mjs [output.glb] [--draco-bits N] [--ktx2|--no-ktx2] ``` - Default output: `.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/) (`samples/*.optimized.glb`). - Pre-rendered WebP placeholders land in [`public/previews/`](../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/.webp` (animated, with alpha). **Status: to be implemented when a sample asset is available.** Two options: 1. **Blender headless CLI (preferred).** Camera parented to an empty at the model origin, Z axis 0→360° keyframed over N frames. Render frames: ```bash blender -b scene.blend -o //frame_### -f 1..N -F PNG ``` Assemble to animated WebP (alpha) or WebM VP9 via ffmpeg: ```bash ffmpeg -framerate 24 -i frame_%03d.png -loop 0 -plays 0 out.webp ``` 2. **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.