6.4 KiB
trmn/assets
- Architecture
- Location Scene Pipeline
- Blender Naming Conventions (procedural overrides)
- Blender Scene Setup
- Runtime API (
index.htmlThree.js block) - Debug Panel (
▸ scene debugin browser) - locations.js
- PRNG Offset Allocation (all streams)
- Conventions
- Gotchas
- Key Files
trmn — asset pipeline: SVG sprites + GLB objects + location scenes
Architecture
Three asset types, all served from $out/assets/ by Caddy:
- Sprites: SVG files in
assets/sprites/{eyes,nose,mouth}/, static/committed, no build step - 3D objects: GLB files in
assets/objects/, generated duringnix buildby Node.js scripts inscripts/ - Location scenes:
.blendfiles inassets/scenes/**are the source of truth; Blender exports GLBs at build time, then packed intoassets/scenes/scenes.pack(single binary, one browser fetch)
Face compositing at runtime: load SVG parts → draw onto OffscreenCanvas(128,128) → THREE.CanvasTexture.
Applied to the +z front face (index 4) of the head BoxGeometry via a material array.
Location Scene Pipeline
.blend files are the source of truth (edit in Blender, commit .blend, GLBs are build artifacts):
flake.nixbuildPhase:blender --background file.blend --python scripts/export-glb.py -- out.glbfor each.blendinassets/scenes/**scripts/pack-scenes.mjs glb_tmp assets/scenes/scenes.pack: validates all SCENES location IDs have a GLB (warn by default,STRICT_LOCATIONS=1to error), concatenates intoscenes.pack- Pack format:
[4-byte LE uint32: manifest len][JSON manifest][...GLB bytes] - Runtime:
loadPack()fetches once,loadLocationScene(id)slices + parses per scene, cached
34 unique location IDs (e.g. home/bedroom, office, school/classroom). Directory mirrors IDs: assets/scenes/home/bedroom.blend → ID home/bedroom. Full mapping in ASSETS.md.
Blender Naming Conventions (procedural overrides)
IF_<NAME>meshes: toggled visible/hidden at runtime byapplyLocationProps()Examples:IF_CRIB(age<3),IF_ADULT_BED(age≥13),IF_TV_LARGE(wealthLevel>70)WALLS,FLOOR: material color replaced at runtime with deterministic palette color- All other mesh names: always visible, untouched
Full IF_* table and authoring guide in ASSETS.md.
Blender Scene Setup
- Character is ~2 units tall; typical interior: 5–8 units wide, 3 units tall
- Camera: pos
(0.5, 1.6, 6.5), looks at(0, 1.0, 0), FOV 26°, aspect 2.39:1 - No lights in .blend files — runtime adds ambient + two directional lights
- Materials: Diffuse BSDF (flat color); Principled BSDF works but is overkill under Lambert
export_apply=Truein export script — modifiers applied; no armatures/animations- Poly budget: ~500 triangles/scene; outdoor scenes skip WALLS/FLOOR (no runtime error)
- Run
nix developto get a shell withblenderon PATH for authoring
Runtime API (index.html Three.js block)
loadPack(): called once at init; gracefully no-ops if pack not built yetloadLocationScene(locationId): returnsTHREE.Group(cloned from cache), ornullif not in packapplyLocationProps(threeScene, props): traverses scene, appliesIF_*visibility + WALLS/FLOOR colorsgenerateLocationProps(locationId, masterSeed, playerState, eventLog?)fromlocations.jswindow.__sceneLoader.loadAndShow(locationId, props): removes previous loc scene, loads + applies new one
Debug Panel (▸ scene debug in browser)
Collapsible <details> panel below the chat box. Controls:
- Location dropdown (all 34 IDs, populated from
SCENESat runtime) - Sliders: age, wealth, children, job tier, moves (home move count)
- sync from today: pulls actual simulated state from
window.__life - load scene: calls
window.__sceneLoader.loadAndShow(); shows[not in pack]if .blend missing - Live props display: wall/floor color swatches + hex, visible
IF_*mesh names
Props update on every slider move even without a loaded scene.
locations.js
ES module, imported by Three.js block and debug module. Exports generateLocationProps.
- Home: seeded from
makeRng(masterSeed, 200000 + moveIndex)where moveIndex = count ofmove_cityevents ≤ currentDay → new house each time player moves - Office: offset 300000; School: 301000; University/dorm: 302000; others: hash into 303000–399999
playerStatefields:{ age, wealthLevel, numChildren, jobTier, inCollege, retired, currentDay }
PRNG Offset Allocation (all streams)
| Range | Owner |
|---|---|
| -2 to -1 | Family layout + protagonist traits |
| 0 – 36,500 | Life simulation daily |
| 50,000+ | Schedule (per-day, offset 50000) |
| 200,000 + moveIndex | Home appearance (per house) |
| 300,000 | Workplace |
| 301,000 | School |
| 302,000 | University / dorm |
| 303,000–399,999 | Other locations (hashed from ID) |
Conventions
- All sprite SVGs are 128×128; composite with
drawImage(img, 0, 0, 128, 128) - Face part selection seeded from
window.__life.masterSeedvia inlinemulberry32in Three.js block - GLB generators: pure Node.js stdlib only (no npm), accept output path as arg
pkgs.blenderinnativeBuildInputs(build) anddevShells.buildInputs(nix developshell)
Gotchas
src = ./.only includes git-tracked files. Rungit add assets/scenes/ scripts/export-glb.py scripts/pack-scenes.mjs locations.jsbeforenix buildor they are silently excluded.BoxGeometrymaterial array face order:[+x, -x, +y, -y, +z, -z]→ index 4 is front face- GLB generator runs in Nix buildPhase and writes to build dir;
cp -r assets $out/assetspicks up generated files locations.jsimportsmakeRngfrom./life.js; served by Caddy alongside other JS modules- Blender headless needs
--backgroundflag; no X11 required in stdenvNoCC sandbox
Key Files
assets/sprites/eyes/eyes_{1,2}.svg— wide-open vs narrow/tired eyesassets/sprites/nose/nose_{1,2}.svg— dot vs nostrils noseassets/sprites/mouth/mouth_{1,2}.svg— smile vs neutral mouthassets/objects/book.glb— generated at build time; dark-red flat box 0.14×0.18×0.03assets/scenes/—.blendsource files, mirroring location ID hierarchyassets/scenes/scenes.pack— build artifact; binary pack of all location GLBsscripts/gen-book.mjs— pure Node.js GLB writer for book objectscripts/export-glb.py— Blender Python: exports active scene to GLBscripts/pack-scenes.mjs— validates + concatenates GLBs intoscenes.packlocations.js—generateLocationProps(locationId, masterSeed, playerState, eventLog?)ASSETS.md— full authoring guide: location→file map, IF_* table, Blender setup, workflow