incanto 0.25.4 → 0.27.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-BGtXxzJn.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-BrneVqN0.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.25.4",
3
+ "version": "0.27.0",
4
4
  "description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -331,6 +331,9 @@
331
331
  {
332
332
  "$ref": "#/$defs/Water3D"
333
333
  },
334
+ {
335
+ "$ref": "#/$defs/WaterCutout3D"
336
+ },
334
337
  {
335
338
  "$ref": "#/$defs/_instance"
336
339
  }
@@ -4732,6 +4735,10 @@
4732
4735
  "type": "string",
4733
4736
  "default": ""
4734
4737
  },
4738
+ "carve": {
4739
+ "type": "boolean",
4740
+ "default": true
4741
+ },
4735
4742
  "flowForce": {
4736
4743
  "type": "number",
4737
4744
  "default": 1
@@ -6691,6 +6698,114 @@
6691
6698
  }
6692
6699
  },
6693
6700
  "required": ["name", "type"]
6701
+ },
6702
+ "WaterCutout3D": {
6703
+ "type": "object",
6704
+ "properties": {
6705
+ "name": {
6706
+ "type": "string"
6707
+ },
6708
+ "uid": {
6709
+ "type": "string"
6710
+ },
6711
+ "type": {
6712
+ "const": "WaterCutout3D"
6713
+ },
6714
+ "groups": {
6715
+ "type": "array",
6716
+ "items": {
6717
+ "type": "string"
6718
+ }
6719
+ },
6720
+ "tags": {
6721
+ "type": "object"
6722
+ },
6723
+ "props": {
6724
+ "type": "object",
6725
+ "properties": {
6726
+ "position": {
6727
+ "type": "array",
6728
+ "items": {
6729
+ "type": "number"
6730
+ },
6731
+ "minItems": 3,
6732
+ "maxItems": 3,
6733
+ "default": [0, 0, 0]
6734
+ },
6735
+ "rotation": {
6736
+ "type": "array",
6737
+ "items": {
6738
+ "type": "number"
6739
+ },
6740
+ "minItems": 3,
6741
+ "maxItems": 3,
6742
+ "default": [0, 0, 0]
6743
+ },
6744
+ "static": {
6745
+ "type": "boolean",
6746
+ "default": false
6747
+ },
6748
+ "scale": {
6749
+ "type": "array",
6750
+ "items": {
6751
+ "type": "number"
6752
+ },
6753
+ "minItems": 3,
6754
+ "maxItems": 3,
6755
+ "default": [1, 1, 1]
6756
+ },
6757
+ "visible": {
6758
+ "type": "boolean",
6759
+ "default": true
6760
+ },
6761
+ "renderOrder": {
6762
+ "type": "number",
6763
+ "default": 0
6764
+ },
6765
+ "orderGroup": {
6766
+ "type": "string",
6767
+ "enum": ["background", "terrain", "default", "characters", "effects", "overlay"],
6768
+ "default": "default"
6769
+ },
6770
+ "size": {
6771
+ "type": "array",
6772
+ "items": {
6773
+ "type": "number"
6774
+ },
6775
+ "minItems": 3,
6776
+ "maxItems": 3,
6777
+ "default": [2, 1, 1]
6778
+ },
6779
+ "enabled": {
6780
+ "type": "boolean",
6781
+ "default": true
6782
+ }
6783
+ },
6784
+ "additionalProperties": false
6785
+ },
6786
+ "script": {
6787
+ "type": "object",
6788
+ "properties": {
6789
+ "name": {
6790
+ "type": "string"
6791
+ },
6792
+ "props": {
6793
+ "type": "object"
6794
+ }
6795
+ },
6796
+ "required": ["name"]
6797
+ },
6798
+ "network": {
6799
+ "type": "object"
6800
+ },
6801
+ "children": {
6802
+ "type": "array",
6803
+ "items": {
6804
+ "$ref": "#/$defs/node"
6805
+ }
6806
+ }
6807
+ },
6808
+ "required": ["name", "type"]
6694
6809
  }
6695
6810
  }
6696
6811
  }
@@ -545,7 +545,7 @@ is radians of `atan(|∇h|)`. Default textures stream from the live agent8 CDN
545
545
  | `size` | `[200, 200]` | [width, depth] meters, centered on the node |
546
546
  | `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
547
  | `seed` | `1` | integer seed — deterministic terrain |
548
- | `resolution` | `128` | grid segments per side (2–128, load-time check) |
548
+ | `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
549
  | `theme` | `"island"` | see table — `custom` requires `layers` |
550
550
  | `roughness` | `0.5` | detail-octave persistence (higher = more rugged) |
551
551
  | `detail` | `4` | detail octave count |
@@ -789,6 +789,44 @@ Swimming down is a different optical world, and the engine renders it as one:
789
789
  "caustics": { "intensity": 0.6 } } } }
790
790
  ```
791
791
 
792
+ ### `WaterCutout3D` — the thing that holds the water back
793
+
794
+ Water is ONE surface; it has no idea a boat has an inside. Float an open hull
795
+ and every crest that rises above its floor renders as sea sloshing through the
796
+ deck. Freeboard cannot promise otherwise — a big enough swell always wins for a
797
+ frame. So name the geometry that keeps the water out:
798
+
799
+ ```json
800
+ { "name": "Rowboat", "type": "RigidBody3D",
801
+ "script": { "name": "Buoyancy", "props": { "draft": 0.2 } },
802
+ "children": [
803
+ { "name": "DryInside", "type": "WaterCutout3D",
804
+ "props": { "size": [2.55, 1.2, 0.95], "position": [-0.1, 0.4, 0] } },
805
+ … hull, seats, oars …
806
+ ] }
807
+ ```
808
+
809
+ It MUST be a CHILD of what it belongs to — a hull, a diving bell, a well under a
810
+ dock, a cave mouth. That parent is the mechanism, not a convenience: the water
811
+ re-draws it (the part inside the box) into the depth pre-pass at the near plane,
812
+ and then hides itself behind it exactly as it hides behind any solid thing. At
813
+ the scene root a cutout has nothing to stand in for, and the loader says so.
814
+
815
+ | prop | default | meaning |
816
+ |---|---|---|
817
+ | `size` | `[2, 1, 1]` | box extent in meters, centered on the node — which PART of the parent holds water back |
818
+ | `enabled` | `true` | live toggle — a scuttled hull can start letting the sea in |
819
+
820
+ **A cutout cannot punch a hole in open water**, because nothing is ever cut out
821
+ of the water. The mask is the parent's own silhouette, so water can only be
822
+ hidden behind geometry the player is already looking at. Overshoot the box and
823
+ more of that hull holds water back; undershoot and water stands in the bow. Size
824
+ it to the interior and err large.
825
+
826
+ Needs the depth pre-pass, so `"quality": "fancy"` (the default). Four cutouts per
827
+ water surface; each costs one extra draw of its parent in that pass. Skinned and
828
+ instanced meshes re-draw at their rest pose — hang cutouts on ordinary geometry.
829
+
792
830
  ### Where water meets ground
793
831
 
794
832
  The waterline is the thing that gives game water away. Incanto fades it in
@@ -937,6 +975,7 @@ underneath decides everything else.
937
975
  | `foam` | `1` | whitewater dial — 0 = a glassy canal, 2 = raging |
938
976
  | `ripples` | `1` | surface-detail dial (ripple relief + glitter) |
939
977
  | `terrain` | `''` | drape target path; empty = auto-find the first Terrain3D (a wrong path fails at load) |
978
+ | `carve` | `true` | cut the bed. The river trenches its own channel into the terrain it drapes on — see below |
940
979
  | `flowForce` | `1` | how hard the current sweeps bodies downstream (0 = visual only) |
941
980
  | `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) |
942
981
  | `sunDirection` / `sunColor` / `sunIntensity` | sky / `#fff6e0` / `1` | glint (the environment sun wins while `sunDirection` is default) |
@@ -952,24 +991,64 @@ underneath decides everything else.
952
991
  runs faster), which is what puts rapids where a real river has them;
953
992
  - **whitewater**: grade × speed makes rapids, the bank shear line makes edge
954
993
  froth, a thinning column riffles over rock. Foam then rides downstream.
994
+ - **what stands in the water**: drop boulders in the channel and the current
995
+ answers them — a cushion of white piled on the upstream face, a torn wake
996
+ opening downstream, and the surface sheen bending around the stone. Nothing
997
+ is wired: the node scans the tree for `MeshInstance3D` and `InstancedMesh3D`
998
+ footprints (a rock scatter is one node with fifty stones in it), keeps the
999
+ ones whose body actually intersects the water column — a footbridge deck
1000
+ three metres up is not a boulder — and writes the eight nearest the camera
1001
+ each frame, ramped in by distance so nothing pops. A drowned stone still
1002
+ bends the flow; only one standing proud tears it white.
955
1003
 
956
1004
  It costs NO extra render passes (unlike fancy `Water3D`), so a map can carry a
957
1005
  dozen rivers.
958
1006
 
959
- ### Cut the bed first
1007
+ ### The bed digs itself, and the water can never hang in the air
1008
+
1009
+ Two promises hold a river to the ground, and both are automatic:
1010
+
1011
+ **1. It carves its own bed** (`carve`, on by default). Real water erodes a
1012
+ channel; a ribbon laid on raw ground has nothing holding it and reads as a
1013
+ strip of blue hovering over the grass. So the node cuts one — a trench per
1014
+ reach, as wide as the creek is there, with banks that stand above the water.
1015
+ `heightAt`, the heightfield collider, the splat and draped vegetation all see
1016
+ it, because it IS the terrain. The floor it cuts only ever FALLS: where the
1017
+ path crosses a rise the bed digs through it (up to 7 water columns deep) so the
1018
+ water keeps running instead of burying itself.
1019
+
1020
+ ```json
1021
+ { "name": "Creek", "type": "River3D", "props": {
1022
+ "path": [[-80, -30], [-20, 5], [30, 10], [90, -10]],
1023
+ "widths": [2.4, 4.5, 7, 9], "depth": 1 } }
1024
+ ```
1025
+
1026
+ That is the whole scene. No `channels` on the terrain, no bank meshes.
960
1027
 
961
- Water needs somewhere to be. Give the same path to the terrain as a `channels`
962
- carve, or the creek will lie on top of the ground as a thin film and read as
963
- riffle everywhere:
1028
+ **2. Its surface is FITTED to the ground it finds.** Each station reads its own
1029
+ cross-section: the water stands no higher than the lowest rim holding it, ends
1030
+ where the ground comes up to meet it (so the ribbon is asymmetric on a sloping
1031
+ reach — a near bank and a far one), and where a bank has fallen away entirely
1032
+ the ribbon lies DOWN on the slope as a film instead of hanging off it. Authored
1033
+ width is how much water there is; the ground decides where it goes.
1034
+
1035
+ Carve it yourself with `Terrain3D.channels` when you want a bed the river does
1036
+ not own — a stone aqueduct, a canal with masonry sides, a dry wash that only
1037
+ floods in a cutscene — and set `"carve": false`.
1038
+
1039
+ **Terrain resolution is the one thing that can defeat this.** A trench thinner
1040
+ than a grid cell smooths away to nothing. 128 segments on a 260 m map is ~2 m
1041
+ per cell, so a 2.4 m headwater creek needs `"resolution": 256` (~1 m cells) on
1042
+ the Terrain3D. The node widens its cut to survive the grid rather than cutting
1043
+ nothing, so a too-coarse terrain shows up as a creek in a bed wider than it
1044
+ should be.
964
1045
 
965
1046
  ```ts
1047
+ // the classic setup: let the terrain choose the line, then pour water on it
966
1048
  const path = traceDownhillPath(terrain.heightAt, { x: peakX, z: peakZ, step: 7 });
967
- // terrain prop — the trench (collider + heightAt + drape all see it)
968
- "channels": [{ "path": path, "width": 7, "depth": 2.2, "taper": 9 }]
969
- // river prop — the water in it
970
- "path": path, "widths": [2.4, 4.5, 7, 9], "depth": 1
971
1049
  ```
972
1050
 
1051
+
973
1052
  ### Waterfalls happen — you do not author them
974
1053
 
975
1054
  Nothing in a scene says "waterfall". Where the ground gives way the surface
@@ -695,6 +695,7 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
695
695
  | `sunColor` | `"#fff6e0"` | string |
696
696
  | `sunIntensity` | `1` | number |
697
697
  | `terrain` | `""` | string |
698
+ | `carve` | `true` | boolean |
698
699
  | `flowForce` | `1` | number |
699
700
  | `spray` | `1` | number |
700
701
 
@@ -996,6 +997,20 @@ Signals: `blocksChanged`
996
997
 
997
998
  Signals: `entered(body)` · `exited(body)`
998
999
 
1000
+ ## `WaterCutout3D` — `incanto/3d`
1001
+
1002
+ | Prop | Default | Kind |
1003
+ |---|---|---|
1004
+ | `position` | `[0,0,0]` | array |
1005
+ | `rotation` | `[0,0,0]` | array |
1006
+ | `static` | `false` | boolean |
1007
+ | `scale` | `[1,1,1]` | array |
1008
+ | `visible` | `true` | boolean |
1009
+ | `renderOrder` | `0` | number |
1010
+ | `orderGroup` | `"default"` | one of: `background` `terrain` `default` `characters` `effects` `overlay` |
1011
+ | `size` | `[2,1,1]` | array |
1012
+ | `enabled` | `true` | boolean |
1013
+
999
1014
  ## Gameplay behaviors — `incanto/gameplay`
1000
1015
 
1001
1016
  Ready-made `Behavior`s auto-registered by `createGame2D`/`createGame3D` (opt out
@@ -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.25.4",
17
+ "incanto": "^0.27.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.25.4",
16
+ "incanto": "^0.27.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.25.4",
16
+ "incanto": "^0.27.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {