Files

7.1 KiB

PLAN.md — hmwebviewer task breakdown

Agents: read this + PROGRESS.md on start. Pick the next todo task whose depends_on are all done. Set it to in_progress with your name before working. Move to done only when acceptance criteria pass and you updated PROGRESS.md.

Status legend

todo · in_progress(@agent) · blocked(reason) · done · skipped(reason)


Phase 0 — Bootstrap

ID Task Agent Status Depends Acceptance
P0-1 Scaffold Vite + TS project, install three + types setup done (build-verify pending) npm run dev serves blank page; npm run build exits 0
P0-2 Resolve Draco + KTX2 decoder WASM from Three.js loader imports setup done (Vite hashed assets) P0-1 production/subpath decoder requests resolve; console clean
P0-3 Basic WebGLRenderer scene (camera, lights, resize loop) viewer-core done (build-verify pending) P0-1 canvas renders a test cube; resizes on window resize

Phase 1 — Core viewer (parallelizable)

ID Task Agent Status Depends Acceptance
P1-1 Singleton loaders (GLTFLoader + DRACOLoader + KTX2Loader, ONE each) viewer-core done (build-verify pending) P0-2 exported factory; reused across loads; no double-instance
P1-2 loadServerAsset(url) with progress bar wiring viewer-core done (build-verify pending) P1-1 loads sample Draco GLB, progress % updates, scene populated
P1-3 loadLocalFile(blob) — createObjectURL + revoke after load viewer-core done (build-verify pending) P1-1 drop GLB loads; URL revoked post-load; memory stable across N drops
P1-4 OrbitControls + camera framing (fit model to view) viewer-core done (build-verify pending) P1-2 model auto-framed; rotate/zoom works

Phase 2 — Drag & Drop (parallelizable with Phase 1)

ID Task Agent Status Depends Acceptance
P2-1 Dropzone UI + HTML5 DnD event capture, file type/size validation dnd-handler done (build-verify pending) P0-1 invalid files rejected with message; valid files accepted
P2-2 Wire dropzone → loadLocalFile dnd-handler done (build-verify pending) P1-3, P2-1 dropped file appears in scene

Phase 3 — SSR hydration (serial, depends on core)

ID Task Agent Status Depends Acceptance
P3-1 WebP placeholder element + CSS opacity transition scaffolding hydration done P1-2 placeholder shows, fades on command
P3-2 executeHydration() — coordinate WebGL-ready AND model-loaded → fade hydration done P3-1 no empty-canvas flash; race handled; transition 0.5s
P3-3 Toggle UI states (progress bar show/hide, loading→ready) hydration done P3-2 UI reflects each load phase

Phase 4 — Asset optimization pipeline (offline, parallelizable)

ID Task Agent Status Depends Acceptance
P4-1 tools/preprocess.mjs — gltf-transform Draco+KTX2 wrapper asset-pipeline done input GLB → output compressed GLB, size reduced, loads in viewer
P4-2 Sample asset set (small/medium/large GLB) for testing asset-pipeline done Box(1.6K)/Duck(118K)/Avocado(7.9M) + Duck.optimized(Draco) under samples/ + public/samples/
P4-3 Pre-render pipeline (Blender CLI OR Puppeteer) → animated WebP asset-pipeline done P4-1 Duck → 24-frame animated WebP (54KB) at public/previews/Duck.webp, wired into #preview hydration

Phase 5 — Hardening (serial)

ID Task Agent Status Depends Acceptance
P5-1 Dispose pattern: geometry/material/texture + revoke on teardown reviewer done P1, P2 no leaks after 10 load/unload cycles (DevTools heap)
P5-2 Error handling: bad file, decode fail, WebGL unsupported reviewer done P1-3, P2-1 graceful message, no uncaught exception
P5-3 Perf smoke: load each sample, record time, assert < 3s perceived reviewer done P4-2 timings logged in PROGRESS.md
P5-4 Full review pass (correctness + simplification) reviewer done all /review clean; no high-severity findings

Phase 6 — Multi-format loaders (OBJ/FBX/DAE/IFC), SSR+CSR — via dynamic workflow multiformat-viewer

ID Task Agent Status Depends Acceptance
P6-1 Unified src/viewer/modelLoader.tsloadModel(url) dispatch by ext, normalize each loader to Object3D viewer-core done P1-1 glb/gltf/obj/fbx/dae/ifc all resolve to scene-addable Object3D; GLB path unchanged
P6-2 OBJ/FBX/DAE via three example loaders, lazy dynamic-import (separate Vite chunks) viewer-core done P6-1 OBJLoader/FBXLoader/ColladaLoader code-split; load samples
P6-3 IFC via web-ifc IfcAPI (single-thread wasm /web-ifc/, no COOP/COEP) — StreamAllMeshes → BufferGeometry viewer-core done P6-1 Cube.ifc loads; geom.delete + CloseModel (no wasm leak)
P6-4 CSR: dropzone accepts all 6 exts via extOf; SSR: loadServerAsset + main.ts preview-base route any ext viewer-core+dnd done P6-1, P2-1 drop any of 6 loads; ?model= + /previews/<base>.webp works per format
P6-5 Samples + 360° WebP previews per format (Cube.obj/dae/fbx/ifc) asset-pipeline done P6-1, P4-3 4 samples in samples/ + public/samples/; 4 animated WebP (24 frames) in public/previews/

Phase 7 — On-screen FPS + adaptive quality (<60fps → optimize)

ID Task Agent Status Depends Acceptance
P7-1 src/ui/fps.ts — on-screen FPS overlay (EMA, throttled DOM, color-coded) viewer-core done P0-3 FPS readout visible; green≥60/orange/red
P7-2 src/viewer/adaptiveQuality.ts — pixelRatio tier ladder + hysteresis; step down when sustained <60, recover with headroom viewer-core done P7-1 tier steps on sustained <60fps; no oscillation; logs tier change
P7-3 Wire fps.sample + adaptive.update into ThreeDViewer.animate viewer-core done P7-1, P7-2 both fed each frame; rotateTo (prerender) unaffected

Parallelization map

  • Wave 1 (after P0): P1-1 (core) then forks → P1-2/P1-3/P1-4 + P2-1 + P4-1/P4-2 can proceed in parallel.
  • Wave 2: P3-* depends on core; P2-2 depends on P1-3+P2-1.
  • Wave 3: P5-* after functional work lands.

Independent agents that may run concurrently: viewer-core, dnd-handler, asset-pipeline. hydration waits on viewer-core. reviewer runs last + on-demand.

Validated extras (post-PLAN)

  • KTX2 runtime decode — KTX2Loader + DRACOLoader together decode the Khronos ABeautifulGame KTX2+Draco GLB (11.5MB) in 626ms perceived (perf-smoke). Runtime path confirmed.
  • KTX2 production encoding ⏸ — needs KTX-Software (toktx) installed; then gltf-transform etc1s|uastc. Not on this box. Documented in tools/preprocess.mjs header + user memory.