incanto 0.28.0 → 0.30.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 +44 -0
- package/bin/incanto-editor.mjs +219 -29
- package/dist/2d.d.ts +54 -6
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +353 -16
- package/dist/3d.js +5 -5
- package/dist/{behavior-CPibUfnH.d.ts → behavior-BAc0erXF.d.ts} +21 -0
- package/dist/{create-game-71TJjW1T.js → create-game-5z_QVtLx.js} +59 -16
- package/dist/{create-game-BRgWpNsa.js → create-game-DuBTv2zI.js} +276 -49
- package/dist/debug-draw-BM3DsvtT.js +18 -0
- package/dist/debug.d.ts +51 -6
- package/dist/debug.js +242 -19
- package/dist/editor-switch-B0wB_DSr.d.ts +59 -0
- package/dist/editor.d.ts +49 -0
- package/dist/editor.js +8554 -0
- package/dist/{gameplay-DEG-TP7D.js → gameplay-Cfr6aFZ1.js} +219 -40
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +31 -3
- package/dist/index.js +2 -2
- package/dist/{loader-DILt9PGC.d.ts → loader-B242FF6N.d.ts} +1 -1
- package/dist/net.d.ts +1 -1
- package/dist/net.js +2 -2
- package/dist/{pathfinding-RWYkNKx9.d.ts → pathfinding-BwD974Ss.d.ts} +1 -1
- package/dist/{physics-2d-DiVFFlH3.js → physics-2d-3kOQCtgd.js} +88 -3
- package/dist/{physics-3d--y5clE2j.js → physics-3d-CeRH-Ff_.js} +475 -9
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-nObreUQR.js → register-BTg0EM7s.js} +35 -3
- package/dist/{register-6R75AC7-.js → register-DJ0SByQg.js} +5550 -5073
- package/dist/{register-CvpSUU3O.js → register-DWcWq4QG.js} +22 -2
- package/dist/{register-BFFE1Mh1.js → register-MelqEdza.js} +1 -1
- package/dist/teardown-ByzfDPyu.js +240 -0
- package/dist/test.d.ts +35 -3
- package/dist/test.js +104 -10
- package/dist/vite.d.ts +139 -1
- package/dist/vite.js +355 -4
- package/editor/assets/{agent8-CojUfCXN.js → agent8-CAp0i5qn.js} +1 -1
- package/editor/assets/debug-BoEYfbqK.js +2 -0
- package/editor/assets/index-BO6WU8by.js +10696 -0
- package/editor/index.html +3 -157
- package/package.json +3 -2
- package/schemas/scene.schema.json +102 -4
- package/skills/incanto-3d-models.md +38 -0
- package/skills/incanto-assets.md +13 -0
- package/skills/incanto-building-3d-games.md +83 -6
- package/skills/incanto-editor.md +212 -8
- package/skills/incanto-environment.md +65 -1
- package/skills/incanto-node-reference.md +33 -1
- package/skills/incanto-physics-and-input.md +60 -6
- package/skills/incanto-verifying-your-game.md +57 -2
- package/templates-app/beacon-isle-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/vite.config.ts +7 -0
- package/templates-app/tps-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/vite.config.ts +7 -0
- package/templates-app/village-quest-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/vite.config.ts +7 -0
- package/dist/debug-draw-CZmOYjL2.js +0 -13
- package/editor/assets/index-D6RQgROR.js +0 -8330
- package/editor/assets/index-D8QvwvOm.css +0 -1
|
@@ -413,6 +413,13 @@ void main() {
|
|
|
413
413
|
* Foliage/flowers additionally BELONG out of the depth pass: alpha-cutout
|
|
414
414
|
* quads depth-write their full rectangle and wind-displaced blades render
|
|
415
415
|
* unposed, so their depth was always a lie the foam mask had to survive.
|
|
416
|
+
*
|
|
417
|
+
* Individual OBJECTS opt out the same way by setting
|
|
418
|
+
* `userData.incantoWaterPassSkip = true` — for a node that is only PARTLY
|
|
419
|
+
* unfit, like a tree whose trunk is honest geometry but whose leaves are the
|
|
420
|
+
* same alpha-cutout lie (a depth-only override drops both their alphaTest and
|
|
421
|
+
* their wind displacement, so every leaf card stamps a solid unposed
|
|
422
|
+
* rectangle; a canopy became a wall of near-depth over the water behind it).
|
|
416
423
|
*/
|
|
417
424
|
const SKIPPED_IN_WATER_PASSES = new Set([
|
|
418
425
|
"Foliage3D",
|
|
@@ -430,6 +437,11 @@ function hideForWaterPass(root, out) {
|
|
|
430
437
|
if (typeof root?.traverse !== "function") return out;
|
|
431
438
|
root.traverse((obj) => {
|
|
432
439
|
if (!obj.visible) return;
|
|
440
|
+
if (obj.userData?.incantoWaterPassSkip === true) {
|
|
441
|
+
obj.visible = false;
|
|
442
|
+
out.push(obj);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
433
445
|
const type = (obj.userData?.incantoNode)?.constructor?.typeName;
|
|
434
446
|
if (type && SKIPPED_IN_WATER_PASSES.has(type)) {
|
|
435
447
|
obj.visible = false;
|
|
@@ -1280,6 +1292,24 @@ float rippleFoam(vec2 p) {
|
|
|
1280
1292
|
}
|
|
1281
1293
|
|
|
1282
1294
|
// Enhanced depth-based foam effect calculation
|
|
1295
|
+
/**
|
|
1296
|
+
* Linear scene depth at uv, cross-filtered over the pre-pass' own texels.
|
|
1297
|
+
* The pre-pass runs at HALF resolution: a single tap quantises the shoreline
|
|
1298
|
+
* to half-res steps, and any camera motion — even sub-pixel — reshuffles which
|
|
1299
|
+
* texel wins, so every depth-driven band (foam, contact line) BOILS while you
|
|
1300
|
+
* move. Averaging the 4-neighbour cross moves smoothly instead. The shoreline
|
|
1301
|
+
* fade has filtered this way all along (waterColumnAt callers); the foam and
|
|
1302
|
+
* edge bands were still on one tap each.
|
|
1303
|
+
*/
|
|
1304
|
+
float filteredSceneDepth(vec2 uv) {
|
|
1305
|
+
float c = linearizeDepth(texture2D(uDepthTexture, uv).r, uCameraNear, uCameraFar);
|
|
1306
|
+
float l = linearizeDepth(texture2D(uDepthTexture, uv - vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
|
|
1307
|
+
float r = linearizeDepth(texture2D(uDepthTexture, uv + vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
|
|
1308
|
+
float d = linearizeDepth(texture2D(uDepthTexture, uv - vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
|
|
1309
|
+
float u = linearizeDepth(texture2D(uDepthTexture, uv + vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
|
|
1310
|
+
return c * 0.4 + (l + r + d + u) * 0.15;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1283
1313
|
float calculateFoam() {
|
|
1284
1314
|
// incanto v2: foam rides the always-on scene depth pass — both gates
|
|
1285
1315
|
if (uEnableFoam < 0.5 || uUseSceneDepth < 0.5) return 0.0;
|
|
@@ -1287,12 +1317,10 @@ float calculateFoam() {
|
|
|
1287
1317
|
// Convert screen coordinates to texture coordinates
|
|
1288
1318
|
vec2 screenUV = vScreenPosition.xy / vScreenPosition.w * 0.5 + 0.5;
|
|
1289
1319
|
|
|
1290
|
-
// Sample scene depth value from depth texture
|
|
1291
|
-
float sceneDepth = texture2D(uDepthTexture, screenUV).r;
|
|
1292
1320
|
float waterDepth = gl_FragCoord.z;
|
|
1293
1321
|
|
|
1294
|
-
// Convert to linear depth
|
|
1295
|
-
float linearSceneDepth =
|
|
1322
|
+
// Convert to linear depth (scene side cross-filtered, see above)
|
|
1323
|
+
float linearSceneDepth = filteredSceneDepth(screenUV);
|
|
1296
1324
|
float linearWaterDepth = linearizeDepth(waterDepth, uCameraNear, uCameraFar);
|
|
1297
1325
|
|
|
1298
1326
|
// Calculate depth difference (distance between water and other objects)
|
|
@@ -1344,10 +1372,9 @@ float calculateEdgeBlending() {
|
|
|
1344
1372
|
if (uEnableFoam < 0.5 || uUseSceneDepth < 0.5 || uEdgeIntensity <= 0.0) return 0.0;
|
|
1345
1373
|
|
|
1346
1374
|
vec2 screenUV = vScreenPosition.xy / vScreenPosition.w * 0.5 + 0.5;
|
|
1347
|
-
float sceneDepth = texture2D(uDepthTexture, screenUV).r;
|
|
1348
1375
|
float waterDepth = gl_FragCoord.z;
|
|
1349
1376
|
|
|
1350
|
-
float linearSceneDepth =
|
|
1377
|
+
float linearSceneDepth = filteredSceneDepth(screenUV); // cross-filtered — one tap boils
|
|
1351
1378
|
float linearWaterDepth = linearizeDepth(waterDepth, uCameraNear, uCameraFar);
|
|
1352
1379
|
|
|
1353
1380
|
float depthDiff = abs(linearSceneDepth - linearWaterDepth);
|
|
@@ -1379,7 +1406,24 @@ void main() {
|
|
|
1379
1406
|
linearSceneDepth = linearizeDepth(sceneDepth, uCameraNear, uCameraFar);
|
|
1380
1407
|
|
|
1381
1408
|
float depthDiff = linearWaterDepth - linearSceneDepth;
|
|
1382
|
-
|
|
1409
|
+
|
|
1410
|
+
// The depth texture DELIBERATELY holds above-water geometry too (the
|
|
1411
|
+
// depth-completion pass, for cutouts) — so a sample NEARER than this
|
|
1412
|
+
// fragment cannot be a bed, and treating it as "column 0" rendered the
|
|
1413
|
+
// water as clear glass over the bare bed. Behind a tree the half-res
|
|
1414
|
+
// pre-pass smears the canopy across the gaps between its own leaves, and
|
|
1415
|
+
// whole stretches of pond turned to glass — "the water isn't drawn". The
|
|
1416
|
+
// column reads the DEEPEST of the 5-tap neighborhood instead; when even
|
|
1417
|
+
// that is contaminated, depth is UNKNOWN (-1) and the flat-ramp fallback
|
|
1418
|
+
// paints honest murk.
|
|
1419
|
+
float seamL = linearizeDepth(texture2D(uDepthTexture, screenUV - vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
|
|
1420
|
+
float seamR = linearizeDepth(texture2D(uDepthTexture, screenUV + vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
|
|
1421
|
+
float seamD = linearizeDepth(texture2D(uDepthTexture, screenUV - vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
|
|
1422
|
+
float seamU = linearizeDepth(texture2D(uDepthTexture, screenUV + vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
|
|
1423
|
+
float seamFarthest = max(max(seamL, seamR), max(seamD, max(seamU, linearSceneDepth)));
|
|
1424
|
+
if (seamFarthest > linearWaterDepth + 0.02) {
|
|
1425
|
+
waterViewDepth = seamFarthest - linearWaterDepth;
|
|
1426
|
+
}
|
|
1383
1427
|
|
|
1384
1428
|
// waterline dither: on a near-flat beach one half-res depth texel spans a
|
|
1385
1429
|
// wide screen block at grazing view, so these thresholds cut staircase
|
|
@@ -1390,10 +1434,21 @@ void main() {
|
|
|
1390
1434
|
float seamJitter =
|
|
1391
1435
|
(noise(vWorldPosition.xz * 3.1) * 0.65 + noise(vWorldPosition.xz * 8.7) * 0.35 - 0.5) * 0.035;
|
|
1392
1436
|
|
|
1437
|
+
// This discard exists for ONE thing: dithering the waterline seam — an
|
|
1438
|
+
// organic jittered edge in the few centimetres where half-res depth
|
|
1439
|
+
// quantisation would carve staircase notches. TRUE occlusion is the
|
|
1440
|
+
// hardware depth test's job (depthTest is on, and anything opaque wrote
|
|
1441
|
+
// full-res depth): a water fragment that reached this shader is, by the
|
|
1442
|
+
// full-res buffer's word, VISIBLE. So a pre-pass sample far in front of it
|
|
1443
|
+
// can only be the half-res lie — a tree canopy smearing near-depth across
|
|
1444
|
+
// the gaps between its own leaves — and acting on it erased the whole pond
|
|
1445
|
+
// when viewed through or past a tree. Only the seam band may discard.
|
|
1446
|
+
bool seamEligible = depthDiff < 0.45;
|
|
1447
|
+
|
|
1393
1448
|
// 🎯 Two-step approach
|
|
1394
1449
|
// Step 1: Discard completely overlapping parts
|
|
1395
1450
|
float hardDiscardThreshold = 0.1 + seamJitter;
|
|
1396
|
-
if (depthDiff > hardDiscardThreshold) {
|
|
1451
|
+
if (seamEligible && depthDiff > hardDiscardThreshold) {
|
|
1397
1452
|
discard; // Completely remove definitively overlapping pixels
|
|
1398
1453
|
}
|
|
1399
1454
|
|
|
@@ -1401,7 +1456,7 @@ void main() {
|
|
|
1401
1456
|
float softDiscardStart = -0.05 + seamJitter; // Start fade slightly ahead
|
|
1402
1457
|
float softDiscardEnd = hardDiscardThreshold;
|
|
1403
1458
|
|
|
1404
|
-
if (depthDiff > softDiscardStart) {
|
|
1459
|
+
if (seamEligible && depthDiff > softDiscardStart) {
|
|
1405
1460
|
depthMask = 1.0 - smoothstep(softDiscardStart, softDiscardEnd, depthDiff);
|
|
1406
1461
|
// At border: 0 (fully transparent) ~ 1 (fully opaque)
|
|
1407
1462
|
}
|
|
@@ -2160,18 +2215,22 @@ function validateCollider3D(collider, nodeName) {
|
|
|
2160
2215
|
return;
|
|
2161
2216
|
}
|
|
2162
2217
|
if (shape === "heightfield") return;
|
|
2163
|
-
|
|
2218
|
+
if (shape === "auto") return;
|
|
2219
|
+
throw new IncantoError("BAD_FORMAT", `Collider on '${nodeName}': "shape" must be 'auto', 'box', 'sphere', 'capsule', 'trimesh', or 'heightfield', got ${JSON.stringify(shape)}.`);
|
|
2164
2220
|
}
|
|
2165
2221
|
//#endregion
|
|
2166
2222
|
//#region src/3d/nodes/node-3d.ts
|
|
2167
|
-
/**
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2223
|
+
/** The first ground-answering node in the tree, searched depth-first. */
|
|
2224
|
+
function firstGroundIn(root) {
|
|
2225
|
+
const probe = root;
|
|
2226
|
+
if (probe.constructor?.typeName === "Terrain3D" && typeof probe.heightAt === "function") return root;
|
|
2227
|
+
for (const child of root.children) {
|
|
2228
|
+
const found = firstGroundIn(child);
|
|
2229
|
+
if (found) return found;
|
|
2230
|
+
}
|
|
2231
|
+
return null;
|
|
2232
|
+
}
|
|
2233
|
+
var Node3D = class Node3D extends Node {
|
|
2175
2234
|
static typeName = "Node3D";
|
|
2176
2235
|
static props = {
|
|
2177
2236
|
position: { default: [
|
|
@@ -2202,7 +2261,8 @@ var Node3D = class extends Node {
|
|
|
2202
2261
|
"effects",
|
|
2203
2262
|
"overlay"
|
|
2204
2263
|
]
|
|
2205
|
-
}
|
|
2264
|
+
},
|
|
2265
|
+
snapToGround: { default: null }
|
|
2206
2266
|
};
|
|
2207
2267
|
position = [
|
|
2208
2268
|
0,
|
|
@@ -2236,6 +2296,23 @@ var Node3D = class extends Node {
|
|
|
2236
2296
|
renderOrder = 0;
|
|
2237
2297
|
/** Named draw-order band; `renderOrder` is the fine offset inside it. */
|
|
2238
2298
|
orderGroup = "default";
|
|
2299
|
+
/**
|
|
2300
|
+
* PUT THIS ON THE GROUND. `true` sets the node's Y from the terrain under it
|
|
2301
|
+
* at load; a number does the same and lifts it by that many metres (a post
|
|
2302
|
+
* whose origin sits at its middle wants half its height).
|
|
2303
|
+
*
|
|
2304
|
+
* Computing a Y by hand is the single most repeated mistake in scene
|
|
2305
|
+
* authoring, and it is not a careless one: the number is right when it is
|
|
2306
|
+
* written and wrong the moment anything reshapes the ground — a river carving
|
|
2307
|
+
* its bed, a basin, a different seed, a generator that mirrors the carve
|
|
2308
|
+
* slightly differently. Every "floating boulder", "buried player" and
|
|
2309
|
+
* "bridge posts in mid-air" in this engine's history is that drift.
|
|
2310
|
+
*
|
|
2311
|
+
* With this the ground is asked at load, in the same frame the terrain is
|
|
2312
|
+
* built, and there is nothing to drift from. `null` (default) leaves `y`
|
|
2313
|
+
* exactly as authored — for anything that genuinely hangs in the air.
|
|
2314
|
+
*/
|
|
2315
|
+
snapToGround = null;
|
|
2239
2316
|
/** What three actually sorts by: band base + renderOrder. */
|
|
2240
2317
|
get effectiveRenderOrder() {
|
|
2241
2318
|
return effectiveOrder(this.orderGroup, this.renderOrder);
|
|
@@ -2249,6 +2326,56 @@ var Node3D = class extends Node {
|
|
|
2249
2326
|
*/
|
|
2250
2327
|
_interpPrev = null;
|
|
2251
2328
|
_interpCurr = null;
|
|
2329
|
+
onReady() {
|
|
2330
|
+
this.applyGroundSnap();
|
|
2331
|
+
}
|
|
2332
|
+
/**
|
|
2333
|
+
* Resolve {@link snapToGround} against the terrain under this node.
|
|
2334
|
+
*
|
|
2335
|
+
* Ancestor transforms are translation-only here, the same contract heightAt
|
|
2336
|
+
* and the physics heightfield already follow, so the world height converts
|
|
2337
|
+
* back to a local Y by subtracting the parents' offset.
|
|
2338
|
+
*/
|
|
2339
|
+
applyGroundSnap() {
|
|
2340
|
+
if (this.snapToGround === null || this.snapToGround === false) return;
|
|
2341
|
+
const lift = this.snapToGround === true ? 0 : this.snapToGround;
|
|
2342
|
+
const terrain = firstGroundIn(this.getRoot());
|
|
2343
|
+
if (!terrain) return;
|
|
2344
|
+
let ox = this.position[0] ?? 0;
|
|
2345
|
+
let oy = 0;
|
|
2346
|
+
let oz = this.position[2] ?? 0;
|
|
2347
|
+
for (let n = this.parent; n; n = n.parent) {
|
|
2348
|
+
if (!(n instanceof Node3D)) continue;
|
|
2349
|
+
const yaw = (n.rotation[1] ?? 0) * Math.PI / 180;
|
|
2350
|
+
if (yaw !== 0) {
|
|
2351
|
+
const c = Math.cos(yaw);
|
|
2352
|
+
const sn = Math.sin(yaw);
|
|
2353
|
+
const rx = ox * c + oz * sn;
|
|
2354
|
+
const rz = -ox * sn + oz * c;
|
|
2355
|
+
ox = rx;
|
|
2356
|
+
oz = rz;
|
|
2357
|
+
}
|
|
2358
|
+
ox += n.position[0] ?? 0;
|
|
2359
|
+
oy += n.position[1] ?? 0;
|
|
2360
|
+
oz += n.position[2] ?? 0;
|
|
2361
|
+
}
|
|
2362
|
+
const worldX = ox;
|
|
2363
|
+
const worldZ = oz;
|
|
2364
|
+
this.position = [
|
|
2365
|
+
this.position[0] ?? 0,
|
|
2366
|
+
terrain.heightAt(worldX, worldZ) + lift - oy,
|
|
2367
|
+
this.position[2] ?? 0
|
|
2368
|
+
];
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* @internal The backing object IF it has been built — never builds one.
|
|
2372
|
+
*
|
|
2373
|
+
* `_ensureObject3D()` is the wrong call during teardown: it allocates a mesh
|
|
2374
|
+
* for a node that never rendered, purely so the next line can dispose it.
|
|
2375
|
+
*/
|
|
2376
|
+
get _builtObject3D() {
|
|
2377
|
+
return this._object3D;
|
|
2378
|
+
}
|
|
2252
2379
|
/** @internal The backing three object (lazily created). */
|
|
2253
2380
|
_ensureObject3D() {
|
|
2254
2381
|
if (!this._object3D) {
|
|
@@ -2291,7 +2418,38 @@ var Node3D = class extends Node {
|
|
|
2291
2418
|
* `{shape:'capsule', radius, height}` (meters, y-up).
|
|
2292
2419
|
*/
|
|
2293
2420
|
var PhysicsBody3D = class extends Node3D {
|
|
2294
|
-
static props = { collider: {
|
|
2421
|
+
static props = { collider: {
|
|
2422
|
+
default: {},
|
|
2423
|
+
variants: {
|
|
2424
|
+
tag: "shape",
|
|
2425
|
+
byTag: {
|
|
2426
|
+
auto: { shape: "auto" },
|
|
2427
|
+
box: {
|
|
2428
|
+
shape: "box",
|
|
2429
|
+
size: [
|
|
2430
|
+
1,
|
|
2431
|
+
1,
|
|
2432
|
+
1
|
|
2433
|
+
]
|
|
2434
|
+
},
|
|
2435
|
+
sphere: {
|
|
2436
|
+
shape: "sphere",
|
|
2437
|
+
radius: .5
|
|
2438
|
+
},
|
|
2439
|
+
capsule: {
|
|
2440
|
+
shape: "capsule",
|
|
2441
|
+
radius: .35,
|
|
2442
|
+
height: 1
|
|
2443
|
+
},
|
|
2444
|
+
trimesh: {
|
|
2445
|
+
shape: "trimesh",
|
|
2446
|
+
vertices: [],
|
|
2447
|
+
indices: []
|
|
2448
|
+
},
|
|
2449
|
+
heightfield: { shape: "heightfield" }
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
} };
|
|
2295
2453
|
/** The unified collision model: every collider participant can emit these. */
|
|
2296
2454
|
static signals = ["triggerEnter", "triggerExit"];
|
|
2297
2455
|
_collider = {};
|
|
@@ -2561,24 +2719,24 @@ const WATER_PRESETS = {
|
|
|
2561
2719
|
}
|
|
2562
2720
|
},
|
|
2563
2721
|
pond: {
|
|
2564
|
-
waveHeight: .
|
|
2722
|
+
waveHeight: .006,
|
|
2565
2723
|
swell: 0,
|
|
2566
2724
|
whitecaps: 0,
|
|
2567
|
-
absorption: .
|
|
2568
|
-
opacity: .
|
|
2569
|
-
detailStrength: .
|
|
2570
|
-
sunIntensity: .
|
|
2725
|
+
absorption: .85,
|
|
2726
|
+
opacity: .93,
|
|
2727
|
+
detailStrength: .14,
|
|
2728
|
+
sunIntensity: .7,
|
|
2571
2729
|
colors: {
|
|
2572
|
-
trough: "#
|
|
2573
|
-
surface: "#
|
|
2574
|
-
peak: "#
|
|
2730
|
+
trough: "#1c2e1e",
|
|
2731
|
+
surface: "#39543c",
|
|
2732
|
+
peak: "#6f8a68"
|
|
2575
2733
|
},
|
|
2576
|
-
color: "#
|
|
2734
|
+
color: "#39543c",
|
|
2577
2735
|
causticsAbove: 0,
|
|
2578
2736
|
reflectionInterval: 800,
|
|
2579
2737
|
_look: {
|
|
2580
|
-
fresnelScale: 1.
|
|
2581
|
-
reflectivityMax: .
|
|
2738
|
+
fresnelScale: 1.05,
|
|
2739
|
+
reflectivityMax: .78,
|
|
2582
2740
|
edgeFade: false
|
|
2583
2741
|
}
|
|
2584
2742
|
}
|
|
@@ -2731,6 +2889,7 @@ var Water3D = class Water3D extends Node3D {
|
|
|
2731
2889
|
mirrorInterval: { default: 33 },
|
|
2732
2890
|
swellSteepness: { default: .35 },
|
|
2733
2891
|
foam: { default: true },
|
|
2892
|
+
reflectivity: { default: null },
|
|
2734
2893
|
interaction: { default: true },
|
|
2735
2894
|
splash: { default: true },
|
|
2736
2895
|
sunDirection: { default: [
|
|
@@ -2790,6 +2949,16 @@ var Water3D = class Water3D extends Node3D {
|
|
|
2790
2949
|
reflectionInterval = 1e3;
|
|
2791
2950
|
/** Depth-texture shoreline foam — rides the always-on fancy scene pass. */
|
|
2792
2951
|
foam = true;
|
|
2952
|
+
/**
|
|
2953
|
+
* How much sky the surface throws back at grazing angles, 0..1 — `null`
|
|
2954
|
+
* keeps the preset's own ceiling.
|
|
2955
|
+
*
|
|
2956
|
+
* Turn it DOWN when the sky is blown out: a bright overcast reflected at 0.78
|
|
2957
|
+
* is a sheet of white, and a pool doing that next to a River3D (which paints
|
|
2958
|
+
* an AUTHORED sky palette and so never blows out) reads as two unrelated
|
|
2959
|
+
* materials meeting at a line. Turn it up for a mirror-calm lake.
|
|
2960
|
+
*/
|
|
2961
|
+
reflectivity = null;
|
|
2793
2962
|
/** Bodies crossing the surface emit signals and raise ripples. */
|
|
2794
2963
|
interaction = true;
|
|
2795
2964
|
/** Splash WHITEWATER: the shaders paint expanding surface foam where bodies
|
|
@@ -2880,9 +3049,10 @@ var Water3D = class Water3D extends Node3D {
|
|
|
2880
3049
|
/** Per-preset internal look constants (fresnel curve, reflection ceiling). */
|
|
2881
3050
|
presetLook() {
|
|
2882
3051
|
const look = WATER_PRESETS[this.preset]?._look;
|
|
3052
|
+
const asked = this.rp("reflectivity");
|
|
2883
3053
|
return {
|
|
2884
3054
|
fresnelScale: look?.fresnelScale ?? FANCY_LOOK.fresnelScale,
|
|
2885
|
-
reflectivityMax: look?.reflectivityMax ?? .6,
|
|
3055
|
+
reflectivityMax: typeof asked === "number" ? Math.min(Math.max(asked, 0), 1) : look?.reflectivityMax ?? .6,
|
|
2886
3056
|
edgeFade: look?.edgeFade ?? true,
|
|
2887
3057
|
foamRangeScale: look?.foamRangeScale ?? 1,
|
|
2888
3058
|
foamIntensity: look?.foamIntensity ?? FOAM_LOOK.intensity
|
|
@@ -2962,6 +3132,9 @@ var Water3D = class Water3D extends Node3D {
|
|
|
2962
3132
|
mirrorCamera = new PerspectiveCamera();
|
|
2963
3133
|
mirrorMatrix = new Matrix4();
|
|
2964
3134
|
lastMirrorAt = 0;
|
|
3135
|
+
/** Camera pose the current mirror image was rendered from (see redraw). */
|
|
3136
|
+
lastMirrorEye = new Vector3(Infinity, Infinity, Infinity);
|
|
3137
|
+
lastMirrorQuat = new Quaternion();
|
|
2965
3138
|
_createObject3D() {
|
|
2966
3139
|
return new Mesh();
|
|
2967
3140
|
}
|
|
@@ -3044,7 +3217,8 @@ var Water3D = class Water3D extends Node3D {
|
|
|
3044
3217
|
if (!this.mirrorTarget) this.mirrorTarget = new WebGLRenderTarget(mirrorW, mirrorH, {
|
|
3045
3218
|
depthBuffer: true,
|
|
3046
3219
|
stencilBuffer: false,
|
|
3047
|
-
type: HalfFloatType
|
|
3220
|
+
type: HalfFloatType,
|
|
3221
|
+
samples: 4
|
|
3048
3222
|
});
|
|
3049
3223
|
const surfaceY = worldTranslationOf(this).y;
|
|
3050
3224
|
const cam = ctx.camera;
|
|
@@ -3065,11 +3239,15 @@ var Water3D = class Water3D extends Node3D {
|
|
|
3065
3239
|
mirrorCam.lookAt(mirrorTargetScratch);
|
|
3066
3240
|
mirrorCam.updateMatrixWorld(true);
|
|
3067
3241
|
mirrorCam.updateProjectionMatrix();
|
|
3068
|
-
|
|
3069
|
-
this.
|
|
3070
|
-
this.
|
|
3071
|
-
if (this.lastMirrorAt === 0 || now - this.lastMirrorAt >= this.rp("mirrorInterval")) {
|
|
3242
|
+
cam.getWorldPosition(mirrorPrevEyeCheck);
|
|
3243
|
+
const moved = mirrorPrevEyeCheck.distanceToSquared(this.lastMirrorEye) > 1e-6 || Math.abs(mirrorQuatScratch.dot(this.lastMirrorQuat)) < .9999995;
|
|
3244
|
+
if (this.lastMirrorAt === 0 || moved || now - this.lastMirrorAt >= this.rp("mirrorInterval")) {
|
|
3072
3245
|
this.lastMirrorAt = now;
|
|
3246
|
+
this.lastMirrorEye.copy(mirrorPrevEyeCheck);
|
|
3247
|
+
this.lastMirrorQuat.copy(mirrorQuatScratch);
|
|
3248
|
+
this.mirrorMatrix.set(.5, 0, 0, .5, 0, .5, 0, .5, 0, 0, .5, .5, 0, 0, 0, 1);
|
|
3249
|
+
this.mirrorMatrix.multiply(mirrorCam.projectionMatrix);
|
|
3250
|
+
this.mirrorMatrix.multiply(mirrorCam.matrixWorldInverse);
|
|
3073
3251
|
const prevMirrorTarget = ctx.gl.getRenderTarget();
|
|
3074
3252
|
const prevMirrorClip = ctx.gl.clippingPlanes;
|
|
3075
3253
|
const wasMirrorVisible = mesh.visible;
|
|
@@ -3172,9 +3350,9 @@ var Water3D = class Water3D extends Node3D {
|
|
|
3172
3350
|
} : null;
|
|
3173
3351
|
}
|
|
3174
3352
|
free() {
|
|
3175
|
-
const mesh = this.
|
|
3176
|
-
mesh
|
|
3177
|
-
mesh
|
|
3353
|
+
const mesh = this._builtObject3D;
|
|
3354
|
+
mesh?.geometry?.dispose();
|
|
3355
|
+
(mesh?.material)?.dispose?.();
|
|
3178
3356
|
this.cubeTarget?.dispose();
|
|
3179
3357
|
this.cubeTarget = null;
|
|
3180
3358
|
this.cubeCamera = null;
|
|
@@ -3697,6 +3875,7 @@ const UNDERWATER_MARGIN = 3;
|
|
|
3697
3875
|
/** Keep-below clip plane for the scene pre-pass (constant set per render):
|
|
3698
3876
|
* normal (0,−1,0) keeps y < constant — see the grab-clip note in _onRender3D. */
|
|
3699
3877
|
const mirrorEyeScratch = new Vector3();
|
|
3878
|
+
const mirrorPrevEyeCheck = new Vector3();
|
|
3700
3879
|
const mirrorTargetScratch = new Vector3();
|
|
3701
3880
|
const mirrorUpScratch = new Vector3();
|
|
3702
3881
|
const mirrorQuatScratch = new Quaternion();
|
package/dist/gameplay.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as JsonValue, s as JsonObject } from "./schema-CcoWb32N.js";
|
|
2
|
-
import { $ as Node, l as PropSchema, n as BehaviorCtor, t as Behavior, v as Engine } from "./behavior-
|
|
2
|
+
import { $ as Node, l as PropSchema, n as BehaviorCtor, t as Behavior, v as Engine } from "./behavior-BAc0erXF.js";
|
|
3
3
|
|
|
4
4
|
//#region src/gameplay/chase.d.ts
|
|
5
5
|
/**
|
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-
|
|
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-Cfr6aFZ1.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.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as Rng, c as JsonValue, d as jsonKind, i as SceneJson, l as jsonClone, n as NodeJson, o as JsonKind, r as SCENE_FORMAT, s as JsonObject, t as ConnectionJson, u as jsonEquals } from "./schema-CcoWb32N.js";
|
|
2
|
-
import { $ as Node, A as SfxPlayOptions, B as spatialPan, C as RendererStats, D as LogManager, E as LogLevel, F as ROLLOFF_MODELS, G as SynthOptions, H as SFX_PRESET_NAMES, I as RolloffModel, J as MusicManager, K as synthSfx, L as SpatialParams, M as Voice, N as VoicePreset, O as InputMap, P as Listener, Q as BusName, R as Vec3, S as GameStats, T as LogEntry, U as SfxParams, V as SFX_PRESETS, W as SfxWave, X as PlayMusicOptions, Y as MusicTrack, Z as AudioBuses, _ as registeredTypes, a as registerBehavior, b as Scheduler, c as PropDef, d as createNode, et as NodeLifecycle, f as getNodeSchema, g as registerNode, h as mergeStaticSignals, i as getBehavior, j as isAudioContextAvailable, k as SfxEngine, l as PropSchema, m as getNodeType, n as BehaviorCtor, nt as SignalListener, o as registeredBehaviors, p as getNodeSignals, q as MusicBackend, r as clearBehaviors, rt as SceneTree, s as NodeCtor, t as Behavior, tt as Signal, u as clearRegistry, v as Engine, w as Scene, x as EngineStats, y as EngineOptions, z as spatialGain } from "./behavior-
|
|
3
|
-
import { n as loadScene, t as LoadSceneOptions } from "./loader-
|
|
2
|
+
import { $ as Node, A as SfxPlayOptions, B as spatialPan, C as RendererStats, D as LogManager, E as LogLevel, F as ROLLOFF_MODELS, G as SynthOptions, H as SFX_PRESET_NAMES, I as RolloffModel, J as MusicManager, K as synthSfx, L as SpatialParams, M as Voice, N as VoicePreset, O as InputMap, P as Listener, Q as BusName, R as Vec3, S as GameStats, T as LogEntry, U as SfxParams, V as SFX_PRESETS, W as SfxWave, X as PlayMusicOptions, Y as MusicTrack, Z as AudioBuses, _ as registeredTypes, a as registerBehavior, b as Scheduler, c as PropDef, d as createNode, et as NodeLifecycle, f as getNodeSchema, g as registerNode, h as mergeStaticSignals, i as getBehavior, j as isAudioContextAvailable, k as SfxEngine, l as PropSchema, m as getNodeType, n as BehaviorCtor, nt as SignalListener, o as registeredBehaviors, p as getNodeSignals, q as MusicBackend, r as clearBehaviors, rt as SceneTree, s as NodeCtor, t as Behavior, tt as Signal, u as clearRegistry, v as Engine, w as Scene, x as EngineStats, y as EngineOptions, z as spatialGain } from "./behavior-BAc0erXF.js";
|
|
3
|
+
import { n as loadScene, t as LoadSceneOptions } from "./loader-B242FF6N.js";
|
|
4
4
|
import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-CwJ5rI_P.js";
|
|
5
|
-
import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-
|
|
5
|
+
import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-BwD974Ss.js";
|
|
6
6
|
import { i as auditScene, n as IncantoErrorCode, r as IncantoErrorDetails, t as IncantoError } from "./errors-1dXlIwoR.js";
|
|
7
7
|
|
|
8
8
|
//#region src/core/audio/crossfade.d.ts
|
|
@@ -47,8 +47,24 @@ declare class WebAudioMusicBackend implements MusicBackend {
|
|
|
47
47
|
*/
|
|
48
48
|
interface DebugLineSource {
|
|
49
49
|
readonly dimension: "2d" | "3d";
|
|
50
|
+
/**
|
|
51
|
+
* The engine whose world these lines describe.
|
|
52
|
+
*
|
|
53
|
+
* A page can hold more than one at a time — the editor runs its edit engine
|
|
54
|
+
* and a preview engine side by side — and a renderer must draw ITS world, not
|
|
55
|
+
* whichever source happens to be first in a module-level Set. Without this,
|
|
56
|
+
* one leaked physics instance paints its frozen colliders over everybody.
|
|
57
|
+
*/
|
|
58
|
+
readonly engine: object;
|
|
50
59
|
/** Toggle at runtime: `physics.debugDraw = true`. */
|
|
51
60
|
debugDraw: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Draw only the colliders under this node (its subtree, or the nearest body
|
|
63
|
+
* above it when the node itself carries none). Null draws every collider in
|
|
64
|
+
* the world — which on a real map is a terrain grid burying the one shape you
|
|
65
|
+
* meant to look at.
|
|
66
|
+
*/
|
|
67
|
+
debugScope: object | null;
|
|
52
68
|
/** Flat segment vertices (xy pairs in 2D px, xyz triples in 3D meters), or null while off. */
|
|
53
69
|
debugLines(): Float32Array | null;
|
|
54
70
|
}
|
|
@@ -108,6 +124,18 @@ declare class HudLayer extends Node {
|
|
|
108
124
|
_element: HTMLElement | null;
|
|
109
125
|
private readonly slots;
|
|
110
126
|
override onEnterTree(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Put the overlay where the page's owner says UI goes: `engine.uiHost`, or
|
|
129
|
+
* the window when nobody claims it (a game — a HUD covers the screen).
|
|
130
|
+
*
|
|
131
|
+
* Re-checked every frame because a scene's tree is BUILT before it is handed
|
|
132
|
+
* to an engine: at onEnterTree there is no engine to ask yet, so the first
|
|
133
|
+
* update is where the answer actually arrives. appendChild moves the element
|
|
134
|
+
* if it already landed somewhere else, so the HUD follows the host across a
|
|
135
|
+
* scene swap into a differently-hosted engine (game → editor) instead of
|
|
136
|
+
* being stranded on document.body over the inspector.
|
|
137
|
+
*/
|
|
138
|
+
private _mount;
|
|
111
139
|
override onExitTree(): void;
|
|
112
140
|
override update(): void;
|
|
113
141
|
/** @internal Widgets mount into per-anchor flex columns. */
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ as registerBehavior, a as SCENE_FORMAT, c as SceneTree, d as resolveConstants, f as Node, g as getBehavior, h as clearBehaviors, i as serializeNode, l as CONST_REF_KEY, m as Behavior, n as loadScene, o as computeViewport, p as parseNodePath, r as Scene, s as resolveViewport, u as isConstRef, v as registeredBehaviors, y as Signal } from "./loader-BZqOKfI2.js";
|
|
2
|
-
import { C as crossfadeGains, S as WebAudioMusicBackend, T as AudioBuses, _ as spatialPan, a as HudLayer, b as synthSfx, c as UiText, d as LogManager, f as InputMap, g as spatialGain, h as ROLLOFF_MODELS, i as UiDialogue, l as AudioPlayer, m as isAudioContextAvailable, n as Timer, o as UiBanner, p as SfxEngine, r as UiButton, s as UiBar, t as registerCoreNodes, u as Engine, v as SFX_PRESETS, w as fadeGain, x as MusicManager, y as SFX_PRESET_NAMES } from "./register-
|
|
2
|
+
import { C as crossfadeGains, S as WebAudioMusicBackend, T as AudioBuses, _ as spatialPan, a as HudLayer, b as synthSfx, c as UiText, d as LogManager, f as InputMap, g as spatialGain, h as ROLLOFF_MODELS, i as UiDialogue, l as AudioPlayer, m as isAudioContextAvailable, n as Timer, o as UiBanner, p as SfxEngine, r as UiButton, s as UiBar, t as registerCoreNodes, u as Engine, v as SFX_PRESETS, w as fadeGain, x as MusicManager, y as SFX_PRESET_NAMES } from "./register-BTg0EM7s.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { t as Rng } from "./rng-DP-SR7eg.js";
|
|
5
5
|
import { t as auditScene } from "./audit-C6rMyict.js";
|
|
@@ -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.
|
|
298
|
+
const VERSION = "0.30.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 };
|
package/dist/net.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CcoWb32N.js";
|
|
2
|
-
import { $ as Node, l as PropSchema, tt as Signal, v as Engine } from "./behavior-
|
|
2
|
+
import { $ as Node, l as PropSchema, tt as Signal, v as Engine } from "./behavior-BAc0erXF.js";
|
|
3
3
|
|
|
4
4
|
//#region src/net/types.d.ts
|
|
5
5
|
type Unsubscribe = () => void;
|
package/dist/net.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as loadScene } from "./loader-BZqOKfI2.js";
|
|
2
|
-
import { u as Engine } from "./register-
|
|
2
|
+
import { u as Engine } from "./register-BTg0EM7s.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
5
5
|
import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
|
|
6
|
-
import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-
|
|
6
|
+
import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-MelqEdza.js";
|
|
7
7
|
//#region src/net/loopback.ts
|
|
8
8
|
/**
|
|
9
9
|
* In-memory multiplayer hub implementing the SAME kernel contract as
|