HardYards/THREADS.md
m3ultra 181dc82f1f Bake joined-node rotations so bounding boxes are tight
Verifying the GLBs in three.js (not just Blender) showed four assets reporting
inflated bounds: tramp_01 came back 3.29 x 1.27 m against a true 2.96 x 0.78.

Box3.setFromObject expands each mesh's LOCAL box by the world matrix, so a node
carrying a rotation over-reports — the same trap Blender's obj.bound_box sets,
and what three uses for frustum culling. Joined nodes inherited parts[0]'s
rotation, which for an arc is half a segment step off-axis.

Applying rotation at join time makes every local box axis-aligned, so the
default Box3 path is now correct for consumers and culling is tight. World
geometry is unchanged; the exported dims are identical.

Adds tools/assetcheck/, the three.js harness that caught this. It's the only
check that can: a Blender round-trip exports Z-up->Y-up and imports Y-up->Z-up,
so a broken export_yup flips back and passes green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:38:32 +10:00

22 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# THREADS — append-only lane log
One line per landed feature, contract change, or open question.
Format: `[lane letter] YYYY-MM-DD — note`
[.] 2026-07-16 — repo seeded: DESIGN.md (canon), PLAN3D.md (build plan), prototype/ (2D reference, do not modify)
[E] 2026-07-16 — ⚠️ PLAN3D §2's asset paths do not exist on this box. We are on m3ultra.local (Apple M3 Ultra); the plan was written against the M1 Ultra (johnking@100.91.239.7), so its "verified" inventory is that box's, not ours. Corrections below — read before you go hunting for a library.
[E] 2026-07-16 — ✅ present here: 90sDJsim (~/Documents/90sDJsim/ — vendored three r175, web/world/models/peds/ full fleet, DEVMANUAL.md). 3D-STORE IS here but at ~/Documents/Destroyulater/3D-STORE/ (NOT ~/Documents/3D-STORE) — has build_booth_room_v23.py, racks_to_glb.py, clean_glbs/. Blender 5.1.2.
[E] 2026-07-16 — ❌ MISSING here entirely, under any name: ~/Documents/character_kit/, ~/Documents/FBX/, ~/Documents/3D=models/, ~/Documents/mixamo-fetch/ (there is an unrelated ~/Documents/mixamo_upload/). Impact: Lane D's whole anim pipeline (character_kit rigs + FBX clips + mixamo-fetch wishlist) has no source on this machine, and Lane A's `3D=models/street-furniture/building_shell_01.glb` does not exist — use Lane E's house_yardside_v1.glb instead. Either those libs live only on the M1 Ultra and need syncing over Tailscale, or the inventory was aspirational. Flagging, not fixing — not my files (§0).
[E] 2026-07-16 — landed tools/blender/build_yard_assets.py: ONE deterministic script → 16 GLBs + grass atlas. Run `blender -b -P tools/blender/build_yard_assets.py` (flags: --only <name> / --no-verify / --no-debris). Proven, not asserted: 17/17 output files byte-identical across two runs; every GLB re-imported from disk and checked for dims-in-range, tri budget, and node-name survival; contact_sheet.png renders each against the 1.7 m ref capsule. Heaviest asset is garden_bed at 2,580 tris — all far under the 15 k budget. Machine-readable manifest: tools/blender/asset_report.json.
[E] 2026-07-16 — contract, trees: `trunk` (trunk+branches, rigid) and `canopy_01..03` are SEPARATE nodes — Lane A, sway the canopies only. `branch_anchor_01..03` are empties carrying anchor_type="tree" + rating_hint (thicker limb = higher) for world.anchors / Lane B.
[E] 2026-07-16 — contract, house_yardside: `fascia_anchor_01..03` empties carry rating_hint=0.35 and collateral="gutter", and the gutter node carries collateral_of="fascia" — DESIGN.md's "the fascia board is a lie" wired as data, so ripping it can take the gutter with it. Facade only (9.20 × 1.05 × 2.90 m), no interior.
[E] 2026-07-16 — contract, sail_post: exported VERTICAL with `rake_pivot` at the footing and `top_anchor` at the head. DESIGN.md says rake the post away from the load — that's a player decision, so rake is a runtime rotation about rake_pivot, NOT baked geometry. Post is 4.0 m. Also: shed_table has `pickup_anchor`, ladder_01 has `ladder_base`/`ladder_top`, gate has `hinge_axis`.
[E] 2026-07-16 — contract, hardware: shackle/carabiner/turnbuckle each keep their failure part as its own node — `pin` (unscrews then shears), `gate` (flutters open), `body` (thread strips) — with failure_mode stamped as a custom prop, so a break anim moves just that piece. They read at 1 m; see contact sheet.
[E] 2026-07-16 — contract, garden_bed: all 3 damage states ship in ONE glb as sibling nodes `plants_full` / `plants_tattered` / `plants_dead` (full visible, rest hide_render). Lane A: toggle .visible, don't reload — instant swap, no pop-in. Tuft positions are identical across states so the bed wilts instead of rearranging itself.
[E] 2026-07-16 — debris in web/world/models/debris/, copied verbatim from the library and scale-checked: BlueCrate_v2 0.36×0.36×0.29, BlackTub_v2 + WhiteTub_v2 0.36×0.54×0.20, WoodenBin_v2 0.35×0.36×0.31 m — all plausible real-world sizes. Plus tramp_01_v1.glb (2.96×2.96×0.78, mass_hint 45) because every Australian storm produces one airborne trampoline. NOTE: §0's *_v1.glb rule beats §5-E's "tramp_01.glb" spelling — Lane C, glob the dir rather than hardcoding names.
[E] 2026-07-16 — grass is a texture, not geometry (§5-E item 9): web/world/models/textures/grass_atlas.png, 512², 2×2 tufts, alpha. Lane A instances billboards from it.
[E] 2026-07-16 — ⚠️ FOR LANE A + LANE C, bounding boxes: `THREE.Box3.setFromObject(obj)` expands each mesh's LOCAL box by the world matrix, so a node carrying a rotation reports an inflated box — and that's what three uses for frustum culling. Blender's obj.bound_box has the identical trap (it cost me an hour of chasing phantom "failures"). Fixed at source: join_group() now bakes rotation into the vertices, so every node's local box is axis-aligned and tight. Before the fix three reported tramp_01 as 3.29×1.27 m vs its true 2.96×0.78. You can use the default Box3 safely now — but if you ever measure geometry yourself, measure VERTICES, not bound_box corners.
[E] 2026-07-16 — landed tools/assetcheck/ (README + assets_in_three.html): loads every GLB through three.js r175 GLTFLoader and asserts Y-up, node survival, and that branch_anchor_01 lands at a sane +Y world position. This exists because the Blender round-trip CANNOT catch an axis bug — it exports Z-up→Y-up and imports Y-up→Z-up, so a broken export_yup flips back and passes. Currently green: 16/16 + anchors, `branch_anchor_01` at (-0.96, 3.64, -1.46). Lane A: once server.py + vendor/ land, this wants to live next to selftest.html — happy for you to fold it in, it reads expectations from asset_report.json so it stays in sync by itself.
[E] 2026-07-16 — added a root .gitignore (thumbs/, *.blend, *.fbx per §0). Unowned file — Lane A, fold it into yours if you'd rather own it.
[E] 2026-07-16 — open q for Lane A: does house_yardside at 9.20 m wide sit right on the north edge of a 30×20 m yard, or do you want a wider/second variant? Cheap to re-cut — it's one function in the script.