- Shared pipeline extracted to src/pipeline.js (browser + node run identical logic) - Draco: vendored decoder WASM in public/draco/, lazy-registered by peeking the GLB JSON chunk before read (gltf-transform fails cryptically on a mid-read missing dependency); DRACOLoader wired into the viewer; extension stripped on write so output is standard GLB - bin/cli.js: NodeIO + draco3dgltf + sharp textures, presets/flag overrides, batch with --outdir, per-file report Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
60 lines
2.8 KiB
Markdown
60 lines
2.8 KiB
Markdown
# 🗜️ SHRINKGOD — local GLB optimizer
|
||
|
||
Drop in a heavy GLB, pick a target, compare original vs optimized side-by-side (synced camera, animation playback, wireframe), export. **Everything runs client-side in the browser** — gltf-transform + meshoptimizer WASM. No uploads, files never leave the machine.
|
||
|
||
## Run
|
||
|
||
```bash
|
||
npm install
|
||
npm run dev # http://localhost:5173
|
||
npm run build # static build in dist/ — host anywhere, still fully local
|
||
```
|
||
|
||
Dev test hook: `?test=<file-in-public>.glb&run=1` auto-loads and optimizes.
|
||
|
||
## CLI (batch / farm use)
|
||
|
||
Same pipeline, headless — sharp for textures (faster than the browser canvas path):
|
||
|
||
```bash
|
||
node bin/cli.js model.glb # -> model.opt.glb (balanced)
|
||
node bin/cli.js --preset crunch *.glb --outdir out/
|
||
node bin/cli.js model.glb --ratio 0.15 --max-tex 512 -o small.glb
|
||
node bin/cli.js --help # all flags
|
||
```
|
||
|
||
`npm link` (or install from the repo) puts a `shrinkgod` command on PATH. Rig protection applies identically.
|
||
|
||
## Pipeline
|
||
|
||
dedup → flatten+join (static models only) → weld → per-primitive meshopt simplify → animation resample → prune → texture resize/re-encode → quantize → (optional) meshopt compression.
|
||
|
||
### Rig protection (automatic)
|
||
- Skinned meshes never go below **50% kept geometry** and error is capped at 0.5% — vertex collapse near joints wrecks skin weights in motion.
|
||
- Morph-target (blendshape) primitives are left untouched.
|
||
- Mesh joining is disabled for rigged/animated models (would break node animation).
|
||
- Best results: **optimize before rigging** — static geometry can be crunched far harder.
|
||
|
||
### Textures
|
||
- Longest side clamped to the chosen max (canvas re-encode).
|
||
- Color/emissive maps optionally converted to WebP (quality slider); normal/roughness/occlusion maps always stay lossless PNG.
|
||
- Re-encode is skipped if it wouldn't shrink the file.
|
||
|
||
### Presets
|
||
| | geometry kept | error | max texture | webp |
|
||
|---|---|---|---|---|
|
||
| Light | 50% | 0.5% | 2048 | no |
|
||
| Balanced | 25% | 1% | 1024 | yes |
|
||
| Crunch | 10% | 5% | 512 | yes |
|
||
|
||
## Draco input
|
||
Draco-compressed GLBs are decoded on load (decoder WASM is vendored in `public/draco/` — still fully local, no CDN) and written back out as standard uncompressed GLB. Draco *output* is intentionally not supported; use meshopt compression for web targets instead.
|
||
|
||
## Known limits
|
||
- Meshopt compression output (`EXT_meshopt_compression`) reads fine in three.js but **not in Blender** — leave it off for assets going back into a DCC.
|
||
- KTX2/basis textures pass through untouched.
|
||
|
||
## Bench (M5 MacBook Pro, Balanced preset)
|
||
- 15.8 MB photogrammetry room scan (151k tris, 13 MB textures) → **1.5 MB** (−90%) in 5 s.
|
||
- 3.5 MB rigged character (18.6k tris, 20 clips) → **2.7 MB** (−24%), clips + skinning intact (rig clamp limits geometry reduction by design).
|