scena3d 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,29 +54,58 @@ game.onUpdate(() => { // ← SC
54
54
  });
55
55
  ```
56
56
 
57
- Neither library imports the other — the obstacle shape (`{ center, radius }`) is structural. Run the full demo (`npm run dev`, expects a sibling `../gama` clone): terrain, forest, a lamp-lit camp, wardens patrolling between the trees, and a flock overhead.
57
+ Neither library imports the other — the obstacle shape (`{ center, radius }`) is structural. Run the full demo (`npm run dev` gama3d comes from npm): a day-night cycle over terrain, lakes, a windblown forest, a seeded hamlet whose windows ignite at dusk, and a dirt road whose single authored curve is at once the visual ribbon, the scatter keep-out, and the wardens' patrol route. Append `?t=0.85` to freeze the time of day.
58
+
59
+ ## A world as JSON
60
+
61
+ Everything above can also be declared instead of coded — a `SceneManifest` is plain data (store it, diff it, send it over the network), and `buildScene` applies all the cross-feature wiring automatically: scatters stay ashore, off roads and out of the village; the village avoids water and paths; its lamps and windows feed the day cycle.
62
+
63
+ ```ts
64
+ const world = buildScene({
65
+ seed: 18,
66
+ palette: 'autumn',
67
+ terrain: { size: 90, amplitude: 5 },
68
+ water: { level: 0.25 },
69
+ dayCycle: { dayLength: 120 },
70
+ paths: [{ points: roadPoints, loop: true }],
71
+ village: { radius: 9, houses: 5 },
72
+ scatters: [{ density: 0.05, items: [{ type: 'tree', weight: 4 }, { type: 'rock' }],
73
+ lod: { distance: 34 } }], // far tiles collapse to cones
74
+ }, scene);
75
+ game.onUpdate((t) => world.update(t.delta));
76
+ ```
77
+
78
+ Run it: `npm run dev:manifest`. For handcrafted interiors there are kits — `assembleKit(['####', '#.S#', '#..D', '####'])` turns an ASCII map into snapped walls, floors, doorways, torches, spawn points and obstacles. And for levels authored in Blender/glTF, `extractMarkers(gltf.scene)` reads `spawn_*` / `route_*_0` / `obstacle_*` / `keepout_*` empties into spawns, ordered patrol routes and steering/scatter metadata.
58
79
 
59
80
  ## API
60
81
 
61
82
  | Area | Exports |
62
83
  |---|---|
63
- | Props | `createTree`, `createRock`, `createCrate`, `createFence`, `createLamp` — each returns `{ object, obstacleRadius }` |
64
- | Environment | `createTerrain` (with `heightAt(x, z)`), `createSky`, `createLightingRig('day' \| 'golden-hour' \| 'overcast' \| 'night')`, `applyFog('clear' \| 'haze' \| 'thick' \| 'eerie')` |
84
+ | Props | `createTree`, `createRock`, `createCrate`, `createFence`, `createLamp`, `createBush`, `createGrassTuft`, `createHouse`, `createTower`, `createWell`, `createRuin` — each returns `{ object, obstacleRadius }` |
85
+ | Environment | `createTerrain` (with `heightAt(x, z)` and `waterLevel` sand bands), `createSky`, `createLightingRig(...)`, `applyFog(...)`, `createWater` + `aboveWater` mask, `createDayCycle` (one `timeOfDay` drives sun/sky/fog/lamps), `applyWind` (vegetation sway), `createPath` (ribbon + patrol route + keep-out from one curve) |
65
86
  | Scattering | `scatter({ seed, area, surface, density \| count, items, mask, minSpacing, keepOut })` → `{ group, placements, obstacles, count }` |
87
+ | Generators | `createVillage({ seed, center, radius, houses, surface, mask })` → `{ group, props, obstacles, lamps, keepOut }` — a hamlet whose windows and lamps hand straight to `createDayCycle`, buildings to `ObstacleAvoidance`, clearing to `scatter` |
88
+ | Kits | `KIT_UNIT`, `assembleKit(asciiRows)` → `{ group, obstacles, spawns, torches, floorAt }` — grid-snapped walls/floors/doorways as two InstancedMeshes |
89
+ | Scene assembly | `buildScene(manifest, scene?)` → a whole wired world from plain JSON; `extractMarkers(root)` → `{ spawns, routes, obstacles, keepOut }` from naming conventions |
66
90
  | Core | `Rng` (seeded), `valueNoise2`/`fractalNoise2`, `PALETTES`, `collectObstacles` |
67
91
 
68
- Scatter placement uses density noise for natural clumping and clearings, a spatial hash for minimum spacing, and per-item visual variants; rendering merges everything into `InstancedMesh`es (one draw call per prop part).
92
+ Scatter placement uses density noise for natural clumping and clearings, a spatial hash for minimum spacing, and per-item visual variants; rendering merges everything into `InstancedMesh`es (one draw call per prop part). Opt into `lod: { distance, tileSize }` and placements bucket into tiles that swap to each item's `createFar` variant beyond the distance (10% hysteresis; call `result.update(camera)` each frame).
69
93
 
70
94
  ## Roadmap
71
95
 
72
96
  - [x] Seeded props with obstacle metadata (tree, rock, crate, fence, lamp)
73
97
  - [x] Instanced `scatter()` with masks, keep-out, spacing and clumping
74
98
  - [x] Noise terrain with exact `heightAt` and height/slope color bands
75
- - [x] Sky dome, lighting rigs, fog presets, three theme palettes
76
- - [ ] Kits: dungeon/village pieces with shared snap dimensions
77
- - [ ] Declarative scene manifests (JSON scene), Blender marker conventions (`spawn_*`, `route_*`, `nav_*`)
78
- - [ ] Water, day-night cycle driving sun + sky together
79
- - [ ] Buildings/ruins generators; LOD tiers for scatter
99
+ - [x] Sky dome, lighting rigs, fog presets, four theme palettes (incl. `winter`)
100
+ - [x] Water with shoreline masks; terrain sand bands
101
+ - [x] Day-night cycle: one `timeOfDay` drives sun, sky, fog and lamps igniting at dusk
102
+ - [x] Wind sway on scattered vegetation (per-instance phase)
103
+ - [x] Paths: one curve = visual ribbon + scatter keep-out + GAMA patrol route
104
+ - [x] Seed-stability snapshot tests (output frozen within a minor version)
105
+ - [x] Buildings (house, watchtower, well) and ruins; `createVillage` hamlet generator with the full gameplay handshake
106
+ - [x] Kits: ASCII maps → grid-snapped dungeon/compound pieces (`assembleKit`)
107
+ - [x] Declarative scene manifests (JSON → scene) and Blender marker conventions (`spawn_*`, `route_*`, `obstacle_*`, `keepout_*`)
108
+ - [x] LOD tiles for scatter (`createFar` variants, hysteresis)
80
109
  - [ ] CC0 asset-pack adapters (Kenney/Quaternius) and a KTX2/Draco pipeline
81
110
  - [ ] Docs site with live playground (reusing GAMA's runner)
82
111
 
@@ -84,10 +113,11 @@ Scatter placement uses density noise for natural clumping and clearings, a spati
84
113
 
85
114
  ```bash
86
115
  npm install
87
- npm test # 21 vitest unit tests (determinism, metadata, scatter rules)
116
+ npm test # 62 vitest unit tests (determinism, metadata, scatter rules, snapshots)
88
117
  npm run typecheck
89
118
  npm run build # tsup → dist (ESM + CJS + d.ts)
90
- npm run dev # the SCENA × GAMA forest demo (needs ../gama sibling clone)
119
+ npm run dev # the SCENA × GAMA living-forest demo
120
+ npm run dev:manifest # the same kind of world, built from one JSON manifest
91
121
  ```
92
122
 
93
123
  ## License