Commit Graph

369 Commits

Author SHA1 Message Date
m3ultra
31a887bf75 Log Lane C landing, contract note and per-lane asks in THREADS
Also corrects the worktree-collision attribution: the checkout that moved HEAD
off lane/a was mine, not Lane D's.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:53:00 +10:00
m3ultra
302972cc6e Fill in Lane E selftest suite: assets verified in three.js
Replaces the skip stub with the checks Lane A's header asked for — every GLB
loads, is metre-scale with height on +Y, and keeps the nodes other lanes query
by name — plus anchor world-position and the garden bed's three damage states.

This catches what the Blender side structurally cannot: Blender exports
Z-up->Y-up and imports Y-up->Z-up, so a broken export_yup round-trips green.
A native glTF reader is the only thing that can prove the file.

GLTFLoader is imported dynamically on purpose. Three.js addons import the bare
specifier `three`, no page in the repo has an importmap yet, and selftest.html
turns an un-importable lane module into a hard FAIL — so a static import would
redden Lane A's merge gate over a harness gap rather than a real defect. It
skips with the fix instead, and lights up by itself once the importmap lands.
Verified behind a temporary probe first: 36/36 pass. Need logged in THREADS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:50:43 +10:00
m3ultra
11f27c493c Fix debris ground friction, cloud seam and yard coords
Three bugs the bench found once it was actually running a storm:

- Debris was glued to the floor. The scrape `v *= 0.86` ran every frame while
  grounded, which is 0.86^60 per second — it pinned a 9 kg crate at 0.7 m/s in
  a 19 m/s wind. Friction now applies on impact, with dt-scaled rolling
  friction while resting. A crate crosses the whole yard at ~6 m/s.
- Debris slid instead of tumbling: wind is horizontal, so once down there was
  no vertical force at all and it skated at constant height. Added tumbling
  lift that flips sign as it rolls — bins hop now.
- The cloud dome had a dead straight seam across the sky. The fbm claimed to
  tile and didn't; now each octave wraps at its own integer period.

Also: shelters and the bench now use Lane A's landed yard coords (t1 -9,2 /
t2 8,-2, gardenBed 1,2) instead of my guesses, so shelter tuning means
something.

Verified in-browser against a real storm: crate crosses the yard and the t=74
bin spawns from storm JSON; sail node shoved 0.32 m; a 14 kg bin at 20 m/s
knocks the player down and a tub drifting at 0.3 m/s correctly does not;
lightning peaks 0.88. Lane A's selftest: 37 pass / 3 skip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:50:41 +10:00
m3ultra
78c98aed64 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:43:06 +10:00
m3ultra
219dd55716 Log Lane E landing and PLAN3D asset-path corrections
PLAN3D §2's inventory was verified against the M1 Ultra, but we build on the
M3 Ultra, where several of those libraries are absent or moved. Records the
real paths and flags the gaps that block Lanes A and D, plus the node/anchor
contracts other lanes consume.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:42:40 +10:00
m3ultra
3815055678 Add generated yard, hardware and debris GLBs
16 generated assets plus the grass billboard atlas and the four debris models
copied verbatim from the 3D-STORE library (copies rule, §0). All meter-scale,
Y-up, and far under the 15k tri budget — garden_bed is heaviest at 2,580.

Regeneration is byte-deterministic: two consecutive runs produce identical
files, so re-running the factory causes no churn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:39:52 +10:00
m3ultra
d8a017ad7d Add deterministic Blender yard-asset factory
One script regenerates every nature/hardware asset in PLAN3D §5-E, following
the house idiom from 3D-STORE/racks_to_glb.py: reset per asset, build under a
root empty at the origin, join by group, stamp props, export Y-up GLB.

Groups are joined per sway-unit rather than per-asset, so trees keep trunk and
canopy_* as separate nodes for Lane A to animate. Paths resolve from __file__
instead of a hardcoded home dir, since the library lives elsewhere on this box.

Verification re-imports each exported GLB from disk and asserts dims, tri
budget, and node-name survival, then renders it against the 1.7 m ref capsule.
Checking the file rather than the in-memory scene is what makes it a real test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:39:52 +10:00
m3ultra
383471d0f5 Add debris, skyfx and the Lane C bench; align to landed contracts
debris.js: hand-rolled kinematic tumble, drag ∝ speed² so the gust that
spikes a corner is the one that launches the neighbour's bin. Ground bounce
via world.heightAt (contracts.js documents it as ours), sphere-vs-player
knockdown reported to Lane D, sphere-vs-sail-node impulse duck-typed so it
lights up when Lane B exposes nodes and stays silent until then.

skyfx.js: instanced rain that wraps around the camera rather than respawning,
storm sky + procedural cloud dome, lightning, and synthesized WebAudio layers
(wind bed, howl, rain, gust whoosh on the telegraph, rope creak off the worst
corner, flog when one blows). It modulates Lane A's lights and hands them back
on dispose() rather than owning them.

weather_demo.html: graybox bench to drive all three before M0 — mock sail,
storm scrub, 4x, throw-a-crate.

Aligned to contracts.js now that it has landed: relative three imports (there
is no importmap), contracts' rng() instead of a local copy, and storm paths
resolved off import.meta.url — server.py serves the repo root, so an absolute
/world/... would have 404'd at integration.

storm_02: fix the southerly change. contracts.js puts north at -Z and the wind
vector blows toward (cos d, sin d), so the old swing to +2.6 blew toward due
south — a northerly wearing a southerly's name. Now slews to -1.35: a SSW
buster off the open side of the yard, into the house.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:38:21 +10:00
m3ultra
84f647a90c Add wind field, storm timelines and weather selftest
Implements the contracts.js wind surface (PLAN3D §4): sample(pos,t) and
gustTelegraph(t), plus storm defs as data.

The prototype scheduled gusts by integrating (wind.gustT += dt). We can't:
sample(pos,t) is called by sail/player/debris/rain at arbitrary t, so gusts
are precomputed into a timeline from a seeded PRNG at load and read from t.
Envelope shape is a faithful port — telegraph 1.5s / ramp 0.8s / hold 1.7s /
fade 1.0s.

Maths lives in weather.core.js with zero imports (no THREE, no DOM, no
Date.now), so the determinism rule is structural and the suite runs in node
without waiting on Lane A's M0.

storm_01_gentle peaks at 11 m/s; storm_02_wildnight sustains 20 m/s and gusts
to 32 (BOM 'destructive'), with the southerly change landing just before the
worst of it — the corners that were slack all storm are the ones that cop it.

15 asserts green: telegraph lead >=1.2s, wind continuity in time and space,
storm JSON validator (incl. 14 deliberately-broken defs it must reject),
determinism, sample-order independence, tree wind shadow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:38:12 +10:00
m3ultra
8d76340f49 Log the shared-worktree collision in THREADS
Lane D is working in the shared ~/Documents/shades checkout rather than its own
clone, so its git checkout moved HEAD off lane/a mid-session and Lane A's M0
commits landed straight on main. No damage this time, but two agents sharing one
HEAD and one index will eventually eat someone's uncommitted work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:36:44 +10:00
m3ultra
868ea5699d Correct PLAN3D asset inventory; log M0 contract additions
§2's paths were substantially wrong. Re-verified against the filesystem:
3D-STORE is at ~/Documents/Destroyulater/3D-STORE (its clean_glbs debris set is
real, just relocated), while 3D=models/, character_kit/, mixamo-fetch/ and FBX/
do not exist on this box at all. That last group is Lane D's whole animation
pipeline, so it's flagged loudly rather than quietly patched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:33:42 +10:00
m3ultra
1049dece24 Add selftest harness with one suite per lane
selftest.html only imports; each lane owns js/tests/<letter>.test.js. Five lanes
sharing one selftest.html would be the single guaranteed merge conflict in the
repo, so the stubs are pre-created with each lane's PLAN3D asserts written into
the header.

No rAF anywhere: it's throttled in a hidden tab, so a rAF-driven selftest would
pass only while you watch it. fixedLoop() drives time instead.

Lane A's suite covers the §5-A acceptance criteria, including "camera never
clips through house" — stated as the underlying invariant (no solid between the
player's head and the camera) so it survives Lane E swapping the house GLB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:33:42 +10:00
m3ultra
a41328a219 Add graybox yard, third-person camera and game shell
The yard's meshes are placeholders; its layout is not. Anchor positions, ground
heights, the garden rect and the sun direction are what the other lanes build
against, so Lane E's GLBs drop into named slots without anyone re-deriving them.

Sun sits in the north (Australian yard), which puts the house's yard-facing wall
in permanent backlight. That's correct, but the fascia line carries three of the
seven anchors, so sky fill is turned up to keep it readable rather than a void.

Camera collision resolves toward the geometry, not away from it: clamping up to
a comfort distance after the raycast is what put the camera 17cm inside the
north wall. Ground is handled by heightAt() instead of a raycast — exact, can't
be tunnelled, and free.

main.js runs a fixed-dt accumulator so sim modules only ever see FIXED_DT. That
is what lets selftest fast-forward a 90s storm and get the numbers the player
got. Nothing auto-runs on import; index.html calls boot().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:33:42 +10:00
m3ultra
ae6e444996 Add contracts.js: the shared integration spine
Types, tuning constants ported from the prototype, a seeded PRNG and Emitter,
plus checkContract() — the tripwire that catches a lane drifting from its
interface at merge time instead of three lanes later.

createStubWind() ports the prototype's gust shape (telegraph 1.5s, ramp 0.8s,
hold 1.7s, fade 1.0s) so B and D can develop before Lane C lands weather.js.
Its schedule is precomputed at construction, which keeps sample() pure in t —
the selftest samples out of order and must get the same answer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:33:42 +10:00
m3ultra
664e378578 Vendor three.js r175, add stdlib dev server and launch config
server.py serves the repo root rather than web/, so the 2D prototype stays
reachable next to the 3D port — you want to flip between the reference
implementation and the thing you're porting without stopping the server.
--selfcheck verifies the tree and prints URLs; no node, no network.

three r175 is copied in from 90sDJsim rather than referenced, so the game has
no runtime dependency on a sibling checkout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:33:42 +10:00
m3ultra
027fb99828 Lane D: player rig assets — ped copy + anim-only clip pack
Character is a byte copy of 90sDJsim's man_casual_01 ped (already metre-scale,
head bone 1.75 m, bind pose proven in that game). Clips ride beside it in an
anim-only carrier built from the FBX libraries on the M1 Ultra.

The peds cannot round-trip through Blender: they encode metre scale as a node
scale of 0.01 on mixamorig*:Hips with child bones in centimetres, and Blender
bones carry no rest scale, so the glTF importer silently drops it — the rig is
exploded on import (LeftToe_End 96 m below the floor) before any merge happens.
So merging clips onto the ped (PLAN3D 5-D.1) is not viable; we ship the ped
untouched and retarget rotation-only clips onto it at runtime, the same shape
90sDJsim already uses for peds/idle.glb + peds/walk.glb.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:27:04 +10:00
m3ultra
8338c0fa99 Add 3D execution plan, lane prompts, and THREADS log
PLAN3D.md: locked stack (vendored three.js r175 + stdlib server, DJsim
house style), verified asset inventory on the M1 Ultra, module contracts,
five parallel lanes (world/sail/weather/player/assets) with acceptance
criteria and integration order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:05:57 +10:00
m3ultra
06404d3505 Add side-systems to design doc + playable sail-storm prototype
Prototype: spring-mass sail, quadratic wind with gust telegraphs and a
mid-storm wind change, per-corner hardware ratings with weakest-link
cascade failures, garden coverage scoring, and a runnable repair loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 20:55:31 +10:00
m3ultra
430a58061b Add design brainstorm: landscaper service, sails, drainage, seasons
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 20:38:59 +10:00