incanto 0.26.0 → 0.28.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/editor/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Incanto Scene Editor</title>
7
7
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%236ee7dc'/><text x='8' y='12' text-anchor='middle' font-size='11' font-family='monospace' fill='%230e1018'>i</text></svg>" />
8
- <script type="module" crossorigin src="./assets/index-D4_I76v2.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-D6RQgROR.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
10
10
  <link rel="stylesheet" crossorigin href="./assets/index-D8QvwvOm.css">
11
11
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incanto",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -2379,6 +2379,10 @@
2379
2379
  "drape": {
2380
2380
  "default": null
2381
2381
  },
2382
+ "avoidWater": {
2383
+ "type": "boolean",
2384
+ "default": true
2385
+ },
2382
2386
  "terrain": {
2383
2387
  "type": "string",
2384
2388
  "default": ""
@@ -2566,6 +2570,10 @@
2566
2570
  "drape": {
2567
2571
  "default": null
2568
2572
  },
2573
+ "avoidWater": {
2574
+ "type": "boolean",
2575
+ "default": true
2576
+ },
2569
2577
  "terrain": {
2570
2578
  "type": "string",
2571
2579
  "default": ""
@@ -4735,6 +4743,10 @@
4735
4743
  "type": "string",
4736
4744
  "default": ""
4737
4745
  },
4746
+ "carve": {
4747
+ "type": "boolean",
4748
+ "default": true
4749
+ },
4738
4750
  "flowForce": {
4739
4751
  "type": "number",
4740
4752
  "default": 1
@@ -359,6 +359,7 @@ tops radially away with a smooth falloff as a character wades through.
359
359
  | `flowers` | `0` | DEPRECATED — confetti heads; use a `Flowers3D` node (load-time check 0–0.2) |
360
360
  | `seed` | `1` | placement seed — identical field across runs/machines |
361
361
  | `drape` | `null` | **AUTO by default**: whenever the scene has a Terrain3D, every blade roots on the ground under it (samples `heightAt`) — omit this prop and rolling terrain just works. `true` forces it (keeps retrying until a terrain appears — streamed worlds), `false` opts out (flat carpet at the node's Y) |
362
+ | `avoidWater` | `true` | blades keep OUT of the water — any instance whose ground sits under a `Water3D` or `River3D` surface is dropped, so a creek that cuts its bed through a meadow gets banks instead of blades waving in the current. `false` plants reeds and paddy fields |
362
363
  | `terrain` | `""` | drape target node path; empty = **auto-find the first Terrain3D** in the tree. A non-empty path that is NOT a Terrain3D fails at load — no silent floating |
363
364
 
364
365
  > **Rolling terrain just works.** Since 0.14 draping is on by default: with a
@@ -415,6 +416,7 @@ billboard confetti. Deterministic from the `seed` PROP.
415
416
  | `clustering` | `0.6` | 0 uniform … 1 tight Voronoi patches (load-time check) |
416
417
  | `sway` | `0.5` | head-bob wind strength; 0 disables |
417
418
  | `drape` | `null` | AUTO by default — plants root on the scene's Terrain3D when one exists; `true` forces, `false` opts out — see the Foliage3D drape note above |
419
+ | `avoidWater` | `true` | plants keep OUT of the water (see the Foliage3D note) — `false` for lilies and marsh planting |
418
420
  | `terrain` | `""` | drape target node path; empty = auto-find the first Terrain3D (a wrong path fails at load) |
419
421
 
420
422
  ## Tree3D
@@ -545,7 +547,7 @@ is radians of `atan(|∇h|)`. Default textures stream from the live agent8 CDN
545
547
  | `size` | `[200, 200]` | [width, depth] meters, centered on the node |
546
548
  | `maxHeight` | `28` | height SCALE — realized heights land at ~3.5–8× this (the ported pipeline sums positive noise octaves); read real numbers off `heightAt` |
547
549
  | `seed` | `1` | integer seed — deterministic terrain |
548
- | `resolution` | `128` | grid segments per side (2–128, load-time check) |
550
+ | `resolution` | `128` | grid segments per side (2–256, load-time check). 128 ≈ 2 m cells on a 260 m map; raise to 256 when the map carries something finer, e.g. a creek bed |
549
551
  | `theme` | `"island"` | see table — `custom` requires `layers` |
550
552
  | `roughness` | `0.5` | detail-octave persistence (higher = more rugged) |
551
553
  | `detail` | `4` | detail octave count |
@@ -975,6 +977,7 @@ underneath decides everything else.
975
977
  | `foam` | `1` | whitewater dial — 0 = a glassy canal, 2 = raging |
976
978
  | `ripples` | `1` | surface-detail dial (ripple relief + glitter) |
977
979
  | `terrain` | `''` | drape target path; empty = auto-find the first Terrain3D (a wrong path fails at load) |
980
+ | `carve` | `true` | cut the bed. The river trenches its own channel into the terrain it drapes on — see below |
978
981
  | `flowForce` | `1` | how hard the current sweeps bodies downstream (0 = visual only) |
979
982
  | `spray` | `1` | mist at the foot of every waterfall the course turns out to contain — the node finds the drops and hangs a Particles3D plume on each (0 = off) |
980
983
  | `sunDirection` / `sunColor` / `sunIntensity` | sky / `#fff6e0` / `1` | glint (the environment sun wins while `sunDirection` is default) |
@@ -990,24 +993,85 @@ underneath decides everything else.
990
993
  runs faster), which is what puts rapids where a real river has them;
991
994
  - **whitewater**: grade × speed makes rapids, the bank shear line makes edge
992
995
  froth, a thinning column riffles over rock. Foam then rides downstream.
996
+ - **a dry bank**: `Foliage3D` and `Flowers3D` refuse to plant where a river
997
+ runs (`avoidWater`, on by default), so a creek cutting its bed through a
998
+ meadow gets bare wet ground at the edge instead of blades waving in the
999
+ current;
1000
+ - **what stands in the water**: drop boulders in the channel and the current
1001
+ answers them — a cushion of white piled on the upstream face, a torn wake
1002
+ opening downstream, and the surface sheen bending around the stone. Nothing
1003
+ is wired: the node scans the tree for `MeshInstance3D` and `InstancedMesh3D`
1004
+ footprints (a rock scatter is one node with fifty stones in it), keeps the
1005
+ ones whose body actually intersects the water column — a footbridge deck
1006
+ three metres up is not a boulder — and writes the eight nearest the camera
1007
+ each frame, ramped in by distance so nothing pops. A drowned stone still
1008
+ bends the flow; only one standing proud tears it white.
993
1009
 
994
1010
  It costs NO extra render passes (unlike fancy `Water3D`), so a map can carry a
995
1011
  dozen rivers.
996
1012
 
997
- ### Cut the bed first
1013
+ ### The bed digs itself, and the water can never hang in the air
998
1014
 
999
- Water needs somewhere to be. Give the same path to the terrain as a `channels`
1000
- carve, or the creek will lie on top of the ground as a thin film and read as
1001
- riffle everywhere:
1015
+ Two promises hold a river to the ground, and both are automatic:
1016
+
1017
+ **1. It carves its own bed** (`carve`, on by default). Real water erodes a
1018
+ channel; a ribbon laid on raw ground has nothing holding it and reads as a
1019
+ strip of blue hovering over the grass. So the node cuts one — a trench per
1020
+ reach, as wide as the creek is there, with banks that stand above the water.
1021
+ `heightAt`, the heightfield collider, the splat and draped vegetation all see
1022
+ it, because it IS the terrain. The floor it cuts only ever FALLS: where the
1023
+ path crosses a rise the bed digs through it (up to 7 water columns deep) so the
1024
+ water keeps running instead of burying itself.
1025
+
1026
+ ```json
1027
+ { "name": "Creek", "type": "River3D", "props": {
1028
+ "path": [[-80, -30], [-20, 5], [30, 10], [90, -10]],
1029
+ "widths": [2.4, 4.5, 7, 9], "depth": 1 } }
1030
+ ```
1031
+
1032
+ That is the whole scene. No `channels` on the terrain, no bank meshes.
1033
+
1034
+ **2. Its surface is FITTED to the ground it finds.** Each station reads its own
1035
+ cross-section: the water stands no higher than the lowest rim holding it, ends
1036
+ where the ground comes up to meet it (so the ribbon is asymmetric on a sloping
1037
+ reach — a near bank and a far one), and where a bank has fallen away entirely
1038
+ the ribbon lies DOWN on the slope as a film instead of hanging off it. Authored
1039
+ width is how much water there is; the ground decides where it goes.
1040
+
1041
+ Carve it yourself with `Terrain3D.channels` when you want a bed the river does
1042
+ not own — a stone aqueduct, a canal with masonry sides, a dry wash that only
1043
+ floods in a cutscene — and set `"carve": false`.
1044
+
1045
+ **Terrain resolution is the one thing that can defeat this.** A trench thinner
1046
+ than a grid cell smooths away to nothing. 128 segments on a 260 m map is ~2 m
1047
+ per cell, so a 2.4 m headwater creek needs `"resolution": 256` (~1 m cells) on
1048
+ the Terrain3D. The node widens its cut to survive the grid rather than cutting
1049
+ nothing, so a too-coarse terrain shows up as a creek in a bed wider than it
1050
+ should be.
1002
1051
 
1003
1052
  ```ts
1053
+ // the classic setup: let the terrain choose the line, then pour water on it
1004
1054
  const path = traceDownhillPath(terrain.heightAt, { x: peakX, z: peakZ, step: 7 });
1005
- // terrain prop — the trench (collider + heightAt + drape all see it)
1006
- "channels": [{ "path": path, "width": 7, "depth": 2.2, "taper": 9 }]
1007
- // river prop — the water in it
1008
- "path": path, "widths": [2.4, 4.5, 7, 9], "depth": 1
1009
1055
  ```
1010
1056
 
1057
+ **Placing things against a self-carved bed?** Ask the engine what the cut will
1058
+ be — never mirror it by hand. `riverCarveChannels` is the same function the node
1059
+ runs at load, so a generator that assigns its result to a scratch Terrain3D gets
1060
+ the EXACT ground the game will have; a hand-rolled approximation puts the player
1061
+ inside a hill.
1062
+
1063
+ ```ts
1064
+ const carved = new Terrain3D('probe');
1065
+ Object.assign(carved, terrainProps);
1066
+ carved.channels = riverCarveChannels({
1067
+ path, widths, depth,
1068
+ cell: size / resolution, // the terrain's own grid
1069
+ groundAt: (x, z) => rawTerrain.heightAt(x, z),
1070
+ });
1071
+ const groundAt = (x: number, z: number) => carved.heightAt(x, z); // place against THIS
1072
+ ```
1073
+
1074
+
1011
1075
  ### Waterfalls happen — you do not author them
1012
1076
 
1013
1077
  Nothing in a scene says "waterfall". Where the ground gives way the surface
@@ -331,6 +331,7 @@ Signals: `movementStateChanged(state)`
331
331
  | `clustering` | `0.6` | number |
332
332
  | `sway` | `0.5` | number |
333
333
  | `drape` | `null` | null |
334
+ | `avoidWater` | `true` | boolean |
334
335
  | `terrain` | `""` | string |
335
336
 
336
337
  ## `Foliage3D` — `incanto/3d`
@@ -363,6 +364,7 @@ Signals: `movementStateChanged(state)`
363
364
  | `flowers` | `0` | number |
364
365
  | `seed` | `1` | number |
365
366
  | `drape` | `null` | null |
367
+ | `avoidWater` | `true` | boolean |
366
368
  | `terrain` | `""` | string |
367
369
 
368
370
  ## `HudLayer` — `incanto`
@@ -695,6 +697,7 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
695
697
  | `sunColor` | `"#fff6e0"` | string |
696
698
  | `sunIntensity` | `1` | number |
697
699
  | `terrain` | `""` | string |
700
+ | `carve` | `true` | boolean |
698
701
  | `flowForce` | `1` | number |
699
702
  | `spray` | `1` | number |
700
703
 
@@ -14,7 +14,7 @@
14
14
  "@dimforge/rapier2d-compat": "0.19.3",
15
15
  "@dimforge/rapier3d-compat": "0.19.3",
16
16
  "@pixiv/three-vrm": "^3.5.3",
17
- "incanto": "^0.26.0",
17
+ "incanto": "^0.28.0",
18
18
  "three": "^0.184.0"
19
19
  },
20
20
  "devDependencies": {
@@ -13,7 +13,7 @@
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.26.0",
16
+ "incanto": "^0.28.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -13,7 +13,7 @@
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.26.0",
16
+ "incanto": "^0.28.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {