incanto 0.23.0 → 0.25.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/2d.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
- import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-DrcEgRup.js";
2
+ import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-sVIgbrmg.js";
3
3
  import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-CvpSUU3O.js";
4
4
  import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-DiVFFlH3.js";
5
5
  //#region src/2d/library-sprite.ts
package/dist/3d.d.ts CHANGED
@@ -377,6 +377,9 @@ interface CreateGame3DOptions {
377
377
  seed?: number;
378
378
  fixedHz?: number;
379
379
  pixelRatio?: number;
380
+ /** Keep the frame rate by rendering fewer pixels while frames run slow
381
+ * (default ON). `false` pins the resolution — screenshots, capture, tests. */
382
+ adaptiveResolution?: boolean;
380
383
  resolveScene?: LoadSceneOptions["resolveScene"];
381
384
  /** @internal Test seam — replaces the Renderer3D construction. */
382
385
  _rendererFactory?: (engine: Engine, canvas: HTMLCanvasElement) => GameRenderer;
@@ -2466,6 +2469,14 @@ interface Renderer3DOptions {
2466
2469
  assets?: AssetStore3D;
2467
2470
  /** Defaults to `window.devicePixelRatio` capped at 2. */
2468
2471
  pixelRatio?: number;
2472
+ /**
2473
+ * Keep the frame rate by rendering at fewer pixels when frames run slow, and
2474
+ * hand them back when they recover (default ON). A retina display asks for
2475
+ * 4× the pixels; shaded water is fragment-heavy; the honest answer is fewer
2476
+ * pixels of the same world, only while it is actually needed. `false` pins
2477
+ * the resolution — for screenshots, video capture and pixel-diff tests.
2478
+ */
2479
+ adaptiveResolution?: boolean;
2469
2480
  }
2470
2481
  /**
2471
2482
  * WebGL presentation layer: subscribes to `engine.updated`, mirrors the active
@@ -2533,10 +2544,22 @@ declare class Renderer3D {
2533
2544
  private bloomCompositeScene;
2534
2545
  private bloomCompositeUniforms;
2535
2546
  constructor(opts: Renderer3DOptions);
2547
+ /** The pixel ratio the scene ASKED for — the governor scales this, never replaces it. */
2548
+ private readonly basePixelRatio;
2549
+ private readonly adaptive;
2550
+ private lastFrameAt;
2536
2551
  private readonly debugLines;
2537
2552
  private readonly selectionLines;
2538
2553
  private syncSelectionOutline;
2539
2554
  private syncDebugLines;
2555
+ /**
2556
+ * Frame-rate governor: measure the frame, and when the median of a window is
2557
+ * slow, render the same world at fewer pixels (and hand them back when it
2558
+ * recovers). Resizing the drawing buffer is all it takes — every render
2559
+ * target in the pipeline is keyed off `getDrawingBufferSize`, so the water
2560
+ * grab, the mirror and the bloom chain follow automatically.
2561
+ */
2562
+ private governResolution;
2540
2563
  private render;
2541
2564
  /**
2542
2565
  * Bloom post pass (only when `environment.bloom` is declared). LDR and safe:
package/dist/3d.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
- import { A as Water3D, F as StaticBody3D, I as Node3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D, z as WATER_MAX_RIPPLES } from "./gameplay-BJ5gXIks.js";
3
- import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-OyjhYUL3.js";
4
- import { A as QUARTER_PITCH, C as BoneAttachment3D, D as TERRAIN_THEMES, E as DEFAULT_TERRAIN_TEXTURE_BASE, M as keyboardIntensity, N as movementState, O as terrainThemeLayers, P as rigPose, S as BoneLookAt3D, T as Terrain3D, _ as Flowers3D, a as Trail3D, b as CharacterController3D, c as Particles3D, d as DirectionalLight3D, f as OmniLight3D, g as DENSITY_PRESETS, h as Foliage3D, i as Tree3D, j as cameraRelative, k as Joint3D, l as ModelInstance3D, m as MeshInstance3D, n as VOXEL_PALETTE, o as River3D, p as InstancedMesh3D, r as VoxelGrid3D, s as traceDownhillPath, t as registerNodes3D, u as LoftMesh3D, v as resolveFlowerDensity, w as Billboard3D, x as Camera3D, y as FLOWER_VARIETIES } from "./register-CpJORtm4.js";
2
+ import { A as Water3D, F as StaticBody3D, I as Node3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D, z as WATER_MAX_RIPPLES } from "./gameplay-DMaCGBBn.js";
3
+ import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-BzaDwZX8.js";
4
+ import { A as QUARTER_PITCH, C as BoneAttachment3D, D as TERRAIN_THEMES, E as DEFAULT_TERRAIN_TEXTURE_BASE, M as keyboardIntensity, N as movementState, O as terrainThemeLayers, P as rigPose, S as BoneLookAt3D, T as Terrain3D, _ as Flowers3D, a as Trail3D, b as CharacterController3D, c as Particles3D, d as DirectionalLight3D, f as OmniLight3D, g as DENSITY_PRESETS, h as Foliage3D, i as Tree3D, j as cameraRelative, k as Joint3D, l as ModelInstance3D, m as MeshInstance3D, n as VOXEL_PALETTE, o as River3D, p as InstancedMesh3D, r as VoxelGrid3D, s as traceDownhillPath, t as registerNodes3D, u as LoftMesh3D, v as resolveFlowerDensity, w as Billboard3D, x as Camera3D, y as FLOWER_VARIETIES } from "./register-BnWbwRsc.js";
5
5
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
6
- import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-CpIB9C-F.js";
6
+ import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-Cwi7-zug.js";
7
7
  //#region src/3d/environment-runtime.ts
8
8
  /**
9
9
  * Live environment editing — the renderer re-applies `scene.environment`
@@ -3,10 +3,10 @@ import { _ as registerBehavior, n as loadScene } from "./loader-BZqOKfI2.js";
3
3
  import { l as AudioPlayer, u as Engine } from "./register-nObreUQR.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
6
- import { I as Node3D, N as PhysicsBody3D, R as createCausticsQuad, n as registerGameplayBehaviors } from "./gameplay-BJ5gXIks.js";
6
+ import { I as Node3D, N as PhysicsBody3D, R as createCausticsQuad, n as registerGameplayBehaviors } from "./gameplay-DMaCGBBn.js";
7
7
  import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
8
- import { d as DirectionalLight3D, l as ModelInstance3D, t as registerNodes3D, x as Camera3D } from "./register-CpJORtm4.js";
9
- import { n as enablePhysics3D } from "./physics-3d-CpIB9C-F.js";
8
+ import { d as DirectionalLight3D, l as ModelInstance3D, t as registerNodes3D, x as Camera3D } from "./register-BnWbwRsc.js";
9
+ import { n as enablePhysics3D } from "./physics-3d-Cwi7-zug.js";
10
10
  import { ACESFilmicToneMapping, AmbientLight, Box3, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
11
11
  import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
12
12
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
@@ -126,6 +126,50 @@ var AssetStore3D = class {
126
126
  }
127
127
  };
128
128
  //#endregion
129
+ //#region src/3d/adaptive-resolution.ts
130
+ const DEFAULTS = {
131
+ slowMs: 20,
132
+ fastMs: 13,
133
+ minScale: .6,
134
+ step: .15,
135
+ window: 45
136
+ };
137
+ var AdaptiveResolution = class {
138
+ opts;
139
+ samples = [];
140
+ scale = 1;
141
+ constructor(options = {}) {
142
+ this.opts = {
143
+ ...DEFAULTS,
144
+ ...options
145
+ };
146
+ }
147
+ /** The fraction of the requested pixel ratio to render at right now. */
148
+ current() {
149
+ return this.scale;
150
+ }
151
+ /**
152
+ * Feed one frame's duration. Returns the new scale when it CHANGED (the
153
+ * caller then resizes), else null — so a steady frame rate costs nothing.
154
+ */
155
+ push(frameMs) {
156
+ if (!(frameMs > 0) || !Number.isFinite(frameMs)) return null;
157
+ this.samples.push(frameMs);
158
+ if (this.samples.length < this.opts.window) return null;
159
+ const sorted = [...this.samples].sort((a, b) => a - b);
160
+ const median = sorted[Math.floor(sorted.length / 2)];
161
+ this.samples = [];
162
+ const before = this.scale;
163
+ if (median > this.opts.slowMs) this.scale = Math.max(this.opts.minScale, this.scale - this.opts.step);
164
+ else if (median < this.opts.fastMs && this.scale < 1) this.scale = Math.min(1, this.scale + this.opts.step);
165
+ return this.scale === before ? null : this.scale;
166
+ }
167
+ /** Forget the window (a paused or backgrounded game is not "slow"). */
168
+ reset() {
169
+ this.samples = [];
170
+ }
171
+ };
172
+ //#endregion
129
173
  //#region src/3d/bloom/bloom-composite.ts
130
174
  /**
131
175
  * HDR bloom post pass:
@@ -1421,7 +1465,9 @@ var Renderer3D = class {
1421
1465
  canvas: opts.canvas,
1422
1466
  antialias: rendering.antialias
1423
1467
  });
1468
+ this.basePixelRatio = rendering.pixelRatio;
1424
1469
  this.webgl.setPixelRatio(rendering.pixelRatio);
1470
+ this.adaptive = opts.adaptiveResolution === false ? null : new AdaptiveResolution();
1425
1471
  this.webgl.shadowMap.enabled = true;
1426
1472
  this.webgl.shadowMap.type = PCFShadowMap;
1427
1473
  this.webgl.toneMapping = ACESFilmicToneMapping;
@@ -1445,6 +1491,10 @@ var Renderer3D = class {
1445
1491
  this.threeScene.add(this.selectionLines);
1446
1492
  this.disconnect = this.engine.updated.connect(() => this.render());
1447
1493
  }
1494
+ /** The pixel ratio the scene ASKED for — the governor scales this, never replaces it. */
1495
+ basePixelRatio;
1496
+ adaptive;
1497
+ lastFrameAt = 0;
1448
1498
  debugLines;
1449
1499
  selectionLines;
1450
1500
  syncSelectionOutline() {
@@ -1468,9 +1518,35 @@ var Renderer3D = class {
1468
1518
  this.debugLines.visible = vertices !== null;
1469
1519
  if (vertices) this.debugLines.geometry.setAttribute("position", new BufferAttribute(vertices, 3));
1470
1520
  }
1521
+ /**
1522
+ * Frame-rate governor: measure the frame, and when the median of a window is
1523
+ * slow, render the same world at fewer pixels (and hand them back when it
1524
+ * recovers). Resizing the drawing buffer is all it takes — every render
1525
+ * target in the pipeline is keyed off `getDrawingBufferSize`, so the water
1526
+ * grab, the mirror and the bloom chain follow automatically.
1527
+ */
1528
+ governResolution() {
1529
+ const gov = this.adaptive;
1530
+ if (!gov) return;
1531
+ const now = globalThis.performance?.now?.() ?? 0;
1532
+ const prev = this.lastFrameAt;
1533
+ this.lastFrameAt = now;
1534
+ if (prev === 0) return;
1535
+ const frameMs = now - prev;
1536
+ if (frameMs > 500) {
1537
+ gov.reset();
1538
+ return;
1539
+ }
1540
+ const scale = gov.push(frameMs);
1541
+ if (scale === null) return;
1542
+ this.webgl.setPixelRatio(this.basePixelRatio * scale);
1543
+ const size = this.lastSize;
1544
+ if (size.w > 0 && size.h > 0) this.webgl.setSize(size.w, size.h, false);
1545
+ }
1471
1546
  render() {
1472
1547
  const scene = this.engine.scene;
1473
1548
  if (!scene) return;
1549
+ this.governResolution();
1474
1550
  this.syncDebugLines();
1475
1551
  this.syncSelectionOutline();
1476
1552
  if (scene.assets && !this.loadedAssetScenes.has(scene)) {
@@ -1937,7 +2013,8 @@ async function createGame3D(opts) {
1937
2013
  const renderer = opts._rendererFactory ? opts._rendererFactory(engine, opts.canvas) : new Renderer3D({
1938
2014
  canvas: opts.canvas,
1939
2015
  engine,
1940
- ...opts.pixelRatio !== void 0 ? { pixelRatio: opts.pixelRatio } : {}
2016
+ ...opts.pixelRatio !== void 0 ? { pixelRatio: opts.pixelRatio } : {},
2017
+ ...opts.adaptiveResolution !== void 0 ? { adaptiveResolution: opts.adaptiveResolution } : {}
1941
2018
  });
1942
2019
  if (opts.debug ?? false) {
1943
2020
  const host = opts.touchContainer ?? opts.canvas.parentElement ?? (typeof document !== "undefined" ? document.body : null);
@@ -3,7 +3,7 @@ import { _ as registerBehavior, n as loadScene, o as computeViewport, s as resol
3
3
  import { l as AudioPlayer, u as Engine } from "./register-nObreUQR.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
6
- import { n as registerGameplayBehaviors } from "./gameplay-BJ5gXIks.js";
6
+ import { n as registerGameplayBehaviors } from "./gameplay-DMaCGBBn.js";
7
7
  import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-CvpSUU3O.js";
8
8
  import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
9
9
  import { n as enablePhysics2D } from "./physics-2d-DiVFFlH3.js";
@@ -1014,6 +1014,8 @@ uniform float uCausticsAbove;
1014
1014
  // incanto presets: per-preset reflection ceiling (the old hard 0.6) — lakes
1015
1015
  // push toward mirror, pools stay glassy-clear.
1016
1016
  uniform float uReflectivityMax;
1017
+ /** Screen-space width (px) of the shoreline dissolve — see the waterline block. */
1018
+ const float SHORE_FADE_PX = 26.0;
1017
1019
  // incanto: 1 while the camera EYE is under THIS surface — the underside is a
1018
1020
  // different optical world (Snell's window), not the top shaded darker.
1019
1021
  uniform float uCameraSubmerged;
@@ -1163,11 +1165,16 @@ vec3 detailNormal(vec3 n, vec2 worldXZ, float strength, float camDist) {
1163
1165
  float fadeFine = 1.0 - smoothstep(18.0, 80.0, camDist);
1164
1166
  float fadeMicro = 1.0 - smoothstep(8.0, 34.0, camDist);
1165
1167
  vec2 dMid = normalize(vec2(0.62, 0.78));
1166
- vec2 slope =
1167
- octaveSlope(worldXZ, WIND_DIR, 6.0, uTime) * (0.085 * fadeCoarse)
1168
- + octaveSlope(worldXZ, dMid, 2.2, uTime) * (0.10 * fadeMid)
1169
- + octavePair(worldXZ, WIND_DIR, 0.9, uTime) * (0.055 * fadeFine)
1170
- + octavePair(worldXZ, dMid, 0.38, uTime) * (0.042 * fadeMicro);
1168
+ // LOD: the fades used to scale a result that had already been PAID FOR — a
1169
+ // pixel of horizon water evaluated four noise octaves to multiply three of
1170
+ // them by zero. Branch instead. camDist barely varies across a quad, so
1171
+ // these are warp-coherent: distant water genuinely skips the work, and the
1172
+ // near look is bit-identical because a skipped layer was contributing < 1%.
1173
+ vec2 slope = vec2(0.0);
1174
+ if (fadeCoarse > 0.01) slope += octaveSlope(worldXZ, WIND_DIR, 6.0, uTime) * (0.085 * fadeCoarse);
1175
+ if (fadeMid > 0.01) slope += octaveSlope(worldXZ, dMid, 2.2, uTime) * (0.10 * fadeMid);
1176
+ if (fadeFine > 0.01) slope += octavePair(worldXZ, WIND_DIR, 0.9, uTime) * (0.055 * fadeFine);
1177
+ if (fadeMicro > 0.01) slope += octavePair(worldXZ, dMid, 0.38, uTime) * (0.042 * fadeMicro);
1171
1178
  slope *= strength * 3.0; // spectrum norm: default strength 0.26 ≈ calm-lively
1172
1179
  return normalize(vec3(n.x - slope.x, n.y, n.z - slope.y));
1173
1180
  }
@@ -1181,9 +1188,9 @@ vec3 glossNormal(vec3 n, vec2 worldXZ, float strength, float camDist) {
1181
1188
  float fadeCoarse = 1.0 - smoothstep(90.0, 420.0, camDist);
1182
1189
  float fadeMid = 1.0 - smoothstep(40.0, 170.0, camDist);
1183
1190
  vec2 dMid = normalize(vec2(0.62, 0.78));
1184
- vec2 slope =
1185
- octaveSlope(worldXZ, WIND_DIR, 6.0, uTime) * (0.085 * fadeCoarse)
1186
- + octaveSlope(worldXZ, dMid, 2.2, uTime) * (0.075 * fadeMid);
1191
+ vec2 slope = vec2(0.0);
1192
+ if (fadeCoarse > 0.01) slope += octaveSlope(worldXZ, WIND_DIR, 6.0, uTime) * (0.085 * fadeCoarse);
1193
+ if (fadeMid > 0.01) slope += octaveSlope(worldXZ, dMid, 2.2, uTime) * (0.075 * fadeMid);
1187
1194
  slope *= strength * 3.0;
1188
1195
  return normalize(vec3(n.x - slope.x, n.y, n.z - slope.y));
1189
1196
  }
@@ -1540,9 +1547,11 @@ void main() {
1540
1547
  if (uCausticsAbove > 0.0) {
1541
1548
  vec3 bottomWorld = cameraPosition + viewDirection * refrT;
1542
1549
  float below = uWaterCenter.y - bottomWorld.y;
1543
- if (below > 0.05) {
1550
+ float causticFade = 1.0 - smoothstep(28.0, 90.0, camDist);
1551
+ // LOD: two 5-iteration caustic loops are the most expensive thing in
1552
+ // this shader — never run them for a contribution that has faded out
1553
+ if (below > 0.05 && causticFade > 0.01) {
1544
1554
  float reach = smoothstep(0.05, 0.5, below) * (1.0 - smoothstep(0.2, 9.0, below));
1545
- float causticFade = 1.0 - smoothstep(28.0, 90.0, camDist);
1546
1555
  float cw = caustic(bottomWorld.xz * 0.42, uTime * 0.7) * 0.65
1547
1556
  + caustic(bottomWorld.xz * 0.72 + 30.0, uTime * 0.55) * 0.35;
1548
1557
  sceneColor += vec3(0.87, 0.96, 1.0) * (cw * uCausticsAbove * reach * causticFade);
@@ -1683,10 +1692,14 @@ void main() {
1683
1692
  if (uWhitecaps > 0.0 && uWaveEnvelope > 0.001) {
1684
1693
  float crestN = (vWorldPosition.y - uWaterCenter.y) / uWaveEnvelope;
1685
1694
  float cap = smoothstep(0.5, 0.85, crestN);
1686
- vec2 capUV = vWorldPosition.xz * 0.3;
1687
- float capNoise = noise(capUV * 2.2 + uTime * vec2(0.10, 0.06)) * 0.65
1688
- + noise(capUV * 6.5 - uTime * vec2(0.05, 0.14)) * 0.35;
1689
1695
  float capFade = 1.0 - smoothstep(220.0, 640.0, camDist);
1696
+ // LOD: no cap, no distance, no noise — the patch field is two octaves
1697
+ float capNoise = 0.0;
1698
+ if (capFade > 0.01 && max(cap, vFold) > 0.001) {
1699
+ vec2 capUV = vWorldPosition.xz * 0.3;
1700
+ capNoise = noise(capUV * 2.2 + uTime * vec2(0.10, 0.06)) * 0.65
1701
+ + noise(capUV * 6.5 - uTime * vec2(0.05, 0.14)) * 0.35;
1702
+ }
1690
1703
  // height says WHERE the crest is; the Jacobian says where it is actually
1691
1704
  // breaking. Taking the max keeps the tuned height-based caps and adds the
1692
1705
  // ones physics insists on — the pinched crest faces about to spill.
@@ -1758,9 +1771,32 @@ void main() {
1758
1771
  float shoreT = camDist * (linearSceneDepth / max(linearWaterDepth, 1e-4));
1759
1772
  float bedY = cameraPosition.y + viewDirection.y * shoreT;
1760
1773
  float column = max(uWaterCenter.y - bedY, 0.0);
1761
- // ragged, so the dissolve edge is a wash line and not a contour
1762
- float lacy = noise(vWorldPosition.xz * 1.9) * 0.4 + noise(vWorldPosition.xz * 5.3) * 0.2;
1763
- shoreDissolve = smoothstep(0.0, 0.18 + lacy * 0.12, column);
1774
+
1775
+ // The fade must be measured in PIXELS, not metres. A beach's water column
1776
+ // climbs from nothing to knee-deep in centimetres of ground on a steep
1777
+ // shore and in metres on a flat one; a fixed depth ramp is invisible on
1778
+ // the first and swallows the second. fwidth() says how fast the column is
1779
+ // changing across this very pixel, so dividing by it turns the ramp into
1780
+ // a constant screen-wide band — the cut edge cannot survive it at any
1781
+ // slope, from any camera.
1782
+ float perPixel = max(fwidth(column), 1e-5);
1783
+ float edgeT = clamp(column / (perPixel * SHORE_FADE_PX), 0.0, 1.0);
1784
+
1785
+ // the wash LINE is ragged and it breathes: a still contour reads as a cut
1786
+ // no matter how soft it is
1787
+ float lacy = noise(vWorldPosition.xz * 2.3 + uTime * 0.05) * 0.6
1788
+ + noise(vWorldPosition.xz * 6.1 - uTime * 0.08) * 0.4;
1789
+ float surge = uShoreWaves > 0.0
1790
+ ? (sin(uTime * 0.5) * 0.5 + sin(uTime * 0.31 + 1.7) * 0.5) * 0.22 * uShoreWaves
1791
+ : 0.0;
1792
+ edgeT = clamp(edgeT * (1.0 + surge) + (lacy - 0.5) * 0.45, 0.0, 1.0);
1793
+ shoreDissolve = smoothstep(0.0, 1.0, edgeT);
1794
+
1795
+ // FROTH gathers where the sheet is thinning out — the last band of a wash
1796
+ // is white, and that white is what makes water read as meeting ground
1797
+ // rather than ending at a line.
1798
+ float hem = (1.0 - edgeT) * smoothstep(0.25, 0.75, lacy) * 0.85;
1799
+ foamAmount = max(foamAmount, hem * shoreDissolve);
1764
1800
  }
1765
1801
 
1766
1802
  // 🔥 Final fundamental solution: Apply depth mask
package/dist/gameplay.js CHANGED
@@ -1,2 +1,2 @@
1
- import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-BJ5gXIks.js";
1
+ import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-DMaCGBBn.js";
2
2
  export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
package/dist/index.js CHANGED
@@ -295,6 +295,6 @@ function newUid() {
295
295
  //#endregion
296
296
  //#region src/index.ts
297
297
  /** Engine version. Kept in sync with package.json by the release pipeline. */
298
- const VERSION = "0.23.0";
298
+ const VERSION = "0.25.0";
299
299
  //#endregion
300
300
  export { AudioBuses, AudioPlayer, Behavior, CONST_REF_KEY, Engine, HudLayer, IncantoError, InputMap, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, Scene, SceneTree, SfxEngine, Signal, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiText, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, mergeStaticSignals, newUid, parseNodePath, preloadUrls, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveConstants, resolveRendering, resolveViewport, serializeNode, spatialGain, spatialPan, startRecording, synthSfx };
@@ -1,8 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
- import { I as Node3D, L as validateCollider3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D } from "./gameplay-BJ5gXIks.js";
3
+ import { I as Node3D, L as validateCollider3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D } from "./gameplay-DMaCGBBn.js";
4
4
  import { n as registerDebugSource } from "./debug-draw-CZmOYjL2.js";
5
- import { T as Terrain3D, k as Joint3D } from "./register-CpJORtm4.js";
5
+ import { T as Terrain3D, k as Joint3D } from "./register-BnWbwRsc.js";
6
6
  import { Euler, Quaternion } from "three";
7
7
  //#region src/3d/physics/physics-3d.ts
8
8
  var physics_3d_exports = /* @__PURE__ */ __exportAll({
package/dist/react.js CHANGED
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
156
156
  pointer: latest.pointer,
157
157
  ...keyboard !== void 0 ? { keyboard } : {}
158
158
  };
159
- const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-OyjhYUL3.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-DrcEgRup.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-BzaDwZX8.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-sVIgbrmg.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;
@@ -4,7 +4,7 @@ import { t as Rng } from "./rng-DP-SR7eg.js";
4
4
  import { i as getNodeSchema, l as registerNode } from "./registry-BVJ2HbCn.js";
5
5
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
6
6
  import { i as applyParticlePreset, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-Bw7hB93B.js";
7
- import { A as Water3D, B as colliderFootDrop, F as StaticBody3D, I as Node3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D } from "./gameplay-BJ5gXIks.js";
7
+ import { A as Water3D, B as colliderFootDrop, F as StaticBody3D, I as Node3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D } from "./gameplay-DMaCGBBn.js";
8
8
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
9
9
  import { AdditiveBlending, AnimationClip, AnimationMixer, Box3, BoxGeometry, BufferAttribute, BufferGeometry, CanvasTexture, CapsuleGeometry, Color, ConeGeometry, CylinderGeometry, DataTexture, DirectionalLight, DoubleSide, DynamicDrawUsage, Euler, Group, IcosahedronGeometry, ImageBitmapLoader, InstancedBufferAttribute, InstancedMesh, LinearFilter, LoopOnce, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshPhysicalMaterial, MeshStandardMaterial, NearestFilter, NoBlending, NormalBlending, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, QuaternionKeyframeTrack, RGBADepthPacking, RGBAFormat, RepeatWrapping, SRGBColorSpace, ShaderChunk, ShaderMaterial, SphereGeometry, Texture, TextureLoader, UniformsLib, UniformsUtils, Vector3, Vector4, VectorKeyframeTrack } from "three";
10
10
  import { clone } from "three/addons/utils/SkeletonUtils.js";
package/dist/test.js CHANGED
@@ -4,9 +4,9 @@ import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as auditScene } from "./audit-C6rMyict.js";
5
5
  import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
6
6
  import { i as getNodeSchema, s as mergeStaticProps } from "./registry-BVJ2HbCn.js";
7
- import { n as registerGameplayBehaviors } from "./gameplay-BJ5gXIks.js";
7
+ import { n as registerGameplayBehaviors } from "./gameplay-DMaCGBBn.js";
8
8
  import { t as registerNodes2D } from "./register-CvpSUU3O.js";
9
- import { t as registerNodes3D } from "./register-CpJORtm4.js";
9
+ import { t as registerNodes3D } from "./register-BnWbwRsc.js";
10
10
  import { t as registerNodesNet } from "./register-BFFE1Mh1.js";
11
11
  //#region src/test/index.ts
12
12
  /**
@@ -127,7 +127,7 @@ async function runScript(json, opts) {
127
127
  const { enablePhysics2D } = await import("./physics-2d-DiVFFlH3.js").then((n) => n.r);
128
128
  await enablePhysics2D(engine);
129
129
  } else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
130
- const { enablePhysics3D } = await import("./physics-3d-CpIB9C-F.js").then((n) => n.r);
130
+ const { enablePhysics3D } = await import("./physics-3d-Cwi7-zug.js").then((n) => n.r);
131
131
  await enablePhysics3D(engine);
132
132
  }
133
133
  const failures = [];
@@ -232,7 +232,7 @@ async function createPlaySession(json, opts = {}) {
232
232
  const { enablePhysics2D } = await import("./physics-2d-DiVFFlH3.js").then((n) => n.r);
233
233
  await enablePhysics2D(engine);
234
234
  } else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
235
- const { enablePhysics3D } = await import("./physics-3d-CpIB9C-F.js").then((n) => n.r);
235
+ const { enablePhysics3D } = await import("./physics-3d-Cwi7-zug.js").then((n) => n.r);
236
236
  await enablePhysics3D(engine);
237
237
  }
238
238
  const stepMs = 1e3 / (opts.fixedHz ?? 60);
@@ -1 +1 @@
1
- import{t as e}from"./index-D-bWhSDj.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;reconnecting=!1;disposed=!1;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.raw.connected?Promise.resolve(!0):(this.disposed=!1,this.rawConnect())}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){this.disposed=!0;for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){if(!this.disposed&&!this.reconnecting){this.reconnecting=!0;try{await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}finally{this.reconnecting=!1}}}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};
1
+ import{t as e}from"./index-Did0QjRa.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;reconnecting=!1;disposed=!1;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.raw.connected?Promise.resolve(!0):(this.disposed=!1,this.rawConnect())}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){this.disposed=!0;for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){if(!this.disposed&&!this.reconnecting){this.reconnecting=!0;try{await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}finally{this.reconnecting=!1}}}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};