incanto 0.19.0 → 0.21.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/dist/3d.d.ts +260 -1
- package/dist/3d.js +5 -5
- package/dist/{create-game-Ct6RC4LF.js → create-game-xbxZRFwR.js} +2 -2
- package/dist/env.js +1 -1
- package/dist/{heightmap-DgX-Opav.js → heightmap-CRK0M4jT.js} +35 -0
- package/dist/index.js +1 -1
- package/dist/{physics-3d-JPdedRAB.js → physics-3d-DHTZUrr0.js} +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-Cv5GNiVo.js → register-BC55Ez6I.js} +1483 -125
- package/dist/test.js +3 -3
- package/editor/assets/{agent8-CcfBcssq.js → agent8-Cdd8g6w6.js} +1 -1
- package/editor/assets/{index-B7gj1VII.js → index-Dt44Z9ka.js} +431 -63
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +179 -0
- package/skills/incanto-environment.md +156 -1
- package/skills/incanto-node-reference.md +31 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/package.json +1 -1
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-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-Dt44Z9ka.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
|
@@ -277,6 +277,9 @@
|
|
|
277
277
|
{
|
|
278
278
|
"$ref": "#/$defs/RigidBody3D"
|
|
279
279
|
},
|
|
280
|
+
{
|
|
281
|
+
"$ref": "#/$defs/River3D"
|
|
282
|
+
},
|
|
280
283
|
{
|
|
281
284
|
"$ref": "#/$defs/Sprite2D"
|
|
282
285
|
},
|
|
@@ -4600,6 +4603,170 @@
|
|
|
4600
4603
|
},
|
|
4601
4604
|
"required": ["name", "type"]
|
|
4602
4605
|
},
|
|
4606
|
+
"River3D": {
|
|
4607
|
+
"type": "object",
|
|
4608
|
+
"properties": {
|
|
4609
|
+
"name": {
|
|
4610
|
+
"type": "string"
|
|
4611
|
+
},
|
|
4612
|
+
"uid": {
|
|
4613
|
+
"type": "string"
|
|
4614
|
+
},
|
|
4615
|
+
"type": {
|
|
4616
|
+
"const": "River3D"
|
|
4617
|
+
},
|
|
4618
|
+
"groups": {
|
|
4619
|
+
"type": "array",
|
|
4620
|
+
"items": {
|
|
4621
|
+
"type": "string"
|
|
4622
|
+
}
|
|
4623
|
+
},
|
|
4624
|
+
"tags": {
|
|
4625
|
+
"type": "object"
|
|
4626
|
+
},
|
|
4627
|
+
"props": {
|
|
4628
|
+
"type": "object",
|
|
4629
|
+
"properties": {
|
|
4630
|
+
"position": {
|
|
4631
|
+
"type": "array",
|
|
4632
|
+
"items": {
|
|
4633
|
+
"type": "number"
|
|
4634
|
+
},
|
|
4635
|
+
"minItems": 3,
|
|
4636
|
+
"maxItems": 3,
|
|
4637
|
+
"default": [0, 0, 0]
|
|
4638
|
+
},
|
|
4639
|
+
"rotation": {
|
|
4640
|
+
"type": "array",
|
|
4641
|
+
"items": {
|
|
4642
|
+
"type": "number"
|
|
4643
|
+
},
|
|
4644
|
+
"minItems": 3,
|
|
4645
|
+
"maxItems": 3,
|
|
4646
|
+
"default": [0, 0, 0]
|
|
4647
|
+
},
|
|
4648
|
+
"static": {
|
|
4649
|
+
"type": "boolean",
|
|
4650
|
+
"default": false
|
|
4651
|
+
},
|
|
4652
|
+
"scale": {
|
|
4653
|
+
"type": "array",
|
|
4654
|
+
"items": {
|
|
4655
|
+
"type": "number"
|
|
4656
|
+
},
|
|
4657
|
+
"minItems": 3,
|
|
4658
|
+
"maxItems": 3,
|
|
4659
|
+
"default": [1, 1, 1]
|
|
4660
|
+
},
|
|
4661
|
+
"visible": {
|
|
4662
|
+
"type": "boolean",
|
|
4663
|
+
"default": true
|
|
4664
|
+
},
|
|
4665
|
+
"renderOrder": {
|
|
4666
|
+
"type": "number",
|
|
4667
|
+
"default": 0
|
|
4668
|
+
},
|
|
4669
|
+
"orderGroup": {
|
|
4670
|
+
"type": "string",
|
|
4671
|
+
"enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
|
|
4672
|
+
"default": "default"
|
|
4673
|
+
},
|
|
4674
|
+
"path": {
|
|
4675
|
+
"type": "array",
|
|
4676
|
+
"default": []
|
|
4677
|
+
},
|
|
4678
|
+
"width": {
|
|
4679
|
+
"type": "number",
|
|
4680
|
+
"default": 6
|
|
4681
|
+
},
|
|
4682
|
+
"widths": {
|
|
4683
|
+
"type": "array",
|
|
4684
|
+
"default": []
|
|
4685
|
+
},
|
|
4686
|
+
"depth": {
|
|
4687
|
+
"type": "number",
|
|
4688
|
+
"default": 0.8
|
|
4689
|
+
},
|
|
4690
|
+
"flowSpeed": {
|
|
4691
|
+
"type": "number",
|
|
4692
|
+
"default": 1.6
|
|
4693
|
+
},
|
|
4694
|
+
"colors": {
|
|
4695
|
+
"type": "object",
|
|
4696
|
+
"default": {}
|
|
4697
|
+
},
|
|
4698
|
+
"absorption": {
|
|
4699
|
+
"type": "number",
|
|
4700
|
+
"default": 0.5
|
|
4701
|
+
},
|
|
4702
|
+
"opacity": {
|
|
4703
|
+
"type": "number",
|
|
4704
|
+
"default": 0.85
|
|
4705
|
+
},
|
|
4706
|
+
"foam": {
|
|
4707
|
+
"type": "number",
|
|
4708
|
+
"default": 1
|
|
4709
|
+
},
|
|
4710
|
+
"ripples": {
|
|
4711
|
+
"type": "number",
|
|
4712
|
+
"default": 1
|
|
4713
|
+
},
|
|
4714
|
+
"sunDirection": {
|
|
4715
|
+
"type": "array",
|
|
4716
|
+
"items": {
|
|
4717
|
+
"type": "number"
|
|
4718
|
+
},
|
|
4719
|
+
"minItems": 3,
|
|
4720
|
+
"maxItems": 3,
|
|
4721
|
+
"default": [0.5, 0.8, 0.3]
|
|
4722
|
+
},
|
|
4723
|
+
"sunColor": {
|
|
4724
|
+
"type": "string",
|
|
4725
|
+
"default": "#fff6e0"
|
|
4726
|
+
},
|
|
4727
|
+
"sunIntensity": {
|
|
4728
|
+
"type": "number",
|
|
4729
|
+
"default": 1
|
|
4730
|
+
},
|
|
4731
|
+
"terrain": {
|
|
4732
|
+
"type": "string",
|
|
4733
|
+
"default": ""
|
|
4734
|
+
},
|
|
4735
|
+
"flowForce": {
|
|
4736
|
+
"type": "number",
|
|
4737
|
+
"default": 1
|
|
4738
|
+
},
|
|
4739
|
+
"spray": {
|
|
4740
|
+
"type": "number",
|
|
4741
|
+
"default": 1
|
|
4742
|
+
}
|
|
4743
|
+
},
|
|
4744
|
+
"additionalProperties": false
|
|
4745
|
+
},
|
|
4746
|
+
"script": {
|
|
4747
|
+
"type": "object",
|
|
4748
|
+
"properties": {
|
|
4749
|
+
"name": {
|
|
4750
|
+
"type": "string"
|
|
4751
|
+
},
|
|
4752
|
+
"props": {
|
|
4753
|
+
"type": "object"
|
|
4754
|
+
}
|
|
4755
|
+
},
|
|
4756
|
+
"required": ["name"]
|
|
4757
|
+
},
|
|
4758
|
+
"network": {
|
|
4759
|
+
"type": "object"
|
|
4760
|
+
},
|
|
4761
|
+
"children": {
|
|
4762
|
+
"type": "array",
|
|
4763
|
+
"items": {
|
|
4764
|
+
"$ref": "#/$defs/node"
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
},
|
|
4768
|
+
"required": ["name", "type"]
|
|
4769
|
+
},
|
|
4603
4770
|
"Sprite2D": {
|
|
4604
4771
|
"type": "object",
|
|
4605
4772
|
"properties": {
|
|
@@ -5198,6 +5365,10 @@
|
|
|
5198
5365
|
"type": "array",
|
|
5199
5366
|
"default": []
|
|
5200
5367
|
},
|
|
5368
|
+
"channels": {
|
|
5369
|
+
"type": "array",
|
|
5370
|
+
"default": []
|
|
5371
|
+
},
|
|
5201
5372
|
"wetline": {
|
|
5202
5373
|
"default": null
|
|
5203
5374
|
}
|
|
@@ -6408,6 +6579,14 @@
|
|
|
6408
6579
|
"type": "number",
|
|
6409
6580
|
"default": 1000
|
|
6410
6581
|
},
|
|
6582
|
+
"mirror": {
|
|
6583
|
+
"type": "boolean",
|
|
6584
|
+
"default": false
|
|
6585
|
+
},
|
|
6586
|
+
"swellSteepness": {
|
|
6587
|
+
"type": "number",
|
|
6588
|
+
"default": 0.35
|
|
6589
|
+
},
|
|
6411
6590
|
"foam": {
|
|
6412
6591
|
"type": "boolean",
|
|
6413
6592
|
"default": true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: incanto-environment
|
|
3
|
-
description: Generating game environments in Incanto — the deterministic `incanto-env` CLI and `incanto/env` library. EXACTLY three themed 3D generators (arena with boxes/ruins/garden, Terrain3D heightfield worlds with island/alpine/plains/desert/meadow/forest/savanna/snow/wetland/volcanic themes, maze with stone/hedge/canyon) plus 2D mazes, roguelike dungeons and platform courses; insertIntoScene, the GENERATORS catalog + runGenerator; and the environment nodes — Terrain3D (biome texture splatting, heightAt, heightfield collider), shader-water Water3D (FBM waves, anisotropic wind ripple, sparse sun glints, crystal shallows, refraction + depth absorption, reflections, shoreline foam, splash signals + ripples), shader-grass Foliage3D (real curved blade meshes, clumps, rolling wind, distance LOD, character bend), Flowers3D flower plants (lush/sparse/none density dial, daisy/cosmos/bellflower varieties, Voronoi patches, head-bob sway) and ez-tree Tree3D groves (branchy trunks, textured wind-swayed leaves, budget-checked tiers). Use when a game needs a level, stage, arena, terrain, heightfield, island, meadow, grass field, flowers, flower bed, forest, trees, mountain, desert, savanna, snow, tundra, swamp, wetland, marsh, volcano, lava field, maze, dungeon, platform course, scattered props, clouds, water, or a splash effect.
|
|
3
|
+
description: Generating game environments in Incanto — the deterministic `incanto-env` CLI and `incanto/env` library. EXACTLY three themed 3D generators (arena with boxes/ruins/garden, Terrain3D heightfield worlds with island/alpine/plains/desert/meadow/forest/savanna/snow/wetland/volcanic themes, maze with stone/hedge/canyon) plus 2D mazes, roguelike dungeons and platform courses; insertIntoScene, the GENERATORS catalog + runGenerator; and the environment nodes — Terrain3D (biome texture splatting, heightAt, heightfield collider), shader-water Water3D (FBM waves, anisotropic wind ripple, sparse sun glints, crystal shallows, refraction + depth absorption, reflections, shoreline foam, splash signals + ripples), River3D running water (path-derived course, terrain-cut banks, derived rapids/riffles, a current that sweeps bodies), shader-grass Foliage3D (real curved blade meshes, clumps, rolling wind, distance LOD, character bend), Flowers3D flower plants (lush/sparse/none density dial, daisy/cosmos/bellflower varieties, Voronoi patches, head-bob sway) and ez-tree Tree3D groves (branchy trunks, textured wind-swayed leaves, budget-checked tiers). Use when a game needs a level, stage, arena, terrain, heightfield, island, meadow, grass field, flowers, flower bed, forest, trees, mountain, desert, savanna, snow, tundra, swamp, wetland, marsh, volcano, lava field, maze, dungeon, platform course, scattered props, clouds, water, a river, stream, creek, brook, rapids, a ford, or a splash effect.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Environment generation — levels from a seed
|
|
@@ -554,6 +554,7 @@ is radians of `atan(|∇h|)`. Default textures stream from the live agent8 CDN
|
|
|
554
554
|
| `layers` | `[]` | custom splat layers (theme `custom` only) |
|
|
555
555
|
| `textureBase` | agent8 CDN | base URL: `<base>/<name>.png` + `<base>/<name>_normal.png` |
|
|
556
556
|
| `basins` | `[]` | lake/pond bowls — `[{ x, z, radius, depth }]` (centered meters) carved into the surface (smooth, 0-slope rim); **negative `depth` RAISES a smooth dome mound instead** (a hill, an islet); `heightAt`, the collider AND draped grass all see them |
|
|
557
|
+
| `channels` | `[]` | river/road TRENCHES carved along polylines — `[{ path, width, depth, taper? }]` (the line counterpart of `basins`). This is what gives a `River3D` a bed to sit in; `heightAt`, the collider and draped vegetation all see it |
|
|
557
558
|
| `wetline` | `null` | WET SAND band — `{ y, band? }` darkens + glosses the splat in a noisy, breathing band just above height `y` (a waterline): the trace of the last swash runup. `band` (default `1.1` m) caps how high above `y` reads wet. `null` = off |
|
|
558
559
|
|
|
559
560
|
**Wet sand at a shoreline.** Set `wetline.y` to the SAME world height as the
|
|
@@ -697,6 +698,8 @@ hue reads grey.
|
|
|
697
698
|
| `colors` | `{}` | fancy ramp overrides: `{"trough", "surface", "peak"}` hex strings |
|
|
698
699
|
| `reflection` | `true` | live CubeCamera reflections (fancy only; needs a renderer) |
|
|
699
700
|
| `reflectionInterval` | `1000` | ms between CubeCamera reflection re-renders |
|
|
701
|
+
| `mirror` | `false` | TRUE planar reflection (see below) — the only way the dock, the boat and the player appear IN the water. One extra scene render per frame |
|
|
702
|
+
| `swellSteepness` | `0.35` | GERSTNER pinch on the swell: 0 = height-only trains, 1 = maximum trochoid (crests narrow to a peak, troughs broaden). It also decides where whitecaps break — the surface's Jacobian collapses on a folding crest, and that is where foam belongs |
|
|
700
703
|
| `foam` | `true` | noise-broken shoreline foam band + crest foam near shores |
|
|
701
704
|
| `interaction` | `true` | character interaction: bodies (CharacterBody3D/RigidBody3D) inside the XZ footprint whose FEET dip below the surface count as in-water — so a WADING character (origin above water, feet below, sized from its collider) splashes, not only a fully-submerged one. Entry/exit emit `entered`/`exited` + a splash ripple; striding through water trails a WAKE (a ripple every ~0.45 m); floating bodies bob gently. Ripples drive both shaders' `uRipples[8]` (fancy) and CPU-displace the mesh (`simple`) |
|
|
702
705
|
| `splash` | `true` | built-in splash WHITEWATER (needs `interaction`): the shaders paint expanding **surface foam** where bodies interact — a **"풍덩"** froth bloom when a body plunges in (bigger the faster it falls) and a **foam trail ("물살")** behind a body wading/running through. Real frothy whitewater on the surface, not flying droplets; works on both `fancy` and `simple` quality. `false` keeps the ripples/waves but paints no foam. No wiring needed; for an extra custom burst add your own `Particles3D` on the `entered` signal |
|
|
@@ -737,6 +740,32 @@ Two placement rules keep the surface artifact-free:
|
|
|
737
740
|
intentionally does not — its submerged hull shows through `refraction`
|
|
738
741
|
instead.
|
|
739
742
|
|
|
743
|
+
### `mirror` — a TRUE planar reflection
|
|
744
|
+
|
|
745
|
+
The default reflection is a CubeCamera env map: it carries the sky and the far
|
|
746
|
+
shore, and it deliberately near-clips everything inside the water's own
|
|
747
|
+
footprint (a nearby crate sampled as an infinite environment smears across the
|
|
748
|
+
whole surface). So the boat, the dock, the pier lamp and the player standing at
|
|
749
|
+
the edge are exactly what it CANNOT show.
|
|
750
|
+
|
|
751
|
+
`mirror: true` re-renders the scene from the camera mirrored across the water
|
|
752
|
+
plane and samples it per fragment, so those things appear in the water:
|
|
753
|
+
|
|
754
|
+
```json
|
|
755
|
+
{ "type": "Water3D", "props": { "preset": "lake", "mirror": true,
|
|
756
|
+
"waveHeight": 0.012, "detailStrength": 0.07 } }
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
- Costs **one extra scene render per frame** (half the drawing buffer, clamped)
|
|
760
|
+
— off by default; a still lake or a pool earns it, open ocean rarely does.
|
|
761
|
+
- A mirror needs a CALM surface to read: chop that shatters the reflection is
|
|
762
|
+
what makes game lakes look like blue plastic. Pair it with a low `waveHeight`
|
|
763
|
+
and a gentle `detailStrength`.
|
|
764
|
+
- Rays that leave the screen have no planar sample; those fragments fall back to
|
|
765
|
+
the cube map (keep `reflection: true`), so there is no hard cut at the edges.
|
|
766
|
+
- Geometry below the waterline is clipped out of the mirror pass, and the whole
|
|
767
|
+
pass is skipped while the camera is submerged.
|
|
768
|
+
|
|
740
769
|
### Character interaction — `entered(body)` / `exited(body)`
|
|
741
770
|
|
|
742
771
|
With `interaction: true` the water scans the tree each frame for
|
|
@@ -779,6 +808,132 @@ emitter only when you want a specific custom burst:
|
|
|
779
808
|
emitter to the body first — the signal hands you the node:
|
|
780
809
|
`onEntered(body) { splash.position = body.position; splash.replay(); }`.)
|
|
781
810
|
|
|
811
|
+
## River3D — running water
|
|
812
|
+
|
|
813
|
+
`Water3D` is a *surface*; `River3D` is a *current*. A river is authored as a
|
|
814
|
+
line, not a shape: give it a centerline `path` and a width, and the terrain
|
|
815
|
+
underneath decides everything else.
|
|
816
|
+
|
|
817
|
+
```json
|
|
818
|
+
{ "name": "Creek", "type": "River3D", "props": {
|
|
819
|
+
"path": [[-80, -30], [-20, 5], [30, 10], [90, -10]],
|
|
820
|
+
"widths": [2.5, 5, 9],
|
|
821
|
+
"depth": 1,
|
|
822
|
+
"flowSpeed": 1.6,
|
|
823
|
+
"colors": { "shallow": "#79b6a4", "deep": "#103c49", "bank": "#39472f" }
|
|
824
|
+
} }
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
| prop | default | meaning |
|
|
828
|
+
|---|---|---|
|
|
829
|
+
| `path` | `[]` | centerline `[[x, z], …]` in NODE-LOCAL meters, Catmull-Rom smoothed (2+ points) |
|
|
830
|
+
| `width` | `6` | channel width in meters |
|
|
831
|
+
| `widths` | `[]` | width profile lerped source→mouth (e.g. `[2.5, 5, 9]`); empty = constant `width` |
|
|
832
|
+
| `depth` | `0.8` | water column at the centerline — also the scale foam thresholds read as "shallow" |
|
|
833
|
+
| `flowSpeed` | `1.6` | reference current in m/s (the mean; reaches speed up and slow down) |
|
|
834
|
+
| `colors` | `{}` | `{ shallow, deep, sky, horizon, bank }` — the body ramp plus what the surface mirrors |
|
|
835
|
+
| `absorption` | `0.5` | how fast the water hides its bed (Beer's law) |
|
|
836
|
+
| `opacity` | `0.85` | upper bound on the body's opacity |
|
|
837
|
+
| `foam` | `1` | whitewater dial — 0 = a glassy canal, 2 = raging |
|
|
838
|
+
| `ripples` | `1` | surface-detail dial (ripple relief + glitter) |
|
|
839
|
+
| `terrain` | `''` | drape target path; empty = auto-find the first Terrain3D (a wrong path fails at load) |
|
|
840
|
+
| `flowForce` | `1` | how hard the current sweeps bodies downstream (0 = visual only) |
|
|
841
|
+
| `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) |
|
|
842
|
+
| `sunDirection` / `sunColor` / `sunIntensity` | sky / `#fff6e0` / `1` | glint (the environment sun wins while `sunDirection` is default) |
|
|
843
|
+
|
|
844
|
+
**What the node derives for you**, all from `path` + the ground:
|
|
845
|
+
|
|
846
|
+
- a surface that follows the bed's DESCENDING envelope — water pours over a lip
|
|
847
|
+
and never climbs one (a rising bed dams it into a still pool);
|
|
848
|
+
- **the visible width**: every vertex carries its own water column
|
|
849
|
+
(`surfaceY − bed`) and the shader stops drawing where that runs out, so
|
|
850
|
+
banks, gravel bars and half-drowned boulders cut the ribbon;
|
|
851
|
+
- **the current**: continuity (a pinched channel runs faster) × grade (a chute
|
|
852
|
+
runs faster), which is what puts rapids where a real river has them;
|
|
853
|
+
- **whitewater**: grade × speed makes rapids, the bank shear line makes edge
|
|
854
|
+
froth, a thinning column riffles over rock. Foam then rides downstream.
|
|
855
|
+
|
|
856
|
+
It costs NO extra render passes (unlike fancy `Water3D`), so a map can carry a
|
|
857
|
+
dozen rivers.
|
|
858
|
+
|
|
859
|
+
### Cut the bed first
|
|
860
|
+
|
|
861
|
+
Water needs somewhere to be. Give the same path to the terrain as a `channels`
|
|
862
|
+
carve, or the creek will lie on top of the ground as a thin film and read as
|
|
863
|
+
riffle everywhere:
|
|
864
|
+
|
|
865
|
+
```ts
|
|
866
|
+
const path = traceDownhillPath(terrain.heightAt, { x: peakX, z: peakZ, step: 7 });
|
|
867
|
+
// terrain prop — the trench (collider + heightAt + drape all see it)
|
|
868
|
+
"channels": [{ "path": path, "width": 7, "depth": 2.2, "taper": 9 }]
|
|
869
|
+
// river prop — the water in it
|
|
870
|
+
"path": path, "widths": [2.4, 4.5, 7, 9], "depth": 1
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### Waterfalls happen — you do not author them
|
|
874
|
+
|
|
875
|
+
Nothing in a scene says "waterfall". Where the ground gives way the surface
|
|
876
|
+
profile falls with it, and past ~20° of grade the shader stops treating the
|
|
877
|
+
sheet as flowing water: it aerates (white, diffuse, no sky reflection), its
|
|
878
|
+
pattern stretches into strands, the standing waves of a rapid switch off, and
|
|
879
|
+
the lip goes glassy-dark for the moment before it breaks. `spray` then hangs a
|
|
880
|
+
mist plume at the foot of each drop, sized by how far the water fell.
|
|
881
|
+
|
|
882
|
+
The cheapest way to put one in a scene is to take the ground away — a
|
|
883
|
+
`basins` pocket on the course, no wider than the creek's own reach:
|
|
884
|
+
|
|
885
|
+
```ts
|
|
886
|
+
const plungeAt = path[Math.round(path.length * 0.82)];
|
|
887
|
+
"basins": [{ "x": plungeAt[0], "z": plungeAt[1], "radius": 7, "depth": 4.5 }]
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
Keep it a POCKET: a wide shallow bowl reads as a crater with a puddle, and the
|
|
891
|
+
drop has to be short and sharp for the curtain to read at all. For your own
|
|
892
|
+
effects (sound, a rainbow, a cave behind the water), `river.fallsAt()` returns
|
|
893
|
+
every drop with its plunge point in WORLD coordinates and its height:
|
|
894
|
+
|
|
895
|
+
```ts
|
|
896
|
+
for (const fall of creek.fallsAt()) {
|
|
897
|
+
if (fall.drop > 4) playAt('$sfx/falls', fall.worldX, fall.worldY, fall.worldZ);
|
|
898
|
+
}
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
A step shallower than the water's own `depth` is deliberately NOT a fall — the
|
|
902
|
+
surface profile treats it as bed grain, which is what stops every riffle in a
|
|
903
|
+
creek from spawning a spray plume.
|
|
904
|
+
|
|
905
|
+
### Let the terrain choose the course — `traceDownhillPath`
|
|
906
|
+
|
|
907
|
+
```ts
|
|
908
|
+
import { traceDownhillPath } from 'incanto/3d';
|
|
909
|
+
const path = traceDownhillPath((x, z) => terrain.heightAt(x, z), {
|
|
910
|
+
x: sourceX, z: sourceZ, // start high
|
|
911
|
+
step: 7, // meters per step (also the gradient probe radius)
|
|
912
|
+
bounds: 100, // stay inside ±100 m
|
|
913
|
+
maxPoints: 46,
|
|
914
|
+
});
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
Steepest descent with momentum and a thalweg snap, so the line follows the
|
|
918
|
+
valley floor instead of rattling between its walls. It ends in a pool, at the
|
|
919
|
+
map edge or at `maxPoints`; `[]` means the start was flat.
|
|
920
|
+
|
|
921
|
+
### The current is gameplay
|
|
922
|
+
|
|
923
|
+
`flowForce` drags bodies toward the water's own velocity — drag, never thrust,
|
|
924
|
+
so a raft accelerates until it matches the current and then coasts, and a
|
|
925
|
+
character can fight across a slow ford but loses ground in a fast one. For
|
|
926
|
+
scripts, `sampleAt(x, z)` (WORLD coords) answers where a point sits:
|
|
927
|
+
|
|
928
|
+
```ts
|
|
929
|
+
const hit = creek.sampleAt(player.position[0], player.position[2]);
|
|
930
|
+
if (hit?.inside && hit.speed > 3) hud.warn('too fast to ford here');
|
|
931
|
+
// hit: { inside, across (±1 = banks), along, surfaceY, dirX, dirZ, speed, halfWidth }
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
The course is derived on the CPU in `update()`, so `sampleAt` works headlessly
|
|
935
|
+
(servers, `bun verify.ts`) before anything has ever been rendered.
|
|
936
|
+
|
|
782
937
|
## Determinism, verified
|
|
783
938
|
|
|
784
939
|
`--seed` makes every run reproducible — keep the seed in your scene comments
|
|
@@ -670,6 +670,34 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
|
|
|
670
670
|
|
|
671
671
|
Signals: `triggerEnter(other)` · `triggerExit(other)`
|
|
672
672
|
|
|
673
|
+
## `River3D` — `incanto/3d`
|
|
674
|
+
|
|
675
|
+
| Prop | Default | Kind |
|
|
676
|
+
|---|---|---|
|
|
677
|
+
| `position` | `[0,0,0]` | array |
|
|
678
|
+
| `rotation` | `[0,0,0]` | array |
|
|
679
|
+
| `static` | `false` | boolean |
|
|
680
|
+
| `scale` | `[1,1,1]` | array |
|
|
681
|
+
| `visible` | `true` | boolean |
|
|
682
|
+
| `renderOrder` | `0` | number |
|
|
683
|
+
| `orderGroup` | `"default"` | one of: `background` `terrain` `default` `characters` `effects` `overlay` |
|
|
684
|
+
| `path` | `[]` | array |
|
|
685
|
+
| `width` | `6` | number |
|
|
686
|
+
| `widths` | `[]` | array |
|
|
687
|
+
| `depth` | `0.8` | number |
|
|
688
|
+
| `flowSpeed` | `1.6` | number |
|
|
689
|
+
| `colors` | `{}` | object |
|
|
690
|
+
| `absorption` | `0.5` | number |
|
|
691
|
+
| `opacity` | `0.85` | number |
|
|
692
|
+
| `foam` | `1` | number |
|
|
693
|
+
| `ripples` | `1` | number |
|
|
694
|
+
| `sunDirection` | `[0.5,0.8,0.3]` | array |
|
|
695
|
+
| `sunColor` | `"#fff6e0"` | string |
|
|
696
|
+
| `sunIntensity` | `1` | number |
|
|
697
|
+
| `terrain` | `""` | string |
|
|
698
|
+
| `flowForce` | `1` | number |
|
|
699
|
+
| `spray` | `1` | number |
|
|
700
|
+
|
|
673
701
|
## `Sprite2D` — `incanto/2d`
|
|
674
702
|
|
|
675
703
|
| Prop | Default | Kind |
|
|
@@ -764,6 +792,7 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
|
|
|
764
792
|
| `layers` | `[]` | array |
|
|
765
793
|
| `textureBase` | `"https://agent8-games.verse8.io/assets/3D/default/textures/terrain"` | string |
|
|
766
794
|
| `basins` | `[]` | array |
|
|
795
|
+
| `channels` | `[]` | array |
|
|
767
796
|
| `wetline` | `null` | null |
|
|
768
797
|
|
|
769
798
|
## `TileMap2D` — `incanto/2d`
|
|
@@ -944,6 +973,8 @@ Signals: `blocksChanged`
|
|
|
944
973
|
| `colors` | `{}` | object |
|
|
945
974
|
| `reflection` | `true` | boolean |
|
|
946
975
|
| `reflectionInterval` | `1000` | number |
|
|
976
|
+
| `mirror` | `false` | boolean |
|
|
977
|
+
| `swellSteepness` | `0.35` | number |
|
|
947
978
|
| `foam` | `true` | boolean |
|
|
948
979
|
| `interaction` | `true` | boolean |
|
|
949
980
|
| `splash` | `true` | boolean |
|