incanto 0.69.0 → 0.71.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/bin/_behaviors-loader.mjs +22 -0
- package/bin/_read-json.mjs +28 -0
- package/bin/incanto-assets.mjs +5 -4
- package/bin/incanto-check.mjs +58 -31
- package/bin/incanto-editor.mjs +128 -5
- package/bin/incanto-env.mjs +3 -2
- package/bin/incanto-feel.mjs +24 -13
- package/bin/incanto-frame.mjs +8 -1
- package/bin/incanto-multiplay.mjs +11 -9
- package/bin/incanto-new.mjs +105 -10
- package/bin/incanto-play.mjs +158 -16
- package/bin/incanto-playtest.mjs +66 -23
- package/bin/incanto-skills.mjs +14 -2
- package/bin/incanto-verify.mjs +121 -102
- package/bin/incanto.mjs +3 -2
- package/dist/2d.d.ts +296 -34
- package/dist/2d.js +4 -4
- package/dist/3d.d.ts +157 -9
- package/dist/3d.js +8 -8
- package/dist/{agent8-CvsfVskX.js → agent8-Cfmd3ar_.js} +61 -8
- package/dist/{audio-player-C0j2k-yB.d.ts → audio-player-L8yccdMP.d.ts} +2 -2
- package/dist/{behavior-rm8vVzu_.d.ts → behavior-Dcz0fr1S.d.ts} +755 -46
- package/dist/{create-game-CwxEDlCm.js → create-game-CH4kt78v.js} +143 -60
- package/dist/{create-game-CvJ1wSRP.js → create-game-LCHBLfA4.js} +98 -48
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +2 -3
- package/dist/diagnostics-Cu85N3tL.d.ts +12 -0
- package/dist/{editor-switch-CnIOiyNJ.d.ts → editor-switch-CFU9mCec.d.ts} +22 -13
- package/dist/editor.js +1088 -882
- package/dist/env.d.ts +1 -1
- package/dist/env.js +5 -3
- package/dist/{environment-presets-BO-7XoFV.js → environment-presets-99BzP_L-.js} +298 -45
- package/dist/{frame-report-Dlq13Gyj.js → frame-report-D-_7YF2G.js} +1 -1
- package/dist/{gameplay-D1KixWK8.js → gameplay-CNULJvwh.js} +914 -214
- package/dist/gameplay.d.ts +254 -10
- package/dist/gameplay.js +2 -2
- package/dist/{heightmap-CRK0M4jT.js → heightmap-BYgD5Edk.js} +1 -1
- package/dist/index.d.ts +179 -13
- package/dist/index.js +10 -12
- package/dist/json-CfTjpvW8.js +67 -0
- package/dist/{loader-DadV2QCT.d.ts → loader-8-IHvbKD.d.ts} +2 -2
- package/dist/net.d.ts +27 -3
- package/dist/net.js +2 -2
- package/dist/{noise-CGUMx44x.js → noise-D3nPpmFg.js} +1 -1
- package/dist/{physics-2d-CF5yyjbj.js → physics-2d-FhrXUc6B.js} +136 -11
- package/dist/{physics-3d-DbYaEcZy.js → physics-3d-CFEGxBXZ.js} +104 -23
- package/dist/{teardown-CMqFVxO9.js → picking-DVo7fI13.js} +104 -13
- package/dist/react.d.ts +2 -2
- package/dist/react.js +2 -2
- package/dist/{register-DbVArsns.js → register-BYQCBySi.js} +429 -28
- package/dist/{register-mmfugpaQ.js → register-DuVdzxbq.js} +594 -831
- package/dist/{replay-BEHLDcQV.js → replay-DEvp3kyV.js} +224 -14
- package/dist/{replay-BQfkX4x7.d.ts → replay-F7IZHdFR.d.ts} +20 -3
- package/dist/{schema-B6ugCV1Q.d.ts → rng-Bb-IutXB.d.ts} +38 -21
- package/dist/{rng-DP-SR7eg.js → rng-CDOMybym.js} +22 -0
- package/dist/{loader-DZE7B07H.js → save-slots-Bvuh2p_r.js} +4619 -2318
- package/dist/{split-screen-Cg76MCV5.d.ts → split-screen-CIYf1zSf.d.ts} +36 -4
- package/dist/{split-screen-CauMwu6y.js → split-screen-eULetcg-.js} +159 -26
- package/dist/{sprite-animation-CY-mrr1L.js → sprite-animation-CqR2o3SA.js} +39 -8
- package/dist/{src-CYEu-_S9.js → src-_jk0qLsS.js} +2 -17
- package/dist/test-CmN54kWv.js +4264 -0
- package/dist/test.d.ts +639 -30
- package/dist/test.js +3 -3
- package/dist/touch-BnCyPA0G.js +519 -0
- package/dist/vite.d.ts +20 -37
- package/dist/vite.js +177 -7
- package/dist/{webgl-unavailable-Z5_g8x9H.js → webgl-unavailable-C8aDbGmR.js} +44 -0
- package/editor/assets/agent8-yLIEYHbd.js +1 -0
- package/editor/assets/{debug-CMULinBW.js → debug-u31w_yhq.js} +2 -2
- package/editor/assets/index-DK9xMGpW.js +11046 -0
- package/editor/index.html +1 -1
- package/package.json +4 -14
- package/schemas/scene.schema.json +34 -3
- package/skills/incanto-3d-character.md +14 -1
- package/skills/incanto-3d-models.md +12 -0
- package/skills/incanto-assets.md +9 -1
- package/skills/incanto-audio.md +8 -2
- package/skills/incanto-behaviors-and-scripts.md +105 -7
- package/skills/incanto-building-2d-games.md +97 -10
- package/skills/incanto-building-3d-games.md +58 -4
- package/skills/incanto-editor.md +46 -7
- package/skills/incanto-environment.md +19 -1
- package/skills/incanto-game-feel.md +70 -0
- package/skills/incanto-gameplay-behaviors.md +172 -20
- package/skills/incanto-hud.md +142 -7
- package/skills/incanto-localization.md +13 -5
- package/skills/incanto-multiplayer.md +83 -3
- package/skills/incanto-node-reference.md +239 -59
- package/skills/incanto-performance.md +52 -0
- package/skills/incanto-physics-and-input.md +123 -24
- package/skills/incanto-playtesting.md +112 -2
- package/skills/incanto-save-slots.md +217 -7
- package/skills/incanto-scene-json-authoring.md +69 -12
- package/skills/incanto-verifying-your-game.md +207 -10
- package/skills/incanto-web-integration.md +28 -0
- package/skills/incanto-your-first-game.md +3 -1
- package/templates-app/beacon-isle-3d/generate-world.ts +77 -9
- package/templates-app/beacon-isle-3d/package.json +2 -2
- package/templates-app/beacon-isle-3d/src/behaviors.ts +22 -0
- package/templates-app/beacon-isle-3d/src/game.scene.json +143 -600
- package/templates-app/beacon-isle-3d/src/main.ts +11 -1
- package/templates-app/beacon-isle-3d/tsconfig.json +1 -1
- package/templates-app/beacon-isle-3d/verify.ts +3 -1
- package/templates-app/molehill-2d/.incanto/playtest/lost-seed1.json +4277 -0
- package/templates-app/molehill-2d/PROJECT/Context.md +58 -0
- package/templates-app/molehill-2d/PROJECT/Requirements.md +39 -0
- package/templates-app/molehill-2d/PROJECT/Status.md +27 -0
- package/templates-app/molehill-2d/PROJECT/Structure.md +48 -0
- package/templates-app/molehill-2d/docs/project-2d-rules.md +44 -0
- package/templates-app/molehill-2d/index.html +73 -0
- package/templates-app/molehill-2d/package.json +23 -0
- package/templates-app/molehill-2d/src/behaviors.ts +198 -0
- package/templates-app/molehill-2d/src/game.scene.json +1205 -0
- package/templates-app/molehill-2d/src/main.ts +41 -0
- package/templates-app/molehill-2d/tsconfig.json +13 -0
- package/templates-app/molehill-2d/verify.ts +247 -0
- package/templates-app/molehill-2d/vite.config.ts +12 -0
- package/templates-app/platformer-2d/package.json +2 -2
- package/templates-app/platformer-2d/src/game.scene.json +178 -986
- package/templates-app/platformer-2d/src/main.ts +11 -1
- package/templates-app/platformer-2d/tsconfig.json +1 -1
- package/templates-app/star-survivor/package.json +2 -2
- package/templates-app/star-survivor/src/game.scene.json +43 -216
- package/templates-app/star-survivor/src/main.ts +11 -1
- package/templates-app/star-survivor/tsconfig.json +1 -1
- package/templates-app/tps-3d/PROJECT/Context.md +1 -1
- package/templates-app/tps-3d/package.json +2 -2
- package/templates-app/tps-3d/src/behaviors.ts +19 -1
- package/templates-app/tps-3d/src/game.scene.json +66 -263
- package/templates-app/tps-3d/src/main.ts +13 -2
- package/templates-app/tps-3d/tsconfig.json +1 -1
- package/templates-app/village-quest-3d/.incanto/playtest/swapped-seed1.json +1735 -0
- package/templates-app/village-quest-3d/package.json +2 -2
- package/templates-app/village-quest-3d/src/behaviors.ts +42 -4
- package/templates-app/village-quest-3d/src/grove.scene.json +68 -306
- package/templates-app/village-quest-3d/src/main.ts +11 -1
- package/templates-app/village-quest-3d/src/village.scene.json +202 -863
- package/templates-app/village-quest-3d/tsconfig.json +1 -1
- package/templates-app/village-quest-3d/verify.ts +14 -1
- package/dist/duplicate-Dj4L-5AA.js +0 -22
- package/dist/json-CwwhxQgb.js +0 -36
- package/dist/registry-CF70EArN.js +0 -212
- package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
- package/dist/test-D6ISmuFE.js +0 -2383
- package/dist/touch-DEAmqGdf.js +0 -225
- package/editor/assets/agent8-BDvaZMQJ.js +0 -1
- package/editor/assets/index-CZIRr5EL.js +0 -10958
package/dist/env.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as SceneJson, r as NodeJson, s as JsonObject, t as Rng } from "./rng-Bb-IutXB.js";
|
|
2
2
|
|
|
3
3
|
//#region src/env/arena.d.ts
|
|
4
4
|
/** Valid `generateArena` themes — drives the catalog options AND validation. */
|
package/dist/env.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
-
import { t as Rng } from "./rng-
|
|
3
|
-
import { t as createNoise2D } from "./noise-
|
|
4
|
-
import { t as buildHeightmap } from "./heightmap-
|
|
2
|
+
import { t as Rng } from "./rng-CDOMybym.js";
|
|
3
|
+
import { t as createNoise2D } from "./noise-D3nPpmFg.js";
|
|
4
|
+
import { t as buildHeightmap } from "./heightmap-BYgD5Edk.js";
|
|
5
5
|
//#region src/env/round.ts
|
|
6
6
|
/** 2-decimal rounding — keeps generated JSON readable without hurting determinism. */
|
|
7
7
|
function round2(value) {
|
|
@@ -147,6 +147,7 @@ function rock(index, x, z, rng, sizeRange, color) {
|
|
|
147
147
|
round2(radius * squash * .6),
|
|
148
148
|
z
|
|
149
149
|
],
|
|
150
|
+
snapToGround: false,
|
|
150
151
|
rotation: [
|
|
151
152
|
0,
|
|
152
153
|
rng.int(0, 359),
|
|
@@ -1400,6 +1401,7 @@ function generateClouds(opts) {
|
|
|
1400
1401
|
return {
|
|
1401
1402
|
name: "Clouds",
|
|
1402
1403
|
type: "Node3D",
|
|
1404
|
+
props: { snapToGround: false },
|
|
1403
1405
|
children
|
|
1404
1406
|
};
|
|
1405
1407
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Y as diagnose } from "./save-slots-Bvuh2p_r.js";
|
|
2
|
+
import { M as translateOn, t as registerCoreNodes } from "./register-DuVdzxbq.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { t as Rng } from "./rng-
|
|
5
|
-
import {
|
|
6
|
-
import { t as createNoise2D } from "./noise-
|
|
7
|
-
import { i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, r as
|
|
8
|
-
import { B as rejectScale,
|
|
4
|
+
import { t as Rng } from "./rng-CDOMybym.js";
|
|
5
|
+
import { c as getNodeSchema, h as didYouMean, p as registerNode } from "./touch-BnCyPA0G.js";
|
|
6
|
+
import { t as createNoise2D } from "./noise-D3nPpmFg.js";
|
|
7
|
+
import { i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, r as validateAnimations, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-CqR2o3SA.js";
|
|
8
|
+
import { B as rejectScale, D as effectRng, G as StaticBody3D, H as CharacterBody3D, K as Node3D, L as Water3D, Q as worldPosition, T as tolerateUnknownAction, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, X as colliderFootDrop, z as WaterCutout3D } from "./gameplay-CNULJvwh.js";
|
|
9
9
|
import { t as checkSheetGrid } from "./sheet-grid-Cea343VO.js";
|
|
10
|
-
import { n as splatWeights, t as buildHeightmap } from "./heightmap-
|
|
10
|
+
import { n as splatWeights, t as buildHeightmap } from "./heightmap-BYgD5Edk.js";
|
|
11
11
|
import { AdditiveBlending, AnimationClip, AnimationMixer, Box3, BoxGeometry, BufferAttribute, BufferGeometry, CanvasTexture, CapsuleGeometry, ClampToEdgeWrapping, Color, ConeGeometry, CylinderGeometry, DataTexture, DirectionalLight, DoubleSide, DynamicDrawUsage, Euler, Group, IcosahedronGeometry, ImageBitmapLoader, InstancedBufferAttribute, InstancedMesh, LinearFilter, LinearMipmapLinearFilter, LoopOnce, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshPhysicalMaterial, MeshStandardMaterial, NearestFilter, NoBlending, NoColorSpace, NormalBlending, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, QuaternionKeyframeTrack, RGBADepthPacking, RGBAFormat, RepeatWrapping, SRGBColorSpace, ShaderChunk, ShaderMaterial, SphereGeometry, Texture, TextureLoader, UniformsLib, UniformsUtils, Vector3, Vector4, VectorKeyframeTrack } from "three";
|
|
12
12
|
import { clone } from "three/addons/utils/SkeletonUtils.js";
|
|
13
13
|
//#region src/3d/textures.ts
|
|
@@ -141,7 +141,7 @@ var TextureCache3D = class {
|
|
|
141
141
|
return this.entries.size;
|
|
142
142
|
}
|
|
143
143
|
dispose() {
|
|
144
|
-
for (const entry of this.entries.values()) entry.texture
|
|
144
|
+
for (const entry of this.entries.values()) entry.texture?.dispose();
|
|
145
145
|
this.entries.clear();
|
|
146
146
|
}
|
|
147
147
|
};
|
|
@@ -716,7 +716,7 @@ var InstancedMesh3D = class extends Node3D {
|
|
|
716
716
|
for (let i = 0; i < this._transforms.length; i++) {
|
|
717
717
|
const row = this._transforms[i];
|
|
718
718
|
posScratch.set(row[0] ?? 0, row[1] ?? 0, row[2] ?? 0);
|
|
719
|
-
quatScratch$2.setFromAxisAngle(UP$
|
|
719
|
+
quatScratch$2.setFromAxisAngle(UP$2, (row[3] ?? 0) * Math.PI / 180);
|
|
720
720
|
const s = row[4] ?? 1;
|
|
721
721
|
scaleScratch$3.set(s, s, s);
|
|
722
722
|
matScratch.compose(posScratch, quatScratch$2, scaleScratch$3);
|
|
@@ -734,7 +734,7 @@ var InstancedMesh3D = class extends Node3D {
|
|
|
734
734
|
super.free();
|
|
735
735
|
}
|
|
736
736
|
};
|
|
737
|
-
const UP$
|
|
737
|
+
const UP$2 = new Vector3(0, 1, 0);
|
|
738
738
|
const posScratch = new Vector3();
|
|
739
739
|
const quatScratch$2 = new Quaternion();
|
|
740
740
|
const scaleScratch$3 = new Vector3();
|
|
@@ -835,6 +835,10 @@ var Joint3D = class extends Node3D {
|
|
|
835
835
|
const a = this.parent;
|
|
836
836
|
const b = this.getNodeOrNull(this.target);
|
|
837
837
|
if (!(b instanceof PhysicsBody3D)) throw new IncantoError("NODE_NOT_FOUND", `Joint3D '${this.name}': target '${this.target}' is not a physics body.`);
|
|
838
|
+
if (a === b) throw new IncantoError("BAD_FORMAT", `Joint3D '${this.name}': target '${this.target}' is the SAME body this joint hangs off, so it constrains nothing. A joint is a child of ONE body pointing at the OTHER — name the other body (e.g. "%Crate").`, {
|
|
839
|
+
prop: "target",
|
|
840
|
+
path: this.getPath()
|
|
841
|
+
});
|
|
838
842
|
return {
|
|
839
843
|
a,
|
|
840
844
|
b
|
|
@@ -1567,9 +1571,18 @@ var Terrain3D = class extends Node3D {
|
|
|
1567
1571
|
default: "island",
|
|
1568
1572
|
options: TERRAIN_THEMES
|
|
1569
1573
|
},
|
|
1570
|
-
roughness: {
|
|
1574
|
+
roughness: {
|
|
1575
|
+
default: .5,
|
|
1576
|
+
range: {
|
|
1577
|
+
min: 0,
|
|
1578
|
+
max: 1
|
|
1579
|
+
}
|
|
1580
|
+
},
|
|
1571
1581
|
detail: { default: 4 },
|
|
1572
|
-
flatThreshold: {
|
|
1582
|
+
flatThreshold: {
|
|
1583
|
+
default: .95,
|
|
1584
|
+
range: { min: 0 }
|
|
1585
|
+
},
|
|
1573
1586
|
islandEdge: { default: null },
|
|
1574
1587
|
layers: { default: [] },
|
|
1575
1588
|
textureBase: { default: DEFAULT_TERRAIN_TEXTURE_BASE },
|
|
@@ -1591,7 +1604,32 @@ var Terrain3D = class extends Node3D {
|
|
|
1591
1604
|
roughness = .5;
|
|
1592
1605
|
/** Detail octave count. */
|
|
1593
1606
|
detail = 4;
|
|
1594
|
-
/**
|
|
1607
|
+
/**
|
|
1608
|
+
* Snap-flatten a plateau within ±0.6 m of maxHeight·flatThreshold.
|
|
1609
|
+
*
|
|
1610
|
+
* A fraction of `maxHeight`, which is a SCALE and not the peak: heights
|
|
1611
|
+
* realize at roughly 3.5–8× it, so the default 0.95 puts the plateau near the
|
|
1612
|
+
* BOTTOM of the range rather than at the summit the name suggests. Measured
|
|
1613
|
+
* on an alpine 200 m terrain, seed 3:
|
|
1614
|
+
*
|
|
1615
|
+
* ```
|
|
1616
|
+
* maxHeight 28 realized 25..150 band at 26.6 = 18% of peak, 28 cells on it
|
|
1617
|
+
* maxHeight 8 realized 8..43 band at 7.6 = 18% of peak, 99 cells on it
|
|
1618
|
+
* ```
|
|
1619
|
+
*
|
|
1620
|
+
* It is doing something — that is a real terrace, and the ±0.6 m boundary
|
|
1621
|
+
* between snapped and unsnapped cells is what `examples/water-ocean-3d` calls
|
|
1622
|
+
* "a sawtooth band across the underwater flat". Read `heightAt` before
|
|
1623
|
+
* choosing a value, the way `maxHeight`'s own note says to.
|
|
1624
|
+
*
|
|
1625
|
+
* 0..1 is the useful range, and anything ABOVE 1 turns the snap off, because
|
|
1626
|
+
* the band then sits over every height the terrain realizes. `examples/water-ocean-3d` uses `20` for exactly that,
|
|
1627
|
+
* with a comment explaining why, which is how a `max: 1` on this prop was
|
|
1628
|
+
* caught the moment it was declared: at `maxHeight 0.1` the ±0.6 m band
|
|
1629
|
+
* swallows the whole field and rips a sawtooth across the underwater flat.
|
|
1630
|
+
* So the bound is `min: 0` and "off" is a documented idiom rather than an
|
|
1631
|
+
* accident.
|
|
1632
|
+
*/
|
|
1595
1633
|
flatThreshold = .95;
|
|
1596
1634
|
/** Wrap edges down into an island rim. null = theme decides (island: on). */
|
|
1597
1635
|
islandEdge = null;
|
|
@@ -1963,7 +2001,13 @@ const SHADOW_CONTACT_PUSH = .5;
|
|
|
1963
2001
|
var Sprite3D = class extends Node3D {
|
|
1964
2002
|
static typeName = "Sprite3D";
|
|
1965
2003
|
static props = {
|
|
1966
|
-
texture: {
|
|
2004
|
+
texture: {
|
|
2005
|
+
default: "",
|
|
2006
|
+
asset: {
|
|
2007
|
+
kind: "texture",
|
|
2008
|
+
urlOnly: true
|
|
2009
|
+
}
|
|
2010
|
+
},
|
|
1967
2011
|
size: { default: [1, 1] },
|
|
1968
2012
|
billboard: {
|
|
1969
2013
|
default: "y",
|
|
@@ -1975,10 +2019,22 @@ var Sprite3D = class extends Node3D {
|
|
|
1975
2019
|
},
|
|
1976
2020
|
anchor: { default: [.5, 0] },
|
|
1977
2021
|
tint: { default: "#ffffff" },
|
|
1978
|
-
opacity: {
|
|
2022
|
+
opacity: {
|
|
2023
|
+
default: 1,
|
|
2024
|
+
range: {
|
|
2025
|
+
min: 0,
|
|
2026
|
+
max: 1
|
|
2027
|
+
}
|
|
2028
|
+
},
|
|
1979
2029
|
flipX: { default: false },
|
|
1980
2030
|
pixelArt: { default: true },
|
|
1981
|
-
alphaTest: {
|
|
2031
|
+
alphaTest: {
|
|
2032
|
+
default: .5,
|
|
2033
|
+
range: {
|
|
2034
|
+
min: 0,
|
|
2035
|
+
max: 1
|
|
2036
|
+
}
|
|
2037
|
+
},
|
|
1982
2038
|
castShadow: { default: false },
|
|
1983
2039
|
shadowMode: {
|
|
1984
2040
|
default: "silhouette",
|
|
@@ -2052,7 +2108,7 @@ var Sprite3D = class extends Node3D {
|
|
|
2052
2108
|
mat.opacity = this.opacity;
|
|
2053
2109
|
mat.transparent = this.opacity < 1;
|
|
2054
2110
|
mat.alphaTest = this.alphaTest;
|
|
2055
|
-
quad.renderOrder = this.
|
|
2111
|
+
quad.renderOrder = this.effectiveRenderOrder;
|
|
2056
2112
|
const wantShadow = this.castShadow && tex !== null;
|
|
2057
2113
|
quad.castShadow = false;
|
|
2058
2114
|
if (wantShadow && this.shadowMode === "silhouette") {
|
|
@@ -2159,7 +2215,6 @@ var Sprite3D = class extends Node3D {
|
|
|
2159
2215
|
mesh.quaternion.copy(_parent.invert()).multiply(_target);
|
|
2160
2216
|
}
|
|
2161
2217
|
free() {
|
|
2162
|
-
this.loadedTexture?.dispose();
|
|
2163
2218
|
this.loadedTexture = null;
|
|
2164
2219
|
if (this.shadowMesh) {
|
|
2165
2220
|
this.shadowMesh.material.dispose();
|
|
@@ -2181,7 +2236,13 @@ var AnimatedSprite3D = class extends Sprite3D {
|
|
|
2181
2236
|
static typeName = "AnimatedSprite3D";
|
|
2182
2237
|
static signals = ["animationFinished"];
|
|
2183
2238
|
static props = {
|
|
2184
|
-
sheet: {
|
|
2239
|
+
sheet: {
|
|
2240
|
+
default: "",
|
|
2241
|
+
asset: {
|
|
2242
|
+
kind: "spritesheet",
|
|
2243
|
+
urlOnly: true
|
|
2244
|
+
}
|
|
2245
|
+
},
|
|
2185
2246
|
frameWidth: { default: 0 },
|
|
2186
2247
|
frameHeight: { default: 0 },
|
|
2187
2248
|
animations: { default: {} },
|
|
@@ -2223,7 +2284,7 @@ var AnimatedSprite3D = class extends Sprite3D {
|
|
|
2223
2284
|
this.playing = false;
|
|
2224
2285
|
}
|
|
2225
2286
|
onReady() {
|
|
2226
|
-
|
|
2287
|
+
validateAnimations(this.animations, this.name);
|
|
2227
2288
|
if (this.autoplay) this.play(this.autoplay);
|
|
2228
2289
|
}
|
|
2229
2290
|
update(dt) {
|
|
@@ -2560,15 +2621,15 @@ var BoneLookAt3D = class extends Node3D {
|
|
|
2560
2621
|
if (targetNode && Array.isArray(targetNode.position)) {
|
|
2561
2622
|
bone.updateWorldMatrix(true, false);
|
|
2562
2623
|
bone.getWorldPosition(bonePosScratch);
|
|
2563
|
-
targetScratch.set(targetNode.position[0] ?? 0, targetNode.position[1] ?? 0, targetNode.position[2] ?? 0);
|
|
2564
|
-
dirScratch$1.copy(targetScratch).sub(bonePosScratch);
|
|
2624
|
+
targetScratch$1.set(targetNode.position[0] ?? 0, targetNode.position[1] ?? 0, targetNode.position[2] ?? 0);
|
|
2625
|
+
dirScratch$1.copy(targetScratch$1).sub(bonePosScratch);
|
|
2565
2626
|
if (dirScratch$1.lengthSq() > 1e-6) {
|
|
2566
2627
|
dirScratch$1.normalize();
|
|
2567
2628
|
axisScratch.copy(AXES[this.forwardAxis] ?? PLUS_Z);
|
|
2568
2629
|
animForwardScratch.copy(axisScratch).applyQuaternion(bone.getWorldQuaternion(quatScratch$1));
|
|
2569
2630
|
if (animForwardScratch.angleTo(dirScratch$1) < this.maxAngleDeg * Math.PI / 180) {
|
|
2570
2631
|
engaged = true;
|
|
2571
|
-
lookMatrixScratch.lookAt(bonePosScratch, targetScratch, UP);
|
|
2632
|
+
lookMatrixScratch.lookAt(bonePosScratch, targetScratch$1, UP$1);
|
|
2572
2633
|
desiredScratch.setFromRotationMatrix(lookMatrixScratch);
|
|
2573
2634
|
correctionScratch.setFromUnitVectors(axisScratch, MINUS_Z);
|
|
2574
2635
|
desiredScratch.multiply(correctionScratch);
|
|
@@ -2591,11 +2652,11 @@ const AXES = {
|
|
|
2591
2652
|
"+z": new Vector3(0, 0, 1),
|
|
2592
2653
|
"-z": new Vector3(0, 0, -1)
|
|
2593
2654
|
};
|
|
2594
|
-
const UP = new Vector3(0, 1, 0);
|
|
2655
|
+
const UP$1 = new Vector3(0, 1, 0);
|
|
2595
2656
|
const PLUS_Z = new Vector3(0, 0, 1);
|
|
2596
2657
|
const MINUS_Z = new Vector3(0, 0, -1);
|
|
2597
2658
|
const bonePosScratch = new Vector3();
|
|
2598
|
-
const targetScratch = new Vector3();
|
|
2659
|
+
const targetScratch$1 = new Vector3();
|
|
2599
2660
|
const dirScratch$1 = new Vector3();
|
|
2600
2661
|
const axisScratch = new Vector3();
|
|
2601
2662
|
const animForwardScratch = new Vector3();
|
|
@@ -2606,6 +2667,13 @@ const parentScratch = new Quaternion();
|
|
|
2606
2667
|
const quatScratch$1 = new Quaternion();
|
|
2607
2668
|
//#endregion
|
|
2608
2669
|
//#region src/3d/nodes/camera-3d.ts
|
|
2670
|
+
const RAD2DEG$1 = 180 / Math.PI;
|
|
2671
|
+
/** World up. The whole fix is that this argument exists. */
|
|
2672
|
+
const UP = new Vector3(0, 1, 0);
|
|
2673
|
+
const eyeScratch = new Vector3();
|
|
2674
|
+
const targetScratch = new Vector3();
|
|
2675
|
+
const lookScratch = new Matrix4();
|
|
2676
|
+
const eulerScratch$1 = new Euler(0, 0, 0, "XYZ");
|
|
2609
2677
|
/**
|
|
2610
2678
|
* A perspective camera. Mark exactly one camera `current: true`; with none
|
|
2611
2679
|
* marked, the renderer falls back to the first camera in tree order.
|
|
@@ -2614,15 +2682,79 @@ const quatScratch$1 = new Quaternion();
|
|
|
2614
2682
|
var Camera3D = class extends Node3D {
|
|
2615
2683
|
static typeName = "Camera3D";
|
|
2616
2684
|
static props = {
|
|
2617
|
-
fov: {
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2685
|
+
fov: {
|
|
2686
|
+
default: 60,
|
|
2687
|
+
range: {
|
|
2688
|
+
min: 0,
|
|
2689
|
+
max: 180,
|
|
2690
|
+
exclusiveMin: true,
|
|
2691
|
+
exclusiveMax: true
|
|
2692
|
+
}
|
|
2693
|
+
},
|
|
2694
|
+
near: {
|
|
2695
|
+
default: .1,
|
|
2696
|
+
range: {
|
|
2697
|
+
min: 0,
|
|
2698
|
+
exclusiveMin: true
|
|
2699
|
+
}
|
|
2700
|
+
},
|
|
2701
|
+
far: {
|
|
2702
|
+
default: 1e3,
|
|
2703
|
+
range: {
|
|
2704
|
+
min: 0,
|
|
2705
|
+
exclusiveMin: true
|
|
2706
|
+
}
|
|
2707
|
+
},
|
|
2708
|
+
current: { default: false },
|
|
2709
|
+
lookAt: {
|
|
2710
|
+
default: "",
|
|
2711
|
+
nodePath: true
|
|
2712
|
+
}
|
|
2621
2713
|
};
|
|
2714
|
+
/** A frustum whose planes are the wrong way round draws nothing at all. */
|
|
2715
|
+
static validateJson(node) {
|
|
2716
|
+
const self = node;
|
|
2717
|
+
if (self.near >= self.far) throw new IncantoError("PROP_TYPE_MISMATCH", `Camera3D '${node.name}' has an inverted depth range: near ${self.near} is not less than far ${self.far}. The near plane is the closest thing this camera can see and the far plane the furthest.`);
|
|
2718
|
+
}
|
|
2622
2719
|
fov = 60;
|
|
2623
2720
|
near = .1;
|
|
2624
2721
|
far = 1e3;
|
|
2625
2722
|
current = false;
|
|
2723
|
+
lookAt = "";
|
|
2724
|
+
/**
|
|
2725
|
+
* Aim, every frame, whenever `lookAt` names something.
|
|
2726
|
+
*
|
|
2727
|
+
* In `update` and not `_syncObject3D`: the second is the renderer's copy pass
|
|
2728
|
+
* and a static node is pruned from it, which silently freezes anything driven
|
|
2729
|
+
* there. `rotation` is written as an ordinary prop, so the editor, the
|
|
2730
|
+
* serializer and `describeFraming` all see what the camera is actually doing.
|
|
2731
|
+
*/
|
|
2732
|
+
/** The `lookAt` path already reported as unlookable — cleared by a new one. */
|
|
2733
|
+
warnedLookAt = "";
|
|
2734
|
+
update(_dt) {
|
|
2735
|
+
if (this.lookAt === "") return;
|
|
2736
|
+
const target = this.getNodeOrNull(this.lookAt);
|
|
2737
|
+
if (!target) return;
|
|
2738
|
+
const to = worldPosition(target);
|
|
2739
|
+
if (to.length < 3) {
|
|
2740
|
+
if (this.warnedLookAt !== this.lookAt) {
|
|
2741
|
+
this.warnedLookAt = this.lookAt;
|
|
2742
|
+
diagnose(this.tree?.engine ?? null, "warn", `[incanto] ${this.getPath()}: lookAt '${this.lookAt}' resolves to a ${target.constructor.typeName ?? "Node"}, which has no world position — the camera keeps its authored rotation. Point it at a Node3D.`);
|
|
2743
|
+
}
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
const from = worldPosition(this);
|
|
2747
|
+
eyeScratch.set(from[0] ?? 0, from[1] ?? 0, from[2] ?? 0);
|
|
2748
|
+
targetScratch.set(to[0] ?? 0, to[1] ?? 0, to[2] ?? 0);
|
|
2749
|
+
if (eyeScratch.distanceToSquared(targetScratch) < 1e-12) return;
|
|
2750
|
+
lookScratch.lookAt(eyeScratch, targetScratch, UP);
|
|
2751
|
+
eulerScratch$1.setFromRotationMatrix(lookScratch, "XYZ");
|
|
2752
|
+
this.rotation = [
|
|
2753
|
+
eulerScratch$1.x * RAD2DEG$1,
|
|
2754
|
+
eulerScratch$1.y * RAD2DEG$1,
|
|
2755
|
+
eulerScratch$1.z * RAD2DEG$1
|
|
2756
|
+
];
|
|
2757
|
+
}
|
|
2626
2758
|
_createObject3D() {
|
|
2627
2759
|
return new PerspectiveCamera(this.fov, 1, this.near, this.far);
|
|
2628
2760
|
}
|
|
@@ -2639,6 +2771,17 @@ var Camera3D = class extends Node3D {
|
|
|
2639
2771
|
};
|
|
2640
2772
|
//#endregion
|
|
2641
2773
|
//#region src/3d/nodes/character-controller-3d.ts
|
|
2774
|
+
/**
|
|
2775
|
+
* The states this controller enters, and therefore the only keys its
|
|
2776
|
+
* `animations` map may carry.
|
|
2777
|
+
*/
|
|
2778
|
+
const MOVEMENT_STATES = [
|
|
2779
|
+
"idle",
|
|
2780
|
+
"walk",
|
|
2781
|
+
"run",
|
|
2782
|
+
"fastRun",
|
|
2783
|
+
"airborne"
|
|
2784
|
+
];
|
|
2642
2785
|
const ACC_DELTA_TIME = 8;
|
|
2643
2786
|
const DRAG_DAMPING = .15;
|
|
2644
2787
|
const SPRING_K = 1.2;
|
|
@@ -2725,7 +2868,13 @@ var CharacterController3D = class extends Node3D {
|
|
|
2725
2868
|
nodePath: true
|
|
2726
2869
|
},
|
|
2727
2870
|
skinYawOffset: { default: 0 },
|
|
2728
|
-
animations: {
|
|
2871
|
+
animations: {
|
|
2872
|
+
default: {},
|
|
2873
|
+
asset: {
|
|
2874
|
+
kind: "animation",
|
|
2875
|
+
mapValues: true
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2729
2878
|
};
|
|
2730
2879
|
view = "free";
|
|
2731
2880
|
camDistance = 4;
|
|
@@ -2825,6 +2974,27 @@ var CharacterController3D = class extends Node3D {
|
|
|
2825
2974
|
* "walk": "$walk", "airborne": "$jump" }
|
|
2826
2975
|
*/
|
|
2827
2976
|
animations = {};
|
|
2977
|
+
/**
|
|
2978
|
+
* Scene-load validation: an animation keyed by a state this controller never
|
|
2979
|
+
* enters is a clip that never plays.
|
|
2980
|
+
*
|
|
2981
|
+
* The five state names live in one place in one skill, and `jumping`,
|
|
2982
|
+
* `sprint`, `walking` and `falling` are what they are called in most other
|
|
2983
|
+
* engines — `jump` is even the ACTION name in every template here. A wrong
|
|
2984
|
+
* key was accepted and never looked at again, so the character walked and
|
|
2985
|
+
* idled correctly and never animated its jump, which reads as broken
|
|
2986
|
+
* animation rather than a wrong key.
|
|
2987
|
+
*/
|
|
2988
|
+
static validateJson(node) {
|
|
2989
|
+
const ctl = node;
|
|
2990
|
+
for (const state of Object.keys(ctl.animations)) {
|
|
2991
|
+
if (MOVEMENT_STATES.includes(state)) continue;
|
|
2992
|
+
throw new IncantoError("BAD_FORMAT", `CharacterController3D '${node.name}' maps an animation to '${state}', which is not a movement state.${didYouMean(state, MOVEMENT_STATES)} The states are [${MOVEMENT_STATES.join(", ")}].`, {
|
|
2993
|
+
prop: "animations",
|
|
2994
|
+
validOptions: [...MOVEMENT_STATES]
|
|
2995
|
+
});
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2828
2998
|
/** Camera yaw/pitch in radians (mouse look mutates these). */
|
|
2829
2999
|
yaw = 0;
|
|
2830
3000
|
pitch = 0;
|
|
@@ -3793,7 +3963,8 @@ var Flowers3D = class extends Node3D {
|
|
|
3793
3963
|
static props = {
|
|
3794
3964
|
density: {
|
|
3795
3965
|
default: "sparse",
|
|
3796
|
-
options: DENSITY_OPTIONS
|
|
3966
|
+
options: DENSITY_OPTIONS,
|
|
3967
|
+
kinds: ["string", "number"]
|
|
3797
3968
|
},
|
|
3798
3969
|
area: { default: [20, 20] },
|
|
3799
3970
|
seed: { default: 1 },
|
|
@@ -5189,7 +5360,10 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
5189
5360
|
options: TUFT_STYLES
|
|
5190
5361
|
},
|
|
5191
5362
|
area: { default: [20, 20] },
|
|
5192
|
-
density: {
|
|
5363
|
+
density: {
|
|
5364
|
+
default: 12,
|
|
5365
|
+
range: { min: 0 }
|
|
5366
|
+
},
|
|
5193
5367
|
maxInstances: { default: 5e4 },
|
|
5194
5368
|
height: { default: .25 },
|
|
5195
5369
|
colorA: { default: "#4d7232" },
|
|
@@ -5204,7 +5378,13 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
5204
5378
|
fadeEnd: { default: 90 },
|
|
5205
5379
|
sway: { default: .6 },
|
|
5206
5380
|
interaction: { default: true },
|
|
5207
|
-
coverage: {
|
|
5381
|
+
coverage: {
|
|
5382
|
+
default: .85,
|
|
5383
|
+
range: {
|
|
5384
|
+
min: 0,
|
|
5385
|
+
max: 1
|
|
5386
|
+
}
|
|
5387
|
+
},
|
|
5208
5388
|
flowers: { default: 0 },
|
|
5209
5389
|
seed: { default: 1 },
|
|
5210
5390
|
drape: { default: null },
|
|
@@ -6053,7 +6233,10 @@ var DirectionalLight3D = class extends Node3D {
|
|
|
6053
6233
|
static typeName = "DirectionalLight3D";
|
|
6054
6234
|
static props = {
|
|
6055
6235
|
color: { default: "#ffffff" },
|
|
6056
|
-
intensity: {
|
|
6236
|
+
intensity: {
|
|
6237
|
+
default: 1,
|
|
6238
|
+
range: { min: 0 }
|
|
6239
|
+
},
|
|
6057
6240
|
castShadow: { default: false },
|
|
6058
6241
|
shadowArea: { default: 75 },
|
|
6059
6242
|
shadowMapSize: { default: 2048 },
|
|
@@ -6104,7 +6287,10 @@ var OmniLight3D = class extends Node3D {
|
|
|
6104
6287
|
static typeName = "OmniLight3D";
|
|
6105
6288
|
static props = {
|
|
6106
6289
|
color: { default: "#ffffff" },
|
|
6107
|
-
intensity: {
|
|
6290
|
+
intensity: {
|
|
6291
|
+
default: 1,
|
|
6292
|
+
range: { min: 0 }
|
|
6293
|
+
},
|
|
6108
6294
|
range: { default: 0 }
|
|
6109
6295
|
};
|
|
6110
6296
|
color = "#ffffff";
|
|
@@ -6634,7 +6820,10 @@ var ModelInstance3D = class extends Node3D {
|
|
|
6634
6820
|
/** Seconds into the clip to start at — stagger a row of identical props. */
|
|
6635
6821
|
animationOffset: { default: 0 },
|
|
6636
6822
|
/** Upper-body layer clip ('' = off). One-shots auto-clear on finish. */
|
|
6637
|
-
animationUpper: {
|
|
6823
|
+
animationUpper: {
|
|
6824
|
+
default: "",
|
|
6825
|
+
asset: { kind: "animation" }
|
|
6826
|
+
},
|
|
6638
6827
|
/** Loop the upper layer (false = one-shot: attack, wave, throw). */
|
|
6639
6828
|
animationUpperLoop: { default: false },
|
|
6640
6829
|
/** Bone whose subtree the upper layer drives (forgiving lookup). */
|
|
@@ -6799,6 +6988,15 @@ var ModelInstance3D = class extends Node3D {
|
|
|
6799
6988
|
return [...embedded, ...assets];
|
|
6800
6989
|
}
|
|
6801
6990
|
/** Called by the 3D sync pass with the renderer's asset store. */
|
|
6991
|
+
/**
|
|
6992
|
+
* A model is ready when its GLB is. Freezing before that mounts nothing —
|
|
6993
|
+
* see `Node3D._staticReady`.
|
|
6994
|
+
*/
|
|
6995
|
+
_staticReady(assets) {
|
|
6996
|
+
if (!this.model) return true;
|
|
6997
|
+
if (!assets) return false;
|
|
6998
|
+
return assets.getModel(this.model).status === "ready";
|
|
6999
|
+
}
|
|
6802
7000
|
_syncModel(assets) {
|
|
6803
7001
|
this.store = assets;
|
|
6804
7002
|
const root = this._ensureObject3D();
|
|
@@ -7205,8 +7403,20 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
7205
7403
|
colorStart: { default: "#ffffff" },
|
|
7206
7404
|
colorEnd: { default: "#ffffff" },
|
|
7207
7405
|
paletteColors: { default: [] },
|
|
7208
|
-
alphaStart: {
|
|
7209
|
-
|
|
7406
|
+
alphaStart: {
|
|
7407
|
+
default: 1,
|
|
7408
|
+
range: {
|
|
7409
|
+
min: 0,
|
|
7410
|
+
max: 1
|
|
7411
|
+
}
|
|
7412
|
+
},
|
|
7413
|
+
alphaEnd: {
|
|
7414
|
+
default: 0,
|
|
7415
|
+
range: {
|
|
7416
|
+
min: 0,
|
|
7417
|
+
max: 1
|
|
7418
|
+
}
|
|
7419
|
+
},
|
|
7210
7420
|
shimmer: { default: 0 },
|
|
7211
7421
|
blend: {
|
|
7212
7422
|
default: "add",
|
|
@@ -7276,7 +7486,7 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
7276
7486
|
_ensureSim() {
|
|
7277
7487
|
if (!this.sim) {
|
|
7278
7488
|
applyParticlePreset(this, this.preset, getNodeSchema(Particles3D.typeName));
|
|
7279
|
-
const rng = this
|
|
7489
|
+
const rng = effectRng(this);
|
|
7280
7490
|
this.sim = new ParticleSim({
|
|
7281
7491
|
rate: this.emitting ? this.rate : 0,
|
|
7282
7492
|
lifetime: [this.lifetime[0] ?? 1, this.lifetime[1] ?? 1],
|
|
@@ -7368,6 +7578,7 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
7368
7578
|
kind,
|
|
7369
7579
|
name: this.preset,
|
|
7370
7580
|
from: this.getPath(),
|
|
7581
|
+
wiredAs: this.wiringPath(),
|
|
7371
7582
|
amount,
|
|
7372
7583
|
at: engine.time
|
|
7373
7584
|
});
|
|
@@ -7435,7 +7646,7 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
7435
7646
|
this.points.frustumCulled = false;
|
|
7436
7647
|
this._ensureObject3D().add(this.points);
|
|
7437
7648
|
}
|
|
7438
|
-
this.points.renderOrder = this.
|
|
7649
|
+
this.points.renderOrder = this.effectiveRenderOrder;
|
|
7439
7650
|
const material = this.points.material;
|
|
7440
7651
|
material.depthTest = this.depthTest;
|
|
7441
7652
|
material.blending = this.blend === "add" ? AdditiveBlending : NormalBlending;
|
|
@@ -8867,7 +9078,13 @@ var River3D = class River3D extends Node3D {
|
|
|
8867
9078
|
flowSpeed: { default: 1.6 },
|
|
8868
9079
|
colors: { default: {} },
|
|
8869
9080
|
absorption: { default: .5 },
|
|
8870
|
-
opacity: {
|
|
9081
|
+
opacity: {
|
|
9082
|
+
default: .85,
|
|
9083
|
+
range: {
|
|
9084
|
+
min: 0,
|
|
9085
|
+
max: 1
|
|
9086
|
+
}
|
|
9087
|
+
},
|
|
8871
9088
|
foam: { default: 1 },
|
|
8872
9089
|
ripples: { default: 1 },
|
|
8873
9090
|
sunDirection: { default: [
|
|
@@ -8876,7 +9093,10 @@ var River3D = class River3D extends Node3D {
|
|
|
8876
9093
|
.3
|
|
8877
9094
|
] },
|
|
8878
9095
|
sunColor: { default: "#fff6e0" },
|
|
8879
|
-
sunIntensity: {
|
|
9096
|
+
sunIntensity: {
|
|
9097
|
+
default: 1,
|
|
9098
|
+
range: { min: 0 }
|
|
9099
|
+
},
|
|
8880
9100
|
terrain: {
|
|
8881
9101
|
default: "",
|
|
8882
9102
|
nodePath: true
|
|
@@ -9497,7 +9717,13 @@ var Trail3D = class extends Node3D {
|
|
|
9497
9717
|
static props = {
|
|
9498
9718
|
width: { default: .3 },
|
|
9499
9719
|
color: { default: "#ffffff" },
|
|
9500
|
-
opacity: {
|
|
9720
|
+
opacity: {
|
|
9721
|
+
default: .8,
|
|
9722
|
+
range: {
|
|
9723
|
+
min: 0,
|
|
9724
|
+
max: 1
|
|
9725
|
+
}
|
|
9726
|
+
},
|
|
9501
9727
|
seconds: { default: .6 },
|
|
9502
9728
|
emitting: { default: true },
|
|
9503
9729
|
additive: { default: false },
|
|
@@ -9546,6 +9772,7 @@ var Trail3D = class extends Node3D {
|
|
|
9546
9772
|
kind: "trail",
|
|
9547
9773
|
name: this.color,
|
|
9548
9774
|
from: this.getPath(),
|
|
9775
|
+
wiredAs: this.wiringPath(),
|
|
9549
9776
|
amount: this.seconds,
|
|
9550
9777
|
at: engine.time
|
|
9551
9778
|
});
|
|
@@ -11327,7 +11554,13 @@ var Tree3D = class Tree3D extends Node3D {
|
|
|
11327
11554
|
area: { default: [10, 10] },
|
|
11328
11555
|
trunkColor: { default: "#7a5a3a" },
|
|
11329
11556
|
canopyColor: { default: "#4a7c3f" },
|
|
11330
|
-
leafTexture: {
|
|
11557
|
+
leafTexture: {
|
|
11558
|
+
default: "",
|
|
11559
|
+
asset: {
|
|
11560
|
+
kind: "texture",
|
|
11561
|
+
urlOnly: true
|
|
11562
|
+
}
|
|
11563
|
+
},
|
|
11331
11564
|
leafFadeStart: { default: 0 },
|
|
11332
11565
|
leafFadeEnd: { default: 0 },
|
|
11333
11566
|
leafShadows: { default: true },
|
|
@@ -12661,9 +12894,25 @@ var VoxelGrid3D = class extends Node3D {
|
|
|
12661
12894
|
static typeName = "VoxelGrid3D";
|
|
12662
12895
|
static signals = ["blocksChanged"];
|
|
12663
12896
|
static props = { voxels: { default: [] } };
|
|
12897
|
+
/**
|
|
12898
|
+
* Hard-fail an authored grid that cannot all be DRAWN.
|
|
12899
|
+
*
|
|
12900
|
+
* The draw loop stops at `MAX_INSTANCES` and the excess blocks stay in the
|
|
12901
|
+
* map — so `tileAt()` still reports them and a chunk collider builder still
|
|
12902
|
+
* makes them solid. The world past the cap was invisible AND you walked into
|
|
12903
|
+
* it. Every other 3D node whose instance count comes from authored data
|
|
12904
|
+
* hard-fails at load; this one silently truncated.
|
|
12905
|
+
*/
|
|
12906
|
+
static validateJson(node) {
|
|
12907
|
+
const self = node;
|
|
12908
|
+
for (const [i, row] of self.voxels.entries()) if (!Array.isArray(row) || row.length < 3 || row.some((v) => typeof v !== "number")) throw new IncantoError("BAD_FORMAT", `VoxelGrid3D '${node.name}': voxels[${i}] must be [x, y, z, tile?] (numbers), got ${JSON.stringify(row)}.`);
|
|
12909
|
+
if (self.voxels.length > MAX_INSTANCES) throw new IncantoError("BAD_FORMAT", `VoxelGrid3D '${node.name}': ${self.voxels.length} voxels exceeds the ${MAX_INSTANCES} this node can draw. Everything past the cap would still be SOLID and still be reported by tileAt(), and simply not appear. Split the world into several VoxelGrid3D nodes (one per chunk) and show the ones near the player.`);
|
|
12910
|
+
}
|
|
12664
12911
|
/** JSON-authored blocks as [x, y, z, tile] tuples (generated terrains bake these). */
|
|
12665
12912
|
voxels = [];
|
|
12666
12913
|
appliedVoxels = null;
|
|
12914
|
+
/** Said once per node: a runtime overflow must not spam a frame loop. */
|
|
12915
|
+
reportedOverflow = false;
|
|
12667
12916
|
map = /* @__PURE__ */ new Map();
|
|
12668
12917
|
mesh = null;
|
|
12669
12918
|
dirty = false;
|
|
@@ -12729,6 +12978,10 @@ var VoxelGrid3D = class extends Node3D {
|
|
|
12729
12978
|
tile: t[3] ?? 0
|
|
12730
12979
|
})));
|
|
12731
12980
|
}
|
|
12981
|
+
if (this.map.size > MAX_INSTANCES && !this.reportedOverflow) {
|
|
12982
|
+
this.reportedOverflow = true;
|
|
12983
|
+
diagnose(this.tree?.engine ?? null, "error", `[incanto] VoxelGrid3D '${this.getPath()}' holds ${this.map.size} blocks and can draw ${MAX_INSTANCES}. The rest are still solid and still reported by tileAt(), and do not appear. Split the world across several VoxelGrid3D nodes.`);
|
|
12984
|
+
}
|
|
12732
12985
|
if (!this.dirty || !this.mesh) return;
|
|
12733
12986
|
this.dirty = false;
|
|
12734
12987
|
const m4 = new Matrix4();
|
|
@@ -177,7 +177,7 @@ const pct = (v) => `${(v * 100).toFixed(1)}%`;
|
|
|
177
177
|
/** The report a person or an agent reads. Verdict first, numbers after. */
|
|
178
178
|
function frameText(stats) {
|
|
179
179
|
const lines = [];
|
|
180
|
-
if (stats.hidden) lines.push("
|
|
180
|
+
if (stats.hidden) lines.push("the page is hidden, so the game is PAUSED — these pixels were rendered for this capture (current), but nothing is moving. A `--do` drive needs the window in front.");
|
|
181
181
|
if (stats.black) lines.push("BLACK SCREEN — nothing was drawn (no light, no camera, or nothing in view)");
|
|
182
182
|
else if (stats.uniform) lines.push("one flat colour — the camera may be inside geometry, or only the sky is drawn");
|
|
183
183
|
else if (!stats.subject) lines.push("nothing but background — the camera is pointed away from everything in the scene");
|